Skip to content

Commit

Permalink
fix: properly resolve file extensions
Browse files Browse the repository at this point in the history
  • Loading branch information
yyx990803 committed May 23, 2020
1 parent c2c9c43 commit aaf61f4
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 33 deletions.
7 changes: 6 additions & 1 deletion playground/TestModuleResolve.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,26 @@
<div class="index-resolve" :class="indexResolve">
directory index resolve: {{ indexResolve }}
</div>
<div class="dot-resolve" :class="dotResolve">
filename with dot resolve: {{ dotResolve }}
</div>
</template>

<script>
import { createRouter } from 'vue-router'
import { createStore } from 'vuex'
import { add } from 'lodash-es'
import { foo } from './util'
import { bar } from './util/bar.util'
export default {
setup() {
return {
router: typeof createRouter === 'function' ? 'ok' : 'error',
store: typeof createStore === 'function' ? 'ok' : 'error',
optResolve: typeof add === 'function' ? 'ok' : 'error',
indexResolve: foo() ? 'ok' : 'error'
indexResolve: foo() ? 'ok' : 'error',
dotResolve: bar() ? 'ok' : 'error'
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions playground/util/bar.util.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function bar() {
return true
}
53 changes: 34 additions & 19 deletions src/node/resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import {
import { resolveOptimizedCacheDir } from './depOptimizer'
import chalk from 'chalk'

const debug = require('debug')('vite:resolve')

export interface Resolver {
requestToFile?(publicPath: string, root: string): string | undefined
fileToRequest?(filePath: string, root: string): string | undefined
Expand All @@ -22,19 +24,21 @@ export interface InternalResolver {
alias(id: string): string | undefined
}

export const supportedExts = ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json']

const defaultRequestToFile = (publicPath: string, root: string): string => {
if (moduleRE.test(publicPath)) {
const id = publicPath.replace(moduleRE, '')
const cachedNodeModule = idToFileMap.get(id)
if (cachedNodeModule) {
return cachedNodeModule
}
// try to resolve from optimized modules
const optimizedModule = resolveOptimizedModule(root, id)
if (optimizedModule) {
return optimizedModule
}
// try to resolve from normal node_modules
const cachedNodeModule = idToFileMap.get(id)
if (cachedNodeModule) {
return cachedNodeModule
}
const nodeModule = resolveNodeModuleFile(root, id)
if (nodeModule) {
idToFileMap.set(id, nodeModule)
Expand All @@ -52,27 +56,26 @@ const defaultFileToRequest = (filePath: string, root: string): string => {
return `/${slash(path.relative(root, filePath))}`
}

export const supportedExts = ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json']

const debug = require('debug')('vite:resolve')
const isFile = (file: string): boolean => {
try {
return fs.statSync(file).isFile()
} catch (e) {
return false
}
}

export const resolveExt = (id: string) => {
const cleanId = cleanUrl(id)
if (!path.extname(cleanId)) {
if (!isFile(cleanId)) {
let inferredExt = ''
for (const ext of supportedExts) {
try {
// foo -> foo.js
fs.statSync(cleanId + ext)
if (isFile(cleanId + ext)) {
inferredExt = ext
break
} catch (e) {
try {
// foo -> foo/index.js
fs.statSync(path.join(cleanId, '/index' + ext))
inferredExt = '/index' + ext
break
} catch (e) {}
}
if (isFile(path.join(cleanId, '/index' + ext))) {
inferredExt = '/index' + ext
break
}
}
const queryMatch = id.match(/\?.*$/)
Expand Down Expand Up @@ -130,7 +133,19 @@ export function createResolver(
export const jsSrcRE = /\.(?:(?:j|t)sx?|vue)$|\.mjs$/
const deepImportRE = /^([^@][^/]*)\/|^(@[^/]+\/[^/]+)\//

export function resolveBareModule(root: string, id: string, importer: string) {
/**
* Redirects a bare module request to a full path under /@modules/
* It resolves a bare node module id to its full entry path so that relative
* imports from the entry can be correctly resolved.
* e.g.:
* - `import 'foo'` -> `import '/@modules/foo/dist/index.js'`
* - `import 'foo/bar/baz'` -> `import '/@modules/foo/bar/baz'`
*/
export function resolveBareModuleRequest(
root: string,
id: string,
importer: string
) {
const optimized = resolveOptimizedModule(root, id)
if (optimized) {
return id
Expand Down
34 changes: 21 additions & 13 deletions src/node/server/serverPluginModuleRewrite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ import {
parse as parseImports,
ImportSpecifier
} from 'es-module-lexer'
import { InternalResolver, resolveBareModule, jsSrcRE } from '../resolver'
import {
InternalResolver,
resolveBareModuleRequest,
jsSrcRE
} from '../resolver'
import {
debugHmr,
importerMap,
Expand All @@ -25,6 +29,8 @@ import {
resolveRelativeRequest
} from '../utils'
import chalk from 'chalk'
import slash from 'slash'
import { moduleRE } from './serverPluginModuleResolve'

const debug = require('debug')('vite:rewrite')

Expand Down Expand Up @@ -230,27 +236,29 @@ export const resolveImport = (
if (bareImportRE.test(id)) {
// directly resolve bare module names to its entry path so that relative
// imports from it (including source map urls) can work correctly
return `/@modules/${resolveBareModule(root, id, importer)}`
return `/@modules/${resolveBareModuleRequest(root, id, importer)}`
} else {
// 1. relative to absolute
// ./foo -> /some/path/foo
let { pathname, query } = resolveRelativeRequest(importer, id)
// append an extension to extension-less imports
if (!path.extname(pathname)) {
const file = resolver.requestToFile(pathname)
const indexMatch = file.match(/\/index\.\w+$/)
if (indexMatch) {
pathname = pathname.replace(/\/(index)?$/, '') + indexMatch[0]
} else {
pathname += path.extname(file)
}

// 2. if this is a relative import between files under /@modules/, preserve
// them as-is
if (moduleRE.test(pathname)) {
return pathname
}

// mark non-src imports
// 3. resolve extensions.
const file = resolver.requestToFile(pathname)
pathname = '/' + slash(path.relative(root, file))

// 4. mark non-src imports
const ext = path.extname(pathname)
if (ext && !jsSrcRE.test(pathname)) {
query += `${query ? `&` : `?`}import`
}

// force re-fetch dirty imports by appending timestamp
// 5. force re-fetch dirty imports by appending timestamp
if (timestamp) {
const dirtyFiles = hmrDirtyFilesMap.get(timestamp)
// only force re-fetch if this is a marked dirty file (in the import
Expand Down
1 change: 1 addition & 0 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ describe('vite', () => {
expect(await getText('.module-resolve-store')).toMatch('ok')
expect(await getText('.module-resolve-optimize')).toMatch('ok')
expect(await getText('.index-resolve')).toMatch('ok')
expect(await getText('.dot-resolve')).toMatch('ok')
})

if (!isBuild) {
Expand Down

0 comments on commit aaf61f4

Please sign in to comment.