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

feat: use module runner to import the config #18637

Open
wants to merge 28 commits into
base: main
Choose a base branch
from

Conversation

sheremet-va
Copy link
Member

@sheremet-va sheremet-va commented Nov 11, 2024

Description

This PR implements an optional --configLoader=runner CLI flag and configLoader: 'runner' inline config property to opt-in to use the module runner instead of bundling the config file. runner only supports ESM syntax and cannot be used with CJS syntax like __filename/__dirname/module.

Some advantages:

  • no need to store a temporary file
  • can import ESM config even in a CJS repo (no type: module needed)
  • importer directory resolved correctly because files are not bundled into a single file (important for monorepos) when an external package is imported

Benchmarking the vitest.config.ts file in the root:

  • 5-6ms to call resolveConfig to construct a simple resolved config (environment requires a config, we can construct a small one just for this environment since we control all private plugins)
  • 0.1ms to init plugin container
  • 23ms is runner.import
    • 2ms to run the code in AsyncFunction
    • 5ms to ssrTransform
    • 16ms to transform with esbuild

Overall the new approach takes ~28-32ms and the old one takes ~14-22ms, but resolving the config is not a hot path fortunetly. We can also shave off 5-6ms if we provide a dummy config

This PR also adds an runnerImport experimental helper that creates a temporary environment with a module runner to import a single file.

@sheremet-va sheremet-va force-pushed the feat/use-runner-to-import branch from fa4e6dd to 3965409 Compare November 11, 2024 11:04
@sapphi-red
Copy link
Member

Probably the test are failing because we support __dirname / __filename in ESM files as well.

@bluwy
Copy link
Member

bluwy commented Nov 12, 2024

This looks similar to what Astro has been doing, which starts up the Vite server and ssrLoadModules the config file, which I had always find hacky 😅 But the module runner feels trimmed down and lightweight enough that this seems better.

Question: Does the module runner handle CJS config files well?

Probably the test are failing because we support __dirname / __filename in ESM files as well.

I'd honestly love if we start to not support them 😅 Separately from this PR, maybe we should start warning if we see them and suggest using import.meta.dirname or import.meta.filename instead if supported, or fiddling with import.meta.url.

@sheremet-va
Copy link
Member Author

Probably the test are failing because we support __dirname / __filename in ESM files as well.

Good catch! We can inject it with a different evaluator.

@sheremet-va
Copy link
Member Author

Good catch! We can inject it with a different evaluator.

Hm, module runner only supports ESM. Injecting __dirname and __filename is not enough 🤔

To support a CJS config, we need to reimplement most of vite-node's "mix cjs-esm" code. Unless we forbid CJS altogether 😄

@sheremet-va sheremet-va added the p2-to-be-discussed Enhancement under consideration (priority) label Nov 12, 2024
@sheremet-va sheremet-va force-pushed the feat/use-runner-to-import branch from e546e04 to 31a84d9 Compare November 14, 2024 16:14
@sheremet-va
Copy link
Member Author

sheremet-va commented Nov 14, 2024

I added --bundleConfig=false option to the CLI; I am open to suggestions on how we should name it. Also need to add a note somewhere in the documentation 🤔

@bluwy
Copy link
Member

bluwy commented Nov 15, 2024

Shouldn't using the module runner be opt-in instead? IIUC there's features like __filename and CJS that won't work properly if this is used by default.

For the option name, it would be nice if it's --configLoader runner perhaps, so that we could also introduce other options like "native" too (if we want that).

@sheremet-va
Copy link
Member Author

It is opt-in, bundleConfig is true by default 😄

@bluwy
Copy link
Member

bluwy commented Nov 15, 2024

Oh didn't notice that, I thought it was false by default. It feels a bit inverted to me to "disable bundleConfig" to "enable import with module runner" though 😅

@sheremet-va
Copy link
Member Author

Oh didn't notice that, I thought it was false by default. It feels a bit inverted to me to "disable bundleConfig" to "enable import with module runner" though 😅

Yeah, I agree. I just couldn't come up with a good name. I like configLoader

@sheremet-va sheremet-va marked this pull request as ready for review November 27, 2024 10:16
@sheremet-va sheremet-va force-pushed the feat/use-runner-to-import branch from 1eb3dcc to 65578af Compare November 27, 2024 10:23
sapphi-red
sapphi-red previously approved these changes Nov 29, 2024
packages/vite/src/node/config.ts Outdated Show resolved Hide resolved
Comment on lines 156 to 159
.option(
'--configLoader <loader>',
`[string] use 'bundle' to bundle the config with esbuild or 'runner' to process it on the fly (default: bundle)`,
)
Copy link
Member

Choose a reason for hiding this comment

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

Do we want to flag this experimental? I guess it's fine not to, but just to confirm.

Copy link
Member Author

Choose a reason for hiding this comment

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

I think we can mark it as experimental since the environment API is experimental 😄

Copy link
Member Author

@sheremet-va sheremet-va Dec 1, 2024

Choose a reason for hiding this comment

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

Marked it like this:

- [string] use 'bundle' to bundle the config with esbuild or 'runner' to process it on the fly (default: bundle)
+ [string] use 'bundle' to bundle the config with esbuild or 'runner' (experimental) to process it on the fly (default: bundle)

@bluwy bluwy added this to the 6.1 milestone Nov 29, 2024
@bluwy bluwy added p3-significant High priority enhancement (priority) and removed p2-to-be-discussed Enhancement under consideration (priority) labels Nov 29, 2024
.default
const environment = createRunnableDevEnvironment(
'inline',
// TODO: provide a dummy config?
Copy link
Member

Choose a reason for hiding this comment

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

Is this TODO comment still relevant?

Copy link
Member Author

Choose a reason for hiding this comment

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

Open to discussion. The environment requires a config and we know every plugin that will be used - should we provide a dummy config instead to make it faster? (I mention the time in the description of the issue)

Copy link
Member

Choose a reason for hiding this comment

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

Can we set configFile: false? It should skip trying to find a config file.

Copy link
Member Author

Choose a reason for hiding this comment

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

Copy link
Member Author

Choose a reason for hiding this comment

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

^this still takes 5-6ms (bundle approach takes 0 for this step as it is not needed)

Copy link
Member

Choose a reason for hiding this comment

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

Ah, so you're trying to reduce the resolveConfig call cost. I'll vote for keep using resolveConfig here than { ...thingseNeeded } as ResolvedConfig.

Maybe we can set envFile: false, cacheDir: process.cwd() and make this call only happen when needed?

const pkgDir = findNearestPackageData(resolvedRoot, packageCache)?.dir

That should eliminate the probably costly fs calls.

Copy link
Member Author

Choose a reason for hiding this comment

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

Adding these shaved off ~1ms

sapphi-red
sapphi-red previously approved these changes Dec 3, 2024
bluwy
bluwy previously approved these changes Dec 4, 2024
sapphi-red
sapphi-red previously approved these changes Dec 5, 2024
Copy link

pkg-pr-new bot commented Dec 22, 2024

Open in Stackblitz

npm i https://pkg.pr.new/vite@18637

commit: e06731d

Comment on lines +73 to +75
} finally {
await environment.close()
}
Copy link
Collaborator

@hi-ogawa hi-ogawa Dec 22, 2024

Choose a reason for hiding this comment

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

If we close the environment here, dynamic import happening after the main module import throws Error: Vite module runner has been closed.. Here is a repro https://stackblitz.com/edit/93byylkl?file=index.js (and I pushed an example as test case)

Do we need to give up explicit close here and rely on proper gc instead?

(Also, dynamic import doesn't appear as dependencies. I'm not sure what is the case with bundled config though.)

Copy link
Member Author

Choose a reason for hiding this comment

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

The environment also overrides global Error.captureStackTrace and close removes the override, so we would have to either disable this and patch the error if it happens or return the environment and document that it should be closed manually (or just return the close method)

dynamic import doesn't appear as dependencies

Dynamic import is not in dependencies because it wasn't executed. We can store ssrResult.dependencies as module.parsedDependencies maybe 🤷🏻

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
p3-significant High priority enhancement (priority) trigger: preview
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

6 participants