microsoft/tslint-microsoft-contrib

How should I use no-relative-imports

AndreasCag opened this issue ยท 18 comments

Hello!
I am amazed how productive became my work with vscode, typescript and this config.
Thank you for your great work with it.

I'm new with typescript and tslint, so I'm a bit confused how to use rule 'no-relative-imports'.

Suppose I have small frontend project with Webpack + Vue + ts-loader.
Main core written with typescript, other files with javascript.

In folder 'core' I have two files:

ElData.ts

...

export class AnimationElement {
  ...
}

...

SportData.ts

import * as ElData from './ElData';

...

The thing is, 'no-relative-imports' throws error:
[tslint] Imported module is being loaded from a relative path. Please use an absolute path: import * as ElData from './ElData'; (no-relative-imports)

Hmm, disable rule is not my way to resolve a problem!

  1. First thing I try is just remove './' from my import like this:

SportData.ts

import * as ElData from 'ElData';

...

[tslint] Module 'ElData' is not listed as dependency in package.json (no-implicit-dependencies)

Well, lets try another way

  1. Improve tsconfig.json
{
  ...
  "compilerOptions": {
    ...
    "baseUrl": "./src"
  }
}

And then

SportData.ts

import * as ElData from 'core/ElData';

...

[tslint] Module 'core' is not listed as dependency in package.json (no-implicit-dependencies)
[tslint] Submodule import paths from this package are disallowed; import from the root instead (no-submodule-imports)

Arrgh

  1. Last hope

My webpack config a bit supercharged with aliases

{
  ....
  resolve: {
    alias: {
      '@': resolve('src'),
    }
  },
  ...
}

SportData.ts

import * as ElData from '@/core/ElData';

...

[tslint] Module '@/core' is not listed as dependency in package.json (no-implicit-dependencies)
[tslint] Submodule import paths from this package are disallowed; import from the root instead (no-submodule-imports)
[ts] Cannot find module '@/core/ElData'.

Jesus, all I achieved is just increase of errors.

How should I cook my imports?

I have the same question. I cannot figure out how to get no-relative-imports to work with the no-submodule-includes and the no-implicit-dependencies issues.

Just seems like too much hassle so I disabled the no-relative-imports rule.

Hi there - welcome to TS/TSLint/VSCode/etc.! Glad you're enjoying the new area ๐Ÿ˜Š it's great to have you!

I personally don't use this rule in my projects. The idea as I understand it is to encourage setting a baseUrl in your tsconfig.json, but that seems uncomfortable to me. Should we deprecate this rule @HamletDRC ? Do you have more context around it?

I've tried using the baseUrl method to get rid of the no relative paths issues, however when I do that I start violating two other rules: no-submodule-includes and no-implicit-dependencies.

no-submodule-includes is hit because I include components from subfolders in my solution.
no-implicit-dependencies is hit because the rule thinks that if I am not doing a relative include, then the code must be coming from a NPM package and wants me to include the package in my package.json file.

Can we drop the no-relative-imports rule from the microsoft tslint rule set? It might make sense for tiny projects or for npm packages but definitely does not work for website projects using tools such as webpack.

violating two other rules

Amusing. Yes, I'm in support of this. Let's leave this issue open for a few weeks, we can mark it as deprecated, then remove in a future major version.

@JoshuaKGoldberg Please don't remove this, I really like this rule. We don't use the no-submodule-includes and just wrapped the no-implicit-dependencies rule to ignore the files in the baseUrl path and it works really well!

Maybe we can just publish this as a standalone rule?

Interesting! @LukeSheard - could you please give a little more info on why you chose that way?

@JoshuaKGoldberg While I probably can't give amazingly detailed specifics.. Essentially we run a very large monorepo + single package.json at the root and so we run with the idea that our "top level" is a set of packages itself, meaning that imports look like

import { myThing } from "my/other/thing/over/here";

Since we other languages side by side with TypeScript we find that it gives nice consistency and also means if you move a file you can easily update imports by just searching for the module path.

That's a really interesting approach, thanks for posting it! Have you tried good-fences?

Also, would you mind also posting your tslint.json config here (or just the relevant rules around paths, importing, and/or modules)?

Instead of deprecating this rule, since this is a very legitimate use case, let's say we remove it from the recommended ruleset and mention the monorepo use case in its documentation?

@JoshuaKGoldberg I haven't come across good-fences before, but it looks very useful for our code base!

Our tslint.json is a little bit up in the air right now as we begin to let the dust settle, but I'll get follow up in a couple of days.

I think that removing it from the default recommended configuration is the best solution, leaving it for users to bring it if it works for their use case.

Just to follow up here this is a the rough selection of our rules around imports and exports. To make things slightly more opaque we actually wrap this up with a script to check the no-implicit-dependencies outputs from tslint so that we ignore modules we know we have in the codebase.

{
    "extends": ["tslint:recommended", "tslint-microsoft-contrib", ... ],
    "defaultSeverity": "warning",
    "rules": {
        "no-default-export": true,
        "no-implicit-dependencies": [true],
        "no-submodule-imports": false,
        "no-relative-imports": true,
        "export-name": false,
        "no-import-side-effect": [true, { "ignore-module": "(\\.css)$" }],
    }
}

While I can get behind the original reasons, I believe this rule should be removed for a few reasons:

  1. It's unconventional in the sense that it differs from traditional module resolution and needs a specific configuration.
  2. As said earlier in this thread, it conflicts with other rules.
  3. Typescript doesn't replace the aliases with the actual file path, e.g if ~/* is configured to be the root, ~/actions/todo will remain as is when compiled. Whatever is consuming the bundle would need to resolve the aliases as well. From what I read, Typescript is unlikely to change this behavior. See TypeScript#15479.
  4. Since it's unconventional, tools are likely to break. I'm using AVA and it fails to resolve the aliases. Perhaps the problem comes from AVA but still, relative paths (aka "traditional paths") are properly resolved.

I'm still pretty new to Typescript and getting my head around this rule is what took me the most time so far.

In v6.0 allow-siblings option will be available which allows imports from same or nested folders so ./ElData from original post will be allowed.

Does this option solve other problems (if any) in this thread?

Also same experience here with conflicting "no-submodule-imports", "no-implicit-dependencies" and "no-relative-imports" rules.

I had defined an alias @app for absolute imports of project modules.
E.g. import xy from "@app/foo/bar"

What helped me, was to add this alias to the whitelist of the rules like this:

"no-implicit-dependencies": [true, ["@app"]],
"no-submodule-imports": [true, "@app"],
"no-relative-imports": [true, "allow-siblings"]

This is not possible for Angular 7. Already tried every answer on the web :(

Sorry to hear that. I can only speak from a react-ts app perspective. Solution above worked for me.

Tweaked @ford04's solution and it seems to work well with a default React project structure:

{
  "no-implicit-dependencies": [true, "dev", ["src"]],
  "no-submodule-imports": [true, "src"],
  "no-relative-imports": [true, "allow-siblings"]
}

Just ran across this project that may be relevant to some people in this discussion.

https://github.com/vladimiry/tslint-rules-bunch

โ˜ ๏ธ It's time! โ˜ ๏ธ

Per #876, this repository is no longer accepting feature pull requests. TSLint is being deprecated and we recommend you switch to https://typescript-eslint.io.

Thanks for open sourcing with us, everyone!