microsoft / TypeScript

TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
https://www.typescriptlang.org
Apache License 2.0
100.95k stars 12.48k forks source link

Recursively Resolving Modules #10649

Open bezreyhan opened 8 years ago

bezreyhan commented 8 years ago

TypeScript Version: 2.1.0

I'm using ts-loader and Webpack to compile my .ts files. In My Webpack config I have the following configuration:

resolve: {
  modulesDirectories: ['shared', 'node_modules']
}

This allows me to import a component that lives in the shared directory without spelling out the full relative path. Example: import Button from 'components/Button'

Webpack will walk up the directory tree and use the first shared directory it finds (the same way it does with the node_modules directories). This way, the component can live in the ../../shared/components/Buttton.tsx and Webpack will find it and bundle it.

My files compile fine because I am using ts-loader but I am getting can not find module errors in my editor.

Is there a way to tell the TS compiler to recursively look for the component in the shared directories?

I noticed the new baseUrl and paths configuration option but to my knowledge you can not use paths to recursively look for a directory.

mhegazy commented 8 years ago

I noticed the new baseUrl and paths configuration option but to my knowledge you can not use paths to recursively look for a directory.

well not fully. but it can emulate the behavior, e.g.

"paths" : {
    "*": [
             "./node_modules/*",
             "../node_modules/*", 
             "../../node_modules/*", 
             "../../../node_modules/*" 

              // you get the idea..
          ] 
}
bezreyhan commented 8 years ago

@mhegazy

I just added this to my tsconfig:

"paths": {
    "*": [
      "./shared/*",
      "../shared/*",
      "../../shared/*",
      "../../../shared/*",
      "../../../../shared/*",
      "../../../../../shared/*",
      "../../../../../../shared/*"
    ]
  },

but I'm still getting the errors.

I ran tsc with traceResolution and grep'd for shared but nothing came up. It doesn't seem like tsc is looking in the shared folders for the modules.

mhegazy commented 8 years ago

Can u share a repro.

bezreyhan commented 8 years ago

@mhegazy Here is a repro: https://github.com/bezreyhan/ts-paths

After you npm install, you can run node_modules/.bin/tsc and see that the helper function in the shared folder can not be found.

Also the output of node_modules/.bin/tsc --traceResolution | grep shared is empty.

mhegazy commented 8 years ago

A few of issues here.

here is a working tsonfig for you:

{
  "compilerOptions": {
    "sourceMap": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "module": "commonjs",
    "target": "es6",
    "jsx": "react",
     "baseUrl": "./",
     "paths": {
        "*": [
          "src/shared/*"
        ]
    }
  },
  "include": [
    "./src/first/second/third/page.ts"
  ]
}
bezreyhan commented 8 years ago

@mhegazy Not putting paths within compilerOptions was a silly oversight by me. Thanks for pointing me the right way.

However, I still need recursive lookups (In the repro I wanted to keep it simple so I only put one shared folder). If my baseUrl is set to './' the resolution will begin at the root of the project. I wanted the resolution to begin at the file that is importing a module and then recursively move up the directory tree.

Can you give me any pointers here?

mhegazy commented 8 years ago

i miss understood then. I do not think there is an easy way to do this.

bezreyhan commented 8 years ago

Hmm, would this be something the TS team would consider implementing? Being able to designate another directory name other than node_modules would be a useful.

For example, many people in the React community use Webpack's modulesDirectories config option to implement this sort of folder structure: https://gist.github.com/ryanflorence/daafb1e3cb8ad740b346

mhegazy commented 8 years ago

would it be possible to have the same compoenent "overridden" in multiple "shared" folder, e.g.:

app
├── screens
│   ├── Admin
│   │   ├── shared
│   │   │       ├── Module.js
│   │   └── index.js   // imports ./shared/Module
├── shared
│   └── Module.js
└── index.js // imports ./shared/Module and not ./screens/Admin/Shared/Module
bezreyhan commented 8 years ago

Sorry for the delayed response.

Yes it would. It function the same way that Node looks for the node_modules.

Here is the Webpack documentation: https://webpack.github.io/docs/configuration.html#resolve-modulesdirectories

mhegazy commented 8 years ago

We do something similar to this in classic module resolution that walks up the folder structure to find a match. we will need a proposal on how to update the resolution to enable this.

wclr commented 8 years ago

It would be very useful if TS supported more advanced ala webpack resolution strategies. https://webpack.github.io/docs/configuration.html#resolve-alias

but no need be as much sophisticated it believe

Ciantic commented 8 years ago

Edit2 I can't get the webpack to work with my below trick, it works for some files.

Btw, for now it's easiest to just drop a symbolic link "Components" to the node_modules. It works even on Windows. Then one can simply do import { Blaa } from "Components/Blaa";And create a simple NPM post install script that uses node's fs.symlink and it should make it pretty good.

kamranayub commented 6 years ago

I know this was made in 2016 but I was going to use this approach on a new project and ran into this issue. I don't know much about TSC internals but I might spend some time to see what this might entail.

I would be apt to propose a similar compiler option as rootDirs but just moduleDirs that the Node resolution strategy would use when it walks up the directory tree. Similar to other tools, by default the value of moduleDirs is just ["node_modules"] but when overridden you can provide other directories, e.g. ["node_modules", "shared"]. I think the same resolution strategy logic should work, it just will change from only considering node_modules to considering all entries in the moduleDirs option.

jquense commented 5 years ago

this is a huge blocker to adopting typescript on larger projects. I've been trying to come up with a flow to typescript migration for a lot of our projects and without this option it's just infeasible.

We need an option that allows customizing which modules are node_modules