-
-
Notifications
You must be signed in to change notification settings - Fork 6.3k
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
perf(resolve): avoid isWorkerRequest and clean up .ts imported a .js #12571
Conversation
Run & review this pull request in StackBlitz Codeflow. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch! Maybe we can simplify the isTsRequest
and isPossibleTsOutput
functions directly here:
vite/packages/vite/src/node/utils.ts
Lines 285 to 289 in 02a46d7
const knownTsRE = /\.(?:ts|mts|cts|tsx)$/ | |
const knownTsOutputRE = /\.(?:js|mjs|cjs|jsx)$/ | |
export const isTsRequest = (url: string): boolean => knownTsRE.test(url) | |
export const isPossibleTsOutput = (url: string): boolean => | |
knownTsOutputRE.test(cleanUrl(url)) |
So we only have to apply one regex to test it.
const type = path.extname(file) | ||
const fileName = file.slice(0, -type.length) | ||
if ( | ||
(res = tryResolveRealFile( | ||
fileName + type.replace('js', 'ts'), | ||
preserveSymlinks, | ||
)) | ||
) | ||
return res | ||
// for .js, also try .tsx | ||
if ( | ||
type === '.js' && | ||
(res = tryResolveRealFile(fileName + '.tsx', preserveSymlinks)) | ||
) | ||
return res |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getPotentialSrcPaths
was returning the following:
- .js -> [.ts, .tsx]
- .mjs -> [.mts, .mtsx] 👀
- .cjs -> [.cts, .ctsx] 👁️ 👁️
- .jsx -> [.tsx]
It was also doing a complex regex taking into account query params. This function was hiding the real checks, better to be explicit and have them expanded here.
Now we no longer check for mtsx
and ctsx
const knownTsOutputRE = /\.(?:js|mjs|cjs|jsx)$/ | ||
const isPossibleTsOutput = (url: string): boolean => knownTsOutputRE.test(url) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved this function here because it isn't a widely used util and it only works without query, that is what we need in tryCleanFsResolve
. I think we are pushing too many functions to utils
lately and we will be better of delaying moving them there to avoid creating abstractions that aren't really needed
test('ts import of file with .js extension', () => { | ||
expect(getPotentialTsSrcPaths('test-file.js')).toEqual([ | ||
'test-file.ts', | ||
'test-file.tsx', | ||
]) | ||
}) | ||
|
||
test('ts import of file with .jsx extension', () => { | ||
expect(getPotentialTsSrcPaths('test-file.jsx')).toEqual(['test-file.tsx']) | ||
}) | ||
|
||
test('ts import of file .mjs,.cjs extension', () => { | ||
expect(getPotentialTsSrcPaths('test-file.cjs')).toEqual([ | ||
'test-file.cts', | ||
'test-file.ctsx', | ||
]) | ||
expect(getPotentialTsSrcPaths('test-file.mjs')).toEqual([ | ||
'test-file.mts', | ||
'test-file.mtsx', | ||
]) | ||
}) | ||
|
||
test('ts import of file with .js before extension', () => { | ||
expect(getPotentialTsSrcPaths('test-file.js.js')).toEqual([ | ||
'test-file.js.ts', | ||
'test-file.js.tsx', | ||
]) | ||
}) | ||
|
||
test('ts import of file with .js and query param', () => { | ||
expect(getPotentialTsSrcPaths('test-file.js.js?lee=123')).toEqual([ | ||
'test-file.js.ts?lee=123', | ||
'test-file.js.tsx?lee=123', | ||
]) | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are testing these in the resolve playground
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice cleanup 🚀
@@ -282,21 +282,8 @@ export const isJSRequest = (url: string): boolean => { | |||
return false | |||
} | |||
|
|||
const knownTsRE = /\.(?:ts|mts|cts|tsx)$/ | |||
const knownTsOutputRE = /\.(?:js|mjs|cjs|jsx)$/ | |||
const knownTsRE = /\.(?:ts|mts|cts|tsx)(?:$|\?)/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI This perf
PR also fixes a bug we run into in Vitest. I was just about to set up a fix PR for it but this exact line fixed it - thanks! My changes for this regexp would have been identical with this one.
Here's minimal repro for it just in case you want to set a test case to prevent this from breaking in future. The file.ts?query
is not detected as TS query and fails to load file.mts
.
import { writeFileSync } from "fs";
import { fileURLToPath } from "url";
import { createServer } from "vite";
writeFileSync(
"./source.ts",
`import { hello } from "./target.mjs"; // Change this to ".mts" or omit extension and it works too
hello();`,
"utf8"
);
writeFileSync(
"./target.mts",
`export const hello = () => "Hello from .mts";`,
"utf8"
);
const __dirname = fileURLToPath(new URL(".", import.meta.url));
const server = await createServer({
configFile: false,
root: __dirname,
server: {
port: 1337,
},
});
await server.listen();
const first = await server.transformRequest("./source.ts");
console.log("First worked!", first.code);
await new Promise((resolve) => setTimeout(resolve, 2000));
const second = await server
.transformRequest("./source.ts?v=123")
.then((result) => console.log("Second worked!", result.code))
.catch((e) => console.error("Second failed", e));
await server.close();
process.exit();
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome! Thanks for letting us know! Maybe not a bad idea to send a PR to add a case for this to one of our playgrounds if you would like.
Description
Alternative to #10273
isWorkerRequest
is costly, and it was showing in the profiles. There is an internal call toparseRequest
, a function we should also review because it uses a regex that for this particular case wasn't needed for example (it is useful with dynamic variables in imports)Using
cleanUrl
should work here. The original issue of having.ts?query
is more general than just in workers.What is the purpose of this pull request?