sveltejs / language-tools

The Svelte Language Server, and official extensions which use it
MIT License
1.26k stars 201 forks source link

Support importing Svelte components without extension #555

Closed matthewmueller closed 2 years ago

matthewmueller commented 4 years ago

Is your feature request related to a problem? Please describe.

I have some complex components with many subcomponents. The subcomponents are private and come together to make one large component.

Here's an example:

form/
  input.svelte
  textarea.svelte
  index.svelte
  radio.svelte
  checkbox.svelte

Describe the solution you'd like

I'd like to be able to resolve form/ to form/index.svelte rather than explicitly writing form/index.svelte.

I think this plugin by default should follow node.js-style resolution for files that have the .svelte extension.

form/             => form/index.svelte
form/input        => form/input.svelte (or form/input/index.svelte)
form/input.svelte => form/input.svelte
dummdidumm commented 4 years ago

I'm against this, because you then have no distinction anymore which file is a Svelte file and which file is a normal file. What happens if you have index.js and index.svelte in the same folder? What should be imported? This would be a breaking change. Also it is not supported by the bundlers either as far as I know.

For your specific case, I suggest to add a index.js with the following contents:

export { default as Index } from './index.svelte';
// ... you can add your other Svelte files here as well

And then import it like

<script>
   import { Index } from './form';
</script>
matthewmueller commented 4 years ago

Thanks for your response. I'd like to double-check that we're on the same page because I didn't consider this issue to be very controversial.

I'm asking for the ability to resolve Svelte files the same way you resolve any Javascript, Typescript, JSX, or TSX file.

In the same way you don't need to add the extension in Typescript or React projects:

import A from "./a.ts"
// you can just do
import A from "./a"

The VSCode typescript resolver follows the Node.js resolution rules. Your point is super valid when we're talking about designing Node's Resolution rules. I agree with you on being more restrictive.

But given that ship has sailed, what makes the .svelte extension any different than the .ts, .tsx, .jsx extensions?

rixo commented 4 years ago

Skipping extensions is historically an idiosyncrasy of Node's resolution mechanism. Npm's Isaac Z. Schlueter who, I guess, had been involved in the discussion at the time, has since expressed regret about it. And he was met with incomprehension in the comments to his post. Rest assured this is surely a very controversial topic.

Standard ESM imports have nothing in the sense of skipping extensions, and that's the direction Svelte (and Rollup) tend to align with, by default.

That being said, that's purely a concern of the bundler you use. If I remember correctly, with Webpack .svelte extension is optional by default. And in Rollup, it's an option of the node-resolve plugin.

dummdidumm commented 4 years ago

That being said, that's purely a concern of the bundler you use. If I remember correctly, with Webpack .svelte extension is optional by default. And in Rollup, it's an option of the node-resolve plugin.

Interesting, I did not know that the extension is optional by default in the webpack plugin.

Supporting this would mean changing the way module resolution is done in the language-server, and possibly other things. Also, it would need to be a setting so that people can declare whether or not they want this behavior - after all, they might need to change their build config as well if they want Svelte imports written without .svelte.

I'll reopen so others can weigh in on this.

antony commented 4 years ago

In case you want to know Rich's feelings on this: https://twitter.com/Rich_Harris/status/1002959357250801664 - and those are also the same as Ryan Dahl, creator of Node.

I too agree. Just because you can resolve files without an extension, doesn't mean you should.

oscarhermoso commented 2 years ago

Agreeing with @matthewmueller, and here's an example to show how it's helpful:

Let's say I'm building out some components for a cooking website, but my Ingredients.svelte file is getting really long. I want to split it into smaller parts.

I have a file structure looks like this before refactoring;

└── cake-recipe
    ├── Ingredients.svelte
    └── Instructions.svelte

...and and looks like this afterwards:

└── cake-recipe
    ├── Ingredients/
    └── Instructions.svelte

Expanding the directory, we see cake-recipe/Ingregdients/index.js containing the default export as recommended by @dummdidumm.

└── cake-recipe
    ├── Ingredients
    │   ├── CakeMixture.svelte
    │   ├── Icing.svelte
    │   ├── Ingredients.svelte
    │   └── index.js
    └── Instructions.svelte

All other files import the Ingredients component exactly the same way, with 'import Ingredients from 'cake-recipe/Ingredients'. No further renaming of components required. Very useful if working in teams or with a large codebase.

I've also seen this pattern commonly in other languages/frameworks, React exactly the same way, and often in Django (with __init__.py) https://stackoverflow.com/questions/1884990/refactor-large-models-py-file-in-django-app

matthewmueller commented 2 years ago

Just an FYI that I've changed my mind on this one. With ESM support in the browser expecting full paths and a lot of the new JS runtimes following suit, this is becoming more and more Node.js-specific.

I'm going to close this issue. Thanks for the interesting discussions everyone!