{"id":13433187,"url":"https://github.com/jedwards1211/dude-wheres-my-module","last_synced_at":"2025-05-05T23:53:42.131Z","repository":{"id":35075607,"uuid":"156761025","full_name":"jedwards1211/dude-wheres-my-module","owner":"jedwards1211","description":"Because VSCode's automatic imports aren't good enough","archived":false,"fork":false,"pushed_at":"2023-01-07T04:09:28.000Z","size":2136,"stargazers_count":16,"open_issues_count":14,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-14T12:45:52.531Z","etag":null,"topics":["automatic-imports","flowtype","ide-tools","suggested-imports"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jedwards1211.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-11-08T19:53:37.000Z","updated_at":"2024-07-06T23:52:00.000Z","dependencies_parsed_at":"2023-01-15T13:30:17.707Z","dependency_job_id":null,"html_url":"https://github.com/jedwards1211/dude-wheres-my-module","commit_stats":null,"previous_names":[],"tags_count":27,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jedwards1211%2Fdude-wheres-my-module","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jedwards1211%2Fdude-wheres-my-module/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jedwards1211%2Fdude-wheres-my-module/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jedwards1211%2Fdude-wheres-my-module/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jedwards1211","download_url":"https://codeload.github.com/jedwards1211/dude-wheres-my-module/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252596396,"owners_count":21773844,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["automatic-imports","flowtype","ide-tools","suggested-imports"],"created_at":"2024-07-31T02:01:22.207Z","updated_at":"2025-05-05T23:53:42.111Z","avatar_url":"https://github.com/jedwards1211.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"# dude-wheres-my-module\n\n[![CircleCI](https://circleci.com/gh/jedwards1211/dude-wheres-my-module.svg?style=svg)](https://circleci.com/gh/jedwards1211/dude-wheres-my-module)\n[![Coverage Status](https://codecov.io/gh/jedwards1211/dude-wheres-my-module/branch/master/graph/badge.svg)](https://codecov.io/gh/jedwards1211/dude-wheres-my-module)\n[![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)\n[![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/)\n[![npm version](https://badge.fury.io/js/dude-wheres-my-module.svg)](https://badge.fury.io/js/dude-wheres-my-module)\n\n### JavaScript/Flow suggested import server\n\nAs far as I can tell this is currently the only tool that can write not just\nordinary JS import and require statements but also Flow type import statements\nfor you too.\n\nI've created [Atom](https://atom.io/packages/import-it) and [VSCode](https://marketplace.visualstudio.com/items?itemName=vscode-dude-wheres-my-module.vscode-dude-wheres-my-module) extensions for using this,\nyou probably won't want to use this package directly unless you're creating an extension for another IDE.\n\n# Table of Contents\n\n\u003c!-- toc --\u003e\n\n- [Features](#features)\n- [Limitations/Known Issues](#limitationsknown-issues)\n- [Installation](#installation)\n- [CLI Commands](#cli-commands)\n  - [`dude wheres [--file ]`](#dude-wheres----file-)\n  - [`dude suggest`](#dude-suggest-)\n  - [`dude log [-f [tail options]]`](#dude-log--f-tail-options)\n  - [`dude errors`](#dude-errors)\n  - [`dude stop`](#dude-stop)\n  - [`dude stahp`/`dude kill`](#dude-stahpdude-kill)\n- [Node.js API](#nodejs-api)\n  - [`new Client(projectRoot: string)`](#new-clientprojectroot-string)\n  - [`Client.suggest(options)`](#clientsuggestoptions)\n  - [`Client.wheres(options)`](#clientwheresoptions)\n  - [`Client.on('starting', () =\u003e any)`](#clientonstarting---any)\n  - [`Client.on('progress', ({ completed: number, total: number }) =\u003e any)`](#clientonprogress--completed-number-total-number---any)\n  - [`Client.on('ready', () =\u003e any)`](#clientonready---any)\n- [Configuration](#configuration)\n  - [Config file API](#config-file-api)\n  - [Config file example](#config-file-example)\n- [Merging suggested imports into the code](#merging-suggested-imports-into-the-code)\n\n\u003c!-- tocstop --\u003e\n\n# Why other tools are lame\n\n- AFAICT VSCode can't even suggest default or namespace imports.\n- If you have `import {foo as bar} from 'foo'` in one file, VSCode/WebStorm won't suggest this for `bar` in another file.\n- And of course, VSCode has no support for Flow `import type` and `import {type ...}`\n\n# Where `dude-where-my-module` currently lags behind\n\nThe main thing is it doesn't currently scan exports in your `dependencies`/`flow-typed`/`@types` etc.\nBut once you have `import chalk from 'chalk'` in your code, it can suggest that for `chalk` in other files.\n\n# Features\n\n- Server that watches/indexes your import and require statements in the\n  background\n- Node.js Client to request import suggestions from the server\n- CLI for test driving/stopping the server\n- Relies on the Babel config in your project to parse your code\n- Can suggest imports for:\n  - native packages\n  - identifiers exported from your code\n  - identifiers imported by your code\n  - custom preferred imports you define in dotfiles\n- Supports `require` statements\n- Supports `import` statements\n- Supports Flow `import type` and `import {type ...}` statements\n\n# Other Limitations/Known Issues\n\nThis project is getting pretty solid, but there are still a few issues.\n\n- `dude-wheres-my-module` doesn't automatically try to figure out what imports are available from packages in your `node_modules` yet. But the good news is that if you've imported something once in one file, it will be available in suggestions for other files. You can also manually configure preferred imports from packages in `node_modules`\n- There are a few cases where obsolete suggested imports\n  stick around after you delete them from the file `dude-wheres-my-module` got them from, or delete that file entirely.\n- It can't currently use Flow type information to rule out invalid suggestions (or decide that the way you're using a built-in idea identifier seems to indicate you meant to import something)\n\n# Installation\n\n```sh\nnpm install --global dude-wheres-my-module\n```\n\n# CLI Commands\n\nThe CLI isn't intended to be the primary way you get import suggestions, it's just for test-driving the server, debugging errors, or telling it to shut down.\n\nAll commands will automatically start a server for the\ncurrent project directory if one isn't already running\n(except for commands that stop the server, of course).\n\nOn a large project the server might take awhile to boot\nup and parse all of your code, but it will show you its\nprogress as it starts up!\n\n## `dude wheres \u003cidentifier\u003e [--file \u003cfilename\u003e]`\n\nSuggests `import` or `require` statements for a given identifier. You can optionally specify a filename to\nimport relative to.\n\n### Example\n\nAssuming both of these imports are found in your project\ncode or in custom preferred imports:\n\n```\n$ dude wheres pick\nimport { pick } from \"lodash/fp\"\nimport { pick } from \"lodash\"\n```\n\n## `dude suggest \u003cfilename\u003e`\n\nSuggests imports for all undeclared identifiers in a file.\n\n### Example\n\n```\n$ dude suggest ListWithOneStatusItem.js\nReact (7:12)   children: React.Node,\n  import * as React from \"react\"\nListItem (8:46)   ListItemProps?: ?React.ElementConfig\u003ctypeof ListItem\u003e,\n  import ListItem from \"@material-ui/core/ListItem\"\nListItemText (9:50)   ListItemTextProps?: ?React.ElementConfig\u003ctypeof ListItemText\u003e,\n  import ListItemText from \"@material-ui/core/ListItemText\"\nList (18:3)   \u003cList {...props}\u003e\n  import List from \"@material-ui/core/List\"\n  import List from \"@material-ui/icons/List\"\n```\n\n##### `ListWithOneStatusItem.js`\n\n```js\n/**\n * @flow\n * @prettier\n */\n\nexport type Props = {\n  children: React.Node,\n  ListItemProps?: ?React.ElementConfig\u003ctypeof ListItem\u003e,\n  ListItemTextProps?: ?React.ElementConfig\u003ctypeof ListItemText\u003e,\n}\n\nconst ListWithOneStatusItem = ({\n  children,\n  ListItemProps,\n  ListItemTextProps,\n  ...props\n}: Props): React.Node =\u003e (\n  \u003cList {...props}\u003e\n    \u003cListItem {...ListItemProps}\u003e\n      \u003cListItemText {...ListItemTextProps}\u003e{children}\u003c/ListItemText\u003e\n    \u003c/ListItem\u003e\n  \u003c/List\u003e\n)\n\nexport default ListWithOneStatusItem\n```\n\n## `dude log [-f [tail options]]`\n\nPrint the server log file. With `-f`, it will `tail` the file.\n\n## `dude errors`\n\nPrint out any error messages found in the server log file.\n\n## `dude stop`\n\nStops the server gracefully.\n\n## `dude stahp`/`dude kill`\n\nStops the server forcefully.\n\n# Node.js API\n\n```js\nimport Client from 'dude-wheres-my-module/Client'\n```\n\n## `new Client(projectRoot: string)`\n\nCreates a client for the project in the `projectRoot`\ndirectory (you can pass a subdirectory or file, and it\nwill automatically find the actual project root directory)\n\n## `Client.suggest(options)`\n\nSuggests `import` or `require` statements for all undeclared identifiers in a file.\n\n### `options`\n\n##### `file` (`string`, _required_)\n\nThe file to suggest import paths relative to.\n\n##### `code` (`string`, _optional_)\n\nThe code to suggest imports for. If not given, the contents of `file` will be\nused.\n\n### Returns (`Promise\u003cSuggestedImportsResult\u003e`)\n\nEach key in `SuggestedImportsResult` is an identifier,\nand the corresponding value is an object with the following properties:\n\n- `identifier: string` - the identifier\n- `start: {line: number, column: number}` - the location of the start of the identifier in the file\n- `end: {line: number, column: number}` the location of the start of the identifier in the file\n- `context: string` - the line of the file on which the identifier appears\n- `kind?: 'value' | 'type'` - whether the identifier appears in a value or type position\n- `suggested` - an array of suggested imports, each having the following properties:\n  - `code: string` - the `import` or `require` statement code\n  - `ast` - the AST of the `import` or `require` statement\n\n## `Client.wheres(options)`\n\nSuggests `import` or `require` statements for a given identifier. You can optionally specify a filename to\nimport relative to.\n\n### `options`\n\n##### `identifier` (`string`, _optional_)\n\nThe identifier to suggest imports for.\n\n##### `file` (`string`, _optional_)\n\nThe file to suggest import paths relative to.\n\n### Returns (`Promise\u003cArray\u003cSuggestedImportResult\u003e\u003e`)\n\nEach `SuggestedImportResult` has the following properties:\n\n- `code: string` - the `import` statement code\n\n## `Client.on('starting', () =\u003e any)`\n\nThis event is emitted when the client has started a new server process.\n\n## `Client.on('progress', ({ completed: number, total: number }) =\u003e any)`\n\nThis event is emitted when the server parses a file, and includes the `total`\nnumber of files it has discovered to parse, and the number of files it has\n`completed` parsing.\n\n## `Client.on('ready', () =\u003e any)`\n\nThis event is emitted when the server has finished starting up.\n\n# Configuration\n\n`dude-where-my-module` looks for `.dude-wheres-my-module.js` files in your\nproject directory and subdirectories. If found, it will load configuration\nfrom them.\n\nThe server will hot-reload a file's configuration whenever you save changes to it.\n\n## Config file API\n\nThe config file's `module.exports` must be a function that\nreturns a config object (or a `Promise` that resolves to a config object).\nThe following properties on the config object are supported:\n\n### `preferredImports: Array\u003cstring\u003e`\n\nAn array of code containing `import` statements you would like to come first\nin suggested import lists.\n\n## Config file example\n\nThis is the config file I use in one of my main projects. It adds submodules\nfrom `lodash`, `@material-ui/core`, `@material-ui/icons`, and many more packages\nto the preferred imports.\n\n```js\n/**\n * @prettier\n */\n\nmodule.exports = async function configure() {\n  const path = require('path')\n  const { promisify } = require('es6-promisify')\n  const glob = promisify(require('glob'))\n\n  const nodeModulesDir = path.join(__dirname, 'node_modules')\n\n  function assumeDefaultImports(files, options = {}) {\n    const transformIdentifier = options.transformIdentifier || ((id) =\u003e id)\n    const result = []\n    files.forEach((file) =\u003e {\n      if (/index\\.js$/.test(file)) file = path.dirname(file)\n      file = file.replace(/\\.js$/, '')\n      const identifier = path.basename(file)\n      if (identifier[0] === '_' || /[^a-zA-Z0-9_]/.test(identifier)) return\n      result.push(\n        `import ${transformIdentifier(identifier, {\n          file,\n        })} from '${path.relative(nodeModulesDir, file)}'`\n      )\n    })\n    return result\n  }\n\n  function assumeNamedImports(files) {\n    const result = []\n    files.forEach((file) =\u003e {\n      if (/index\\.js$/.test(file)) file = path.dirname(file)\n      file = file.replace(/\\.js$/, '')\n      const identifier = path.basename(file)\n      if (/^_|[^a-zA-Z0-9_]|^function$/.test(identifier)) return\n      result.push(\n        `import { ${identifier} } from '${path\n          .relative(nodeModulesDir, path.dirname(file))\n          .replace(/^\\.\\//, '')}'`\n      )\n    })\n    return result\n  }\n\n  async function globNodeModules(pattern) {\n    const files = await glob(path.join(nodeModulesDir, pattern))\n    return assumeDefaultImports(files)\n  }\n\n  const preferredImports = [\n    `\nimport _ from 'lodash'\nimport gql from 'graphql-tag'\nimport Sequelize, {Model, Association, type Transaction, type QueryGenerator, type FindOptions, type WhereOptions} from 'sequelize'\nimport {Query, Mutation, type QueryRenderProps, type MutationFunction} from 'react-apollo'\nimport Route from 'react-router-parsed/Route'\nimport {Link, NavLink, type Match, type RouterHistory, type Location} from 'react-router-dom'\nimport {createSelector, createStructuredSelector} from 'reselect'\nimport {connect, bindActionCreators} from 'react-redux'\nimport {compose} from 'redux'\nimport * as graphql from 'graphql'\nimport * as React from 'react'\nimport {reify} from 'flow-runtime'\nimport type {Type} from 'flow-runtime'\nimport classNames from 'classnames'\nimport requireEnv from '@jcoreio/require-env'\nimport path from 'path'\nimport fs from 'fs-extra'\nimport promisify from 'es6-promisify'\nimport BreakpointMedia from 'react-media-material-ui/BreakpointMedia'\nimport {type FormProps, type FieldProps, type FieldArrayProps} from 'redux-form'\nimport {describe, it, before, after, beforeEach, afterEach} from 'mocha'\nimport {expect} from 'chai'\n    `,\n  ]\n\n  preferredImports.push(\n    ...assumeNamedImports(\n      await glob(path.join(nodeModulesDir, 'lodash/fp/*.js'))\n    )\n  )\n\n  for (let pattern of [\n    'redux-form/es/*.js',\n    'redux-form-material-ui/es/*.js',\n  ]) {\n    preferredImports.push(...(await globNodeModules(pattern)))\n  }\n  preferredImports.push(\n    ...assumeDefaultImports(\n      await glob(path.join(nodeModulesDir, '@material-ui/core/**/index.js'), {\n        ignore: [path.join(nodeModulesDir, '@material-ui/core/es/**')],\n      })\n    )\n  )\n  preferredImports.push(\n    ...assumeDefaultImports(\n      await glob(path.join(nodeModulesDir, '@material-ui/icons/*.js')),\n      { transformIdentifier: (identifier) =\u003e `${identifier}Icon` }\n    )\n  )\n  preferredImports.push(\n    ...assumeDefaultImports(\n      await glob(path.join(nodeModulesDir, '@material-ui/icons/*.js'))\n    )\n  )\n\n  return {\n    preferredImports,\n  }\n}\n```\n\n# Merging suggested imports into the code\n\nIf you're interested in writing an IDE plugin that uses `dude-wheres-my-module`,\nI'd recommend using [`jscodeshift-add-imports`](https://github.com/jedwards1211/jscodeshift-add-imports#readme)\nto merge the suggested imports into the the code.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjedwards1211%2Fdude-wheres-my-module","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjedwards1211%2Fdude-wheres-my-module","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjedwards1211%2Fdude-wheres-my-module/lists"}