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

fix(vite-node): normalize relative paths to be from current working directory #952

Merged
merged 7 commits into from
Mar 16, 2022
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 3 additions & 1 deletion packages/vite-node/src/client.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { createRequire } from 'module'
import { fileURLToPath, pathToFileURL } from 'url'
import vm from 'vm'
import { dirname, extname, isAbsolute, resolve } from 'pathe'
import { dirname, extname, isAbsolute, normalize, resolve } from 'pathe'
import { isNodeBuiltin } from 'mlly'
import { isPrimitive, normalizeId, slash, toFilePath } from './utils'
import type { ModuleCache, ViteNodeRunnerOptions } from './types'
Expand Down Expand Up @@ -56,9 +56,11 @@ export class ViteNodeRunner {
// probably means it was passed as variable
// and wasn't transformed by Vite
if (this.shouldResolveId(dep)) {
if (extname(dep)) dep = normalize(`${dirname(id)}/${dep}`)
Copy link
Member

Choose a reason for hiding this comment

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

So after digging up I know why I didn't like this code from the first glance.

id is treated as /src/path instead of ${root}/src/path - this is why resolveId doesn't work. we should combine root and id when needed and pass it to this.options.resolveId. or better teach resolveId how to handle imports without root in them (resolveId knows root from this.server.options.root)

And extname has nothing to do with it

So, in the end it is importer that is wrong, not the dep. This code in server.ts worked for me:

    if (importer && !importer.startsWith(this.server.config.root))
      importer = join(this.server.config.root, importer)

Another path is to fix resolveId in Vite inself

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ID looked like the path from the current directory to the file that has the import unless I miss understood something. I needed to check extname otherwise I was hitting problems with node internals and package names, but I guess shouldResolveId is handling those cases now?

Regardless I was just trying fix one of the few problems remaining so I could fully adopt this library for my companies code base, any suggested fix for my problem will be fine for me since you know the code base better. I like trying to provide a solution rather then making another issue. I can add the above to the server.ts code in my PR.

Copy link
Member

Choose a reason for hiding this comment

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

Try to add this to server.ts in your PR, yeah. The dep value is fine - since it is not absolute, it will be resolved according to importer, but importer was incorrect. So the solution to your problem would be to fix importer.

You also can import files without specifying the extension name, so the problem can rise again. I think the suggested solution is more well around.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I updated my PR, I think I added it in the right spot. Test is still passing so seems to have worked.

const resolvedDep = await this.options.resolveId(dep, id)
dep = resolvedDep?.id?.replace(this.root, '') || dep
}

if (callstack.includes(dep)) {
if (!this.moduleCache.get(dep)?.exports)
throw new Error(`[vite-node] Circular dependency detected\nStack:\n${[...callstack, dep].reverse().map(p => `- ${p}`).join('\n')}`)
Expand Down
3 changes: 3 additions & 0 deletions test/core/src/relative-import.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function dynamicRelativeImport(file: string) {
return import(`./${file}.ts`)
}
8 changes: 8 additions & 0 deletions test/core/test/imports.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { expect, test } from 'vitest'
import { dynamicRelativeImport } from '../src/relative-import'

test('dynamic relative import works', async() => {
const stringTimeoutMod = await import('./../src/timeout')
Expand All @@ -9,6 +10,13 @@ test('dynamic relative import works', async() => {
expect(stringTimeoutMod).toBe(variableTimeoutMod)
})

test('Relative imports in imported modules work', async() => {
const relativeImportFromFile = await dynamicRelativeImport('timeout')
const directImport = await import('./../src/timeout')

expect(relativeImportFromFile).toBe(directImport)
})

test('dynamic aliased import works', async() => {
const stringTimeoutMod = await import('./../src/timeout')

Expand Down