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

Make ssr.noExternal shallow #8991

Open
4 tasks done
brillout opened this issue Jul 8, 2022 · 12 comments
Open
4 tasks done

Make ssr.noExternal shallow #8991

brillout opened this issue Jul 8, 2022 · 12 comments

Comments

@brillout
Copy link
Contributor

brillout commented Jul 8, 2022

Description

Current behavior in v2: when an SSR dependency some-library is noExternal, then Vite will as well noExternal all some-library's dependencies.

Current behavior in v3 (beta): slightly better because it only noExternal dependencies of some-libraries that cannot be resolved. (Correct me if I'm wrong.)

Suggested solution

Externalize all some-library's dependencies. I.e. only make some-library noExternal.

Priority

Low. (Because AFAICT this doesn't cause the same problems than noExternalize too many SSR user dependencies.)

Alternative

No response

Additional context

No response

Validations

@patak-dev
Copy link
Member

Current behavior in v2: when an SSR dependency some-library is noExternal, then Vite will as well noExternal all some-library's dependencies.

It is the other way around. IIUC, in v2 sub-dependencies of some-library will end up being external

Current behavior in v3 (beta): slightly better because it only noExternal dependencies of some-libraries that cannot be resolved. (Correct me if I'm wrong.)

This is correct, if a sub-dependency is a direct dependency of your project it will be external, but if not they will be noExternal as node will not be able to resolve to them depending on your setup.

What you are proposing here is to keep v2's logic. But as we were discussing in #8983, this is broken if we also don't rewrite imports to resolved node_modules paths that may also break in prod, no?

@brillout
Copy link
Contributor Author

brillout commented Jul 8, 2022

Since some-library is noExternal, it lives in dist/, which means that, with pnpm (without shamefully-hoist=true), the some-library code living in dist/ cannot resolve its dependencies anymore.

Potential solution: resolve the import statements in dist/: instead of having import 'some-library-dep' we would have import '../../node_modules/.pnpm/some-library-dep@0.0.23_biqbaboplfbrettd7655fr4n2y/node_modules/some-library-dep'.

I'm not really sure about rewriting imports for externalized deps since it makes the output code not portable
@bluwy

It means that the user shouldn't modify node_modules after having run $ vite build which I think is a fairly safe assumption. I wonder if there are use cases where dist/ is uploaded without node_modules/. Usually it's either you let the deploy provider both install and build, or you make it on some other machine and upload the whole thing. I'm not aware of any deploy use case where only dist/ is uploaded?

@brillout
Copy link
Contributor Author

brillout commented Jul 8, 2022

It is the other way around. IIUC, in v2 sub-dependencies of some-library will end up being external

I just tried and it does seem to be as I described. (The VikePress dependency tweemoji is living in dist/.)

this is broken if we also don't rewrite imports to resolved node_modules paths that may also break in prod, no?

Yes exactly, see my previous reply.

@patak-dev
Copy link
Member

patak-dev commented Jul 8, 2022

I think this isn't difficult to implement now that we do lazy SSR externals/noExternal resolving, maybe it could be optional? ssr.shallowNoExternal: true?

@patak-dev
Copy link
Member

Or maybe we could use a similar scheme to what @bluwy implemented with

optimizeDeps.include: [ 'dep > nested' ]

And have

ssr.external: [ 'vikepress > tweemoji' ]

And even

ssr.external: [ 'vikepress > *' ]

The global boolean may cause surprises as every other user dep will work as shallow no external too.

@brillout
Copy link
Contributor Author

brillout commented Jul 8, 2022

Sounds good.

Also maybe: ssr.noExternalShallow. Or if we do it shallow by default (which I think we should): ssr.noExternal => shallow, ssr.noExternalDeep => deep.

(Btw. the naming ssr.noExternal and optimizeDeps was quite confusing to me when I first played with Vite. Maybe we can revisit the naming once the dust settles.)

@patak-dev
Copy link
Member

Yes, maybe it should be ssr.bundle? I don't know about making shallow the default, I think I'm missing context to make that call

@brillout
Copy link
Contributor Author

brillout commented Jul 8, 2022

👍 Also ssr.include if the user wants it to be included in dev as well. And maybe we can say "excluded" instead of "externalized". (I know that Rollup's naming is "external" but I'd suggest we diverge.)

So:

  • ssr.bundle = noExternal only in build.
  • ssr.include = noExternal for both dev + build.

About the portability issue: since we make shallow/deep configurable, it's not a blocker anymore so we're good.

@bluwy
Copy link
Member

bluwy commented Jul 9, 2022

Re portability, my concern are for setups that publish dist only and run npm install --prod elsewhere. I think this is still something we should support, as it won't work with this proposal as vikepress and twemoji would be part of devDependencies and installing prod deps would exclude them, which would fail to import in runtime.

This could be fixed though if vikepress is a dependency but it may be wasteful and be in an odd spot IMO. And if it'd already be a dependency, maybe it should stay externalized in the first place 🤔

Perhaps this can be implemented as a Vite plugin for now to test it out.

@brillout
Copy link
Contributor Author

publish dist only and run npm install --prod elsewhere

I wonder if this has a real world use case; I don't know why one would do this.

Also when using lock files, which is an increasingly ubiquitous practice, this may actually still work.

I think this is still something we should support

Yes I agree and that's why the idea is to support both shallow externalization and deep externalization.

Those who need to build before installation can use deep externalization.

@bluwy
Copy link
Member

bluwy commented Jul 13, 2022

I wonder if this has a real world use case; I don't know why one would do this.

Also when using lock files, which is an increasingly ubiquitous practice, this may actually still work.

It's the reason why we split dependencies and devDependencies in the first place, so I think it's fairly common. At least on SvelteKit side, this is commonly done for adapter-node. Lock files won't help unless vikepress is a "dependency" which npm install --prod would only then install. If it's a "devDependency", we can't access twemoji even if we resolved to a deep path because it's not installed.

@brillout
Copy link
Contributor Author

It's the reason why we split dependencies and devDependencies in the first place, so I think it's fairly common.

I see, that makes sense.

Lock files won't help unless vikepress is a "dependency" which npm install --prod would only then install. If it's a "devDependency", we can't access twemoji even if we resolved to a deep path because it's not installed.

Vite only needs to resolve twemoji If it occurs somewhere in the code. If it occurs somewhere in the code then it's not a devDependency to start with.

So I still think a lock file works (as long as the package manager produces a node_modules file structure that is stable given a specific lock file).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants