Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for paths in tsconfig.json and jsconfig.json #11293

Merged
merged 9 commits into from Mar 23, 2020

Conversation

timneutkens
Copy link
Member

@timneutkens timneutkens commented Mar 23, 2020

Adds support for "paths" in both tsconfig.json or jsconfig.json.

Examples:

Single module alias

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "mytheme": ["components/mytheme.js"]
    }
  }
}

Wildcard alias

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@c/*": ["components/*"],
    }
  }
}

Fallback behavior

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@mytheme/*": ["themes/theme-a/*", "lib/theme-b/*"]
    }
  }
}

Fixes #7779

@ijjk
Copy link
Member

ijjk commented Mar 23, 2020

Stats from current PR

Default Server Mode
General Overall increase ⚠️
zeit/next.js canary timneutkens/next.js add/jsconfig-paths Change
buildDuration 10.7s 10.5s -186ms
nodeModulesSize 52.8 MB 52.8 MB ⚠️ +14.6 kB
Client Bundles (main, webpack, commons)
zeit/next.js canary timneutkens/next.js add/jsconfig-paths Change
main-HASH.js gzip 6.24 kB 6.24 kB
webpack-HASH.js gzip 746 B 746 B
de003c3a9d30..c6c1.js gzip 10.1 kB 10.1 kB
framework.HASH.js gzip 39.1 kB 39.1 kB
Overall change 56.2 kB 56.2 kB
Client Bundles (main, webpack, commons) Modern
zeit/next.js canary timneutkens/next.js add/jsconfig-paths Change
main-HASH.module.js gzip 4.78 kB 4.78 kB
webpack-HASH..dule.js gzip 746 B 746 B
de003c3a9d30..dule.js gzip 6.71 kB 6.71 kB
framework.HA..dule.js gzip 39.1 kB 39.1 kB
Overall change 51.4 kB 51.4 kB
Legacy Client Bundles (polyfills)
zeit/next.js canary timneutkens/next.js add/jsconfig-paths Change
polyfills-HASH.js gzip 26.3 kB 26.3 kB
Overall change 26.3 kB 26.3 kB
Client Pages
zeit/next.js canary timneutkens/next.js add/jsconfig-paths Change
_app.js gzip 1.24 kB 1.24 kB
_error.js gzip 3.15 kB 3.15 kB
hooks.js gzip 664 B 664 B
index.js gzip 222 B 222 B
link.js gzip 2.03 kB 2.03 kB
routerDirect.js gzip 279 B 279 B
withRouter.js gzip 278 B 278 B
Overall change 7.86 kB 7.86 kB
Client Pages Modern
zeit/next.js canary timneutkens/next.js add/jsconfig-paths Change
_app.module.js gzip 594 B 594 B
_error.module.js gzip 2.08 kB 2.08 kB
hooks.module.js gzip 370 B 370 B
index.module.js gzip 212 B 212 B
link.module.js gzip 1.48 kB 1.48 kB
routerDirect..dule.js gzip 271 B 271 B
withRouter.m..dule.js gzip 270 B 270 B
Overall change 5.28 kB 5.28 kB
Client Build Manifests
zeit/next.js canary timneutkens/next.js add/jsconfig-paths Change
_buildManifest.js gzip 61 B 61 B
_buildManife..dule.js gzip 61 B 61 B
Overall change 122 B 122 B
Rendered Page Sizes
zeit/next.js canary timneutkens/next.js add/jsconfig-paths Change
index.html gzip 916 B 916 B
link.html gzip 924 B 924 B
withRouter.html gzip 913 B 913 B
Overall change 2.75 kB 2.75 kB

Serverless Mode (Decrease detected ✓)
General Overall increase ⚠️
zeit/next.js canary timneutkens/next.js add/jsconfig-paths Change
buildDuration 11.6s 11.3s -324ms
nodeModulesSize 52.8 MB 52.8 MB ⚠️ +14.6 kB
Client Bundles (main, webpack, commons)
zeit/next.js canary timneutkens/next.js add/jsconfig-paths Change
main-HASH.js gzip 6.24 kB 6.24 kB
webpack-HASH.js gzip 746 B 746 B
de003c3a9d30..c6c1.js gzip 10.1 kB 10.1 kB
framework.HASH.js gzip 39.1 kB 39.1 kB
Overall change 56.2 kB 56.2 kB
Client Bundles (main, webpack, commons) Modern
zeit/next.js canary timneutkens/next.js add/jsconfig-paths Change
main-HASH.module.js gzip 4.78 kB 4.78 kB
webpack-HASH..dule.js gzip 746 B 746 B
de003c3a9d30..dule.js gzip 6.71 kB 6.71 kB
framework.HA..dule.js gzip 39.1 kB 39.1 kB
Overall change 51.4 kB 51.4 kB
Legacy Client Bundles (polyfills)
zeit/next.js canary timneutkens/next.js add/jsconfig-paths Change
polyfills-HASH.js gzip 26.3 kB 26.3 kB
Overall change 26.3 kB 26.3 kB
Client Pages
zeit/next.js canary timneutkens/next.js add/jsconfig-paths Change
_app.js gzip 1.24 kB 1.24 kB
_error.js gzip 3.15 kB 3.15 kB
hooks.js gzip 664 B 664 B
index.js gzip 222 B 222 B
link.js gzip 2.03 kB 2.03 kB
routerDirect.js gzip 279 B 279 B
withRouter.js gzip 278 B 278 B
Overall change 7.86 kB 7.86 kB
Client Pages Modern
zeit/next.js canary timneutkens/next.js add/jsconfig-paths Change
_app.module.js gzip 594 B 594 B
_error.module.js gzip 2.08 kB 2.08 kB
hooks.module.js gzip 370 B 370 B
index.module.js gzip 212 B 212 B
link.module.js gzip 1.48 kB 1.48 kB
routerDirect..dule.js gzip 271 B 271 B
withRouter.m..dule.js gzip 270 B 270 B
Overall change 5.28 kB 5.28 kB
Client Build Manifests
zeit/next.js canary timneutkens/next.js add/jsconfig-paths Change
_buildManifest.js gzip 61 B 61 B
_buildManife..dule.js gzip 61 B 61 B
Overall change 122 B 122 B
Serverless bundles Overall decrease ✓
zeit/next.js canary timneutkens/next.js add/jsconfig-paths Change
_error.js gzip 293 kB 293 kB -111 B
404.html gzip 1.32 kB 1.32 kB
hooks.html gzip 956 B 956 B
index.js gzip 294 kB 294 kB ⚠️ +7 B
link.js gzip 301 kB 301 kB -34 B
routerDirect.js gzip 300 kB 300 kB ⚠️ +149 B
withRouter.js gzip 300 kB 300 kB -129 B
Overall change 1.49 MB 1.49 MB -118 B

@ijjk
Copy link
Member

ijjk commented Mar 23, 2020

Stats from current PR

Default Server Mode
General Overall increase ⚠️
zeit/next.js canary timneutkens/next.js add/jsconfig-paths Change
buildDuration 9.5s 9.5s ⚠️ +55ms
nodeModulesSize 52.8 MB 52.8 MB ⚠️ +14.6 kB
Client Bundles (main, webpack, commons)
zeit/next.js canary timneutkens/next.js add/jsconfig-paths Change
main-HASH.js gzip 6.24 kB 6.24 kB
webpack-HASH.js gzip 746 B 746 B
de003c3a9d30..c6c1.js gzip 10.1 kB 10.1 kB
framework.HASH.js gzip 39.1 kB 39.1 kB
Overall change 56.2 kB 56.2 kB
Client Bundles (main, webpack, commons) Modern
zeit/next.js canary timneutkens/next.js add/jsconfig-paths Change
main-HASH.module.js gzip 4.78 kB 4.78 kB
webpack-HASH..dule.js gzip 746 B 746 B
de003c3a9d30..dule.js gzip 6.71 kB 6.71 kB
framework.HA..dule.js gzip 39.1 kB 39.1 kB
Overall change 51.4 kB 51.4 kB
Legacy Client Bundles (polyfills)
zeit/next.js canary timneutkens/next.js add/jsconfig-paths Change
polyfills-HASH.js gzip 26.3 kB 26.3 kB
Overall change 26.3 kB 26.3 kB
Client Pages
zeit/next.js canary timneutkens/next.js add/jsconfig-paths Change
_app.js gzip 1.24 kB 1.24 kB
_error.js gzip 3.15 kB 3.15 kB
hooks.js gzip 664 B 664 B
index.js gzip 222 B 222 B
link.js gzip 2.03 kB 2.03 kB
routerDirect.js gzip 279 B 279 B
withRouter.js gzip 278 B 278 B
Overall change 7.86 kB 7.86 kB
Client Pages Modern
zeit/next.js canary timneutkens/next.js add/jsconfig-paths Change
_app.module.js gzip 594 B 594 B
_error.module.js gzip 2.08 kB 2.08 kB
hooks.module.js gzip 370 B 370 B
index.module.js gzip 212 B 212 B
link.module.js gzip 1.48 kB 1.48 kB
routerDirect..dule.js gzip 271 B 271 B
withRouter.m..dule.js gzip 270 B 270 B
Overall change 5.28 kB 5.28 kB
Client Build Manifests
zeit/next.js canary timneutkens/next.js add/jsconfig-paths Change
_buildManifest.js gzip 61 B 61 B
_buildManife..dule.js gzip 61 B 61 B
Overall change 122 B 122 B
Rendered Page Sizes
zeit/next.js canary timneutkens/next.js add/jsconfig-paths Change
index.html gzip 916 B 916 B
link.html gzip 924 B 924 B
withRouter.html gzip 913 B 913 B
Overall change 2.75 kB 2.75 kB

Serverless Mode (Increase detected ⚠️)
General Overall increase ⚠️
zeit/next.js canary timneutkens/next.js add/jsconfig-paths Change
buildDuration 10.2s 10.5s ⚠️ +249ms
nodeModulesSize 52.8 MB 52.8 MB ⚠️ +14.6 kB
Client Bundles (main, webpack, commons)
zeit/next.js canary timneutkens/next.js add/jsconfig-paths Change
main-HASH.js gzip 6.24 kB 6.24 kB
webpack-HASH.js gzip 746 B 746 B
de003c3a9d30..c6c1.js gzip 10.1 kB 10.1 kB
framework.HASH.js gzip 39.1 kB 39.1 kB
Overall change 56.2 kB 56.2 kB
Client Bundles (main, webpack, commons) Modern
zeit/next.js canary timneutkens/next.js add/jsconfig-paths Change
main-HASH.module.js gzip 4.78 kB 4.78 kB
webpack-HASH..dule.js gzip 746 B 746 B
de003c3a9d30..dule.js gzip 6.71 kB 6.71 kB
framework.HA..dule.js gzip 39.1 kB 39.1 kB
Overall change 51.4 kB 51.4 kB
Legacy Client Bundles (polyfills)
zeit/next.js canary timneutkens/next.js add/jsconfig-paths Change
polyfills-HASH.js gzip 26.3 kB 26.3 kB
Overall change 26.3 kB 26.3 kB
Client Pages
zeit/next.js canary timneutkens/next.js add/jsconfig-paths Change
_app.js gzip 1.24 kB 1.24 kB
_error.js gzip 3.15 kB 3.15 kB
hooks.js gzip 664 B 664 B
index.js gzip 222 B 222 B
link.js gzip 2.03 kB 2.03 kB
routerDirect.js gzip 279 B 279 B
withRouter.js gzip 278 B 278 B
Overall change 7.86 kB 7.86 kB
Client Pages Modern
zeit/next.js canary timneutkens/next.js add/jsconfig-paths Change
_app.module.js gzip 594 B 594 B
_error.module.js gzip 2.08 kB 2.08 kB
hooks.module.js gzip 370 B 370 B
index.module.js gzip 212 B 212 B
link.module.js gzip 1.48 kB 1.48 kB
routerDirect..dule.js gzip 271 B 271 B
withRouter.m..dule.js gzip 270 B 270 B
Overall change 5.28 kB 5.28 kB
Client Build Manifests
zeit/next.js canary timneutkens/next.js add/jsconfig-paths Change
_buildManifest.js gzip 61 B 61 B
_buildManife..dule.js gzip 61 B 61 B
Overall change 122 B 122 B
Serverless bundles Overall increase ⚠️
zeit/next.js canary timneutkens/next.js add/jsconfig-paths Change
_error.js gzip 293 kB 294 kB ⚠️ +705 B
404.html gzip 1.32 kB 1.32 kB
hooks.html gzip 956 B 956 B
index.js gzip 293 kB 294 kB ⚠️ +631 B
link.js gzip 301 kB 301 kB -311 B
routerDirect.js gzip 300 kB 300 kB -177 B
withRouter.js gzip 300 kB 300 kB ⚠️ +114 B
Overall change 1.49 MB 1.49 MB ⚠️ +962 B

@timneutkens timneutkens changed the title [WIP] Add support for paths in tsconfig.json and jsconfig.json Add support for paths in tsconfig.json and jsconfig.json Mar 23, 2020
@Janpot
Copy link
Contributor

Janpot commented Mar 23, 2020

@timneutkens Since paths is read from json files, the value is completely static. I'd be interested in being able to set these from next.config.js. That way they can also be populated from environment variables (so that they can be populated from e.g. ZEIT now buildEnv). Is that a use-case that could be considered for this PR as well?

@timneutkens
Copy link
Member Author

That way they can also be populated from environment variables (so that they can be populated from e.g. ZEIT now buildEnv). Is that a use-case that could be considered for this PR as well?

That's not supported by most code editors while tsconfig and jsconfig are. What's the use case?

@timneutkens
Copy link
Member Author

Allowing dynamic values also makes it significantly harder to do any type of caching in the future.

Copy link
Member

@Timer Timer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Excited for this existing convention based feature to work out of the box with zero added configuration to Next.js. 😌

@Timer Timer merged commit 7fce52b into vercel:canary Mar 23, 2020
@timneutkens timneutkens deleted the add/jsconfig-paths branch March 23, 2020 14:46
@jerrygreen
Copy link
Contributor

Wow, I was waiting for this feature! I like using ~/ alias for my src folder, and had to setup this in both next.config.js and tsconfig.json. Now one setting is enough. Thanks a lot!

@Janpot
Copy link
Contributor

Janpot commented Mar 23, 2020

That's not supported by most code editors while tsconfig and jsconfig are. What's the use case?

@timneutkens Use case is to create a localized next.js app, where each locale is an instance of the app on its own. locale data is made dynamic by configuring an alias to a locale folder. (This is just a POC, still trying to feel the water on where next.js is going to go feature-wise.)
It'd basically be used to override what's already in tsconfig. Not to add paths

@Janpot
Copy link
Contributor

Janpot commented Mar 23, 2020

Just a thought, but can't this be (ab)used to enable "transpile node_modules"? as in

"paths": {
  "my-module/*": "node_modules/my-module/*"
}

@felixmosh
Copy link
Contributor

I'm using tsconfig-paths-webpack-plugin for more then a year, and it just works.

Do you know what are differences between your implementation and this lib?

@timneutkens
Copy link
Member Author

timneutkens commented Mar 24, 2020

I'm using tsconfig-paths-webpack-plugin for more then a year, and it just works.

Do you know what are differences between your implementation and this lib?

Significantly more overhead as it uses another library that implements filesystem calls itself instead of relying on webpack's resolver.

Besides that it didn't seem to be based on the actual TypeScript source code, meaning there could be differences in how the output path is calculated.

It also maintained a lot of baggage, eg webpack 3 support and checks that are not needed as we know exactly how the plugin will execute in Next.js.

Initially I forked that resolver but eventually went back to writing a new one as I identified it could be a lot simpler.

@4ortytwo
Copy link
Contributor

I have this in my tsconfig

 "baseUrl": ".",
    "paths": {
      "@r/*": ["./*"], // alias for root
      "@s/*": ["./src/*"] // alias for src
    }

And this in my next.config

experimental: {
    jsconfigPaths: true, 
  },

Let's say I created a new tsx file and used Material-ui <Container></Container> in it without prior import. I see that it's not imported and use 'Add import statement' of WebStorm.
At the top, I see that it's imported from @r(root)/node_modules/@materialui-core...etc.
Anything imported from any other library also has a similarly looking path.

I wonder whether it could be related to the IDE. I'd like to be able to use aliases for my paths but I still want library imports to look concise. How can I achieve that?

@favna
Copy link

favna commented Apr 16, 2020

@4ortytwo That's just how TypeScript resolves paths and in a way it is sort of related to your IDE but I doubt using something like VSCode is going to change it much. My own recommendation would be to not have a path alias for root dir and instead just keep files in a src dir (which you can then also set as the rootDir and baseDir in the tsconfig, which will somewhat improve TSServer since it doesn't have to traverse as many files).

Alternatively, paths are resolved bottom to top (with top taking higher priority I mean) so if you really need to keep the root path alias then add another alias, not sure if this exactly works, might need some tweaking. But I think it'll work:

{
    "paths": {
      "@material-ui/*": ["./node_modules/@material-ui/*"],
      "@r/*": ["./*"],
      "@s/*": ["./src/*"]
    }
}

By example in another project where I work we have our paths set up like this:

{
		"paths": {
			"@utils/*": ["lib/util/*"],
			"@lib/*": ["lib/*"],
			"@root/*": ["*"]
		}
}

And whenever a file is in lib/util it will suggest @utils/file.ts rather than @lib/util/file.ts.

@davecardwell
Copy link
Contributor

Didn't add resolving of extends currently

@timneutkens do you plan to support this? If so, is there an issue I can follow? I would definitely be interested in this feature but cannot use jsconfigPaths as it stands.

@Timer
Copy link
Member

Timer commented Apr 21, 2020

@davecardwell it's on the roadmap, but there's no ETA. Feel free to send a pull request adding support!

@HeadFox
Copy link

HeadFox commented Apr 22, 2020

Hi everyone ✋ Hi @timneutkens
I spend a loooottt of time on this issue and still can't get it work.
There is no problem if the alias concern a folder outside of src but if it's in src, it's not working... .
I have the Typescript error in dev mode.
image

I am trying to do import ExampleContainer from '@containers/ExampleContainer where ExempleContainer is located at src/containers/ExampleContainer

Here is my tsconfig.json:

{
  "compilerOptions": {
    "target": "es5",
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": false,
    "forceConsistentCasingInFileNames": true,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "preserve",
    "baseUrl": "./src",
    "paths": {
      "@containers/*": ["containers/*"],
      "@context/*": ["config/context/*"],
      "@components/*": ["components/*"],
      "@styles/*": ["styles/*"],
      "@contents/*": ["contents/*"],
      "@assets/*": ["assets/*"],
      "@utils/*": ["utils/*"],
      "@constants/*": ["config/constants/*"]
    }
  },
  "exclude": [
    "node_modules"
  ],
  "include": [
    "next-env.d.ts",
    "**/*.ts",
    "**/*.tsx"
  ]
}

My next.config.js :

module.exports = {
  experimental: {
    jsconfigPaths: true,
  },
};

Thank you for your help 👍

@timneutkens
Copy link
Member Author

Can you create a minimal repository?

@HeadFox
Copy link

HeadFox commented Apr 22, 2020

@timneutkens
Copy link
Member Author

Looks like it's related to yarn PnP because when you get rid of .yarn and yarnrc.yml it works fine

@HeadFox
Copy link

HeadFox commented Apr 22, 2020

Okey thanks a lot👍
I will ask @arcanis if he can help me on this.

@timneutkens
Copy link
Member Author

I think we might have to run our plugin before the pnp resolver plugin, though I'm not sure how to test this and it seems incredibly hard to debug installed modules when using PnP.

@timneutkens
Copy link
Member Author

Hopefully this fixes it, as said I couldn't test it: #12104

@HeadFox
Copy link

HeadFox commented Apr 22, 2020

Okey thanks I will test it as soon as I can !
I also send a message in the Yarn's discord

@arcanis
Copy link
Contributor

arcanis commented Apr 22, 2020

It's probably worth a separate issue, but generally speaking we don't recommend using paths or aliases (even though aliases work fine with Webpack last time I checked).

Instead, we strongly suggest to use the link: protocol, which is the idiomatic way to point a package name to a location in a way that all tools can understand without any configuration. More details here.

@rvcas
Copy link
Contributor

rvcas commented Apr 23, 2020

@arcanis worked great thanks

@lauralouiset
Copy link

lauralouiset commented May 6, 2020

This is feature is working for me, but eslint is throwing a "unable to resolve path to module" errors whenever I try this. The build looks fine, but eslint is unhappy. Any insight on how to fix this? Will #11986 help?

update: duh, i figured it out thanks to:
import-js/eslint-plugin-import#1485

@vutpov
Copy link

vutpov commented Jun 6, 2020

Adds support for "paths" in both tsconfig.json or jsconfig.json.

Examples:

Single module alias

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "mytheme": ["components/mytheme.js"]
    }
  }
}

Wildcard alias

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@c/*": ["components/*"],
    }
  }
}

Fallback behavior

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@mytheme/*": ["themes/theme-a/*", "lib/theme-b/*"]
    }
  }
}

Temporarily behind an experimental feature flag:

// next.config.js
module.exports = {
  experimental: {
    jsconfigPaths: true, // enables it for both jsconfig.json and tsconfig.json
  }
}

Fixes #7779

Hi, I'm new to webpack and next.js.
How do I add the jsconfigPaths flag if my next.config.js looks like this:

module.exports = withLess({ withCSS({ webpack(config) { return config } }) })

@Timer
Copy link
Member

Timer commented Jun 6, 2020

This feature does not require the flag, as it has been released as stable. It should work out of the box.

@FBosler
Copy link

FBosler commented Jun 14, 2020

The aliasing works perfectly for me. Thx for the feature. However, I can't use "code peek" (i.e. command+click on the alias to get to the module) any longer, I am using visual studio code. Any idea why that might be?

Cheers

@chris-tse
Copy link

chris-tse commented Jul 6, 2020

Hello, I am using Next 9.4.0 with TypeScript. Setting up the aliases seems to work fine, but Next fails to import the files specified unless the aliases have a slash after the @ symbol (e.g. import { bla } from @/components will work, but @components will fail) even though VSCode is able to interpret it just fine.

Any ideas on why this might be? Could it be attempting to look for a node module with an @ in front, similar to @angular/core for instance?

@lfades
Copy link
Member

lfades commented Jul 7, 2020

@chris-tse I've seen that working in multiple projects, try this:

"paths": {
  "@/*": ["/*"]
}

@chris-tse
Copy link

@lfades I think I figured out the problem. I created a blank tsconfig.json, ran next dev to auto-populate the file, then added the paths to it. It wasn't recognizing the paths properly until I closed and restarted the dev server.

@vercel vercel locked as resolved and limited conversation to collaborators Jan 30, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Automatically configure webpack to handle tsconfig.json's baseUrl and paths