Skip to content

Commit

Permalink
chore: merge main
Browse files Browse the repository at this point in the history
  • Loading branch information
bluwy committed Jul 15, 2022
2 parents 42a6d5b + a2b3131 commit 65edbee
Show file tree
Hide file tree
Showing 12 changed files with 162 additions and 80 deletions.
17 changes: 11 additions & 6 deletions docs/guide/build.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,27 +163,32 @@ Running `vite build` with this config uses a Rollup preset that is oriented towa
```
$ vite build
building for production...
[write] my-lib.mjs 0.08kb, brotli: 0.07kb
[write] my-lib.umd.js 0.30kb, brotli: 0.16kb
dist/my-lib.js 0.08 KiB / gzip: 0.07 KiB
dist/my-lib.umd.cjs 0.30 KiB / gzip: 0.16 KiB
```

Recommended `package.json` for your lib:

```json
{
"name": "my-lib",
"type": "module",
"files": ["dist"],
"main": "./dist/my-lib.umd.js",
"module": "./dist/my-lib.mjs",
"main": "./dist/my-lib.umd.cjs",
"module": "./dist/my-lib.js",
"exports": {
".": {
"import": "./dist/my-lib.mjs",
"require": "./dist/my-lib.umd.js"
"import": "./dist/my-lib.js",
"require": "./dist/my-lib.umd.cjs"
}
}
}
```

::: tip Note
If the `package.json` does not contain `"type": "module"`, Vite will generate different file extensions for Node.js compatibility. `.js` will become `.mjs` and `.cjs` will become `.js`.
:::

## Advanced Base Options

::: warning
Expand Down
99 changes: 42 additions & 57 deletions packages/vite/src/node/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import {
dynamicImport,
isExternalUrl,
isObject,
isTS,
lookupFile,
mergeAlias,
mergeConfig,
Expand Down Expand Up @@ -816,7 +815,6 @@ export async function loadConfigFromFile(
const getTime = () => `${(performance.now() - start).toFixed(2)}ms`

let resolvedPath: string | undefined
let dependencies: string[] = []

if (configFile) {
// explicit config path is always resolved from cwd
Expand Down Expand Up @@ -852,42 +850,13 @@ export async function loadConfigFromFile(
}

try {
let userConfig: UserConfigExport | undefined

if (isESM) {
const fileUrl = pathToFileURL(resolvedPath)
const bundled = await bundleConfigFile(resolvedPath, true)
dependencies = bundled.dependencies

if (isTS(resolvedPath)) {
// before we can register loaders without requiring users to run node
// with --experimental-loader themselves, we have to do a hack here:
// bundle the config file w/ ts transforms first, write it to disk,
// load it with native Node ESM, then delete the file.
fs.writeFileSync(resolvedPath + '.mjs', bundled.code)
try {
userConfig = (await dynamicImport(`${fileUrl}.mjs?t=${Date.now()}`))
.default
} finally {
fs.unlinkSync(resolvedPath + '.mjs')
}
debug(`TS + native esm config loaded in ${getTime()}`, fileUrl)
} else {
// using Function to avoid this from being compiled away by TS/Rollup
// append a query so that we force reload fresh config in case of
// server restart
userConfig = (await dynamicImport(`${fileUrl}?t=${Date.now()}`)).default
debug(`native esm config loaded in ${getTime()}`, fileUrl)
}
}

if (!userConfig) {
// Bundle config file and transpile it to cjs using esbuild.
const bundled = await bundleConfigFile(resolvedPath)
dependencies = bundled.dependencies
userConfig = await loadConfigFromBundledFile(resolvedPath, bundled.code)
debug(`bundled config file loaded in ${getTime()}`)
}
const bundled = await bundleConfigFile(resolvedPath, isESM)
const userConfig = await loadConfigFromBundledFile(
resolvedPath,
bundled.code,
isESM
)
debug(`bundled config file loaded in ${getTime()}`)

const config = await (typeof userConfig === 'function'
? userConfig(configEnv)
Expand All @@ -898,7 +867,7 @@ export async function loadConfigFromFile(
return {
path: normalizePath(resolvedPath),
config,
dependencies
dependencies: bundled.dependencies
}
} catch (e) {
createLogger(logLevel).error(
Expand All @@ -911,7 +880,7 @@ export async function loadConfigFromFile(

async function bundleConfigFile(
fileName: string,
isESM = false
isESM: boolean
): Promise<{ code: string; dependencies: string[] }> {
const importMetaUrlVarName = '__vite_injected_original_import_meta_url'
const result = await build({
Expand Down Expand Up @@ -954,7 +923,7 @@ async function bundleConfigFile(
)};`

return {
loader: isTS(args.path) ? 'ts' : 'js',
loader: args.path.endsWith('ts') ? 'ts' : 'js',
contents: injectValues + contents
}
})
Expand All @@ -976,24 +945,40 @@ interface NodeModuleWithCompile extends NodeModule {
const _require = createRequire(import.meta.url)
async function loadConfigFromBundledFile(
fileName: string,
bundledCode: string
): Promise<UserConfig> {
const extension = path.extname(fileName)
const realFileName = fs.realpathSync(fileName)
const loaderExt = extension in _require.extensions ? extension : '.js'
const defaultLoader = _require.extensions[loaderExt]!
_require.extensions[loaderExt] = (module: NodeModule, filename: string) => {
if (filename === realFileName) {
;(module as NodeModuleWithCompile)._compile(bundledCode, filename)
} else {
defaultLoader(module, filename)
bundledCode: string,
isESM: boolean
): Promise<UserConfigExport> {
// for esm, before we can register loaders without requiring users to run node
// with --experimental-loader themselves, we have to do a hack here:
// write it to disk, load it with native Node ESM, then delete the file.
if (isESM) {
const fileUrl = pathToFileURL(fileName)
fs.writeFileSync(fileName + '.mjs', bundledCode)
try {
return (await dynamicImport(`${fileUrl}.mjs?t=${Date.now()}`)).default
} finally {
fs.unlinkSync(fileName + '.mjs')
}
}
// for cjs, we can register a custom loader via `_require.extensions`
else {
const extension = path.extname(fileName)
const realFileName = fs.realpathSync(fileName)
const loaderExt = extension in _require.extensions ? extension : '.js'
const defaultLoader = _require.extensions[loaderExt]!
_require.extensions[loaderExt] = (module: NodeModule, filename: string) => {
if (filename === realFileName) {
;(module as NodeModuleWithCompile)._compile(bundledCode, filename)
} else {
defaultLoader(module, filename)
}
}
// clear cache in case of server restart
delete _require.cache[_require.resolve(fileName)]
const raw = _require(fileName)
_require.extensions[loaderExt] = defaultLoader
return raw.__esModule ? raw.default : raw
}
// clear cache in case of server restart
delete _require.cache[_require.resolve(fileName)]
const raw = _require(fileName)
_require.extensions[loaderExt] = defaultLoader
return raw.__esModule ? raw.default : raw
}

export function getDepOptimizationConfig(
Expand Down
13 changes: 8 additions & 5 deletions packages/vite/src/node/plugins/esbuild.ts
Original file line number Diff line number Diff line change
Expand Up @@ -437,11 +437,14 @@ function reloadOnTsconfigChange(changedFile: string) {

// reset tsconfck so that recompile works with up2date configs
initTSConfck(server.config).finally(() => {
// force full reload
server.ws.send({
type: 'full-reload',
path: '*'
})
// server may not be available if vite config is updated at the same time
if (server) {
// force full reload
server.ws.send({
type: 'full-reload',
path: '*'
})
}
})
}
}
12 changes: 12 additions & 0 deletions packages/vite/src/node/plugins/resolve.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
isObject,
isPossibleTsOutput,
isTsRequest,
isWindows,
nestedResolveFrom,
normalizePath,
resolveFrom,
Expand Down Expand Up @@ -243,6 +244,17 @@ export function resolvePlugin(resolveOptions: InternalResolveOptions): Plugin {
}
}

// drive relative fs paths (only windows)
if (isWindows && id.startsWith('/')) {
const basedir = importer ? path.dirname(importer) : process.cwd()
const fsPath = path.resolve(basedir, id)
if ((res = tryFsResolve(fsPath, options))) {
isDebug &&
debug(`[drive-relative] ${colors.cyan(id)} -> ${colors.dim(res)}`)
return res
}
}

// absolute fs paths
if (
isNonDriveRelativeAbsolutePath(id) &&
Expand Down
32 changes: 23 additions & 9 deletions packages/vite/src/node/server/middlewares/proxy.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type * as http from 'node:http'
import type * as net from 'node:net'
import httpProxy from 'http-proxy'
import type { Connect } from 'types/connect'
import type { HttpProxy } from 'types/http-proxy'
Expand Down Expand Up @@ -43,16 +44,29 @@ export function proxyMiddleware(
}
const proxy = httpProxy.createProxyServer(opts) as HttpProxy.Server

proxy.on('error', (err, req, res) => {
config.logger.error(`${colors.red(`http proxy error:`)}\n${err.stack}`, {
timestamp: true,
error: err
})
res
.writeHead(500, {
'Content-Type': 'text/plain'
proxy.on('error', (err, req, originalRes) => {
// When it is ws proxy, res is net.Socket
const res = originalRes as http.ServerResponse | net.Socket
if ('req' in res) {
config.logger.error(
`${colors.red(`http proxy error:`)}\n${err.stack}`,
{
timestamp: true,
error: err
}
)
res
.writeHead(500, {
'Content-Type': 'text/plain'
})
.end()
} else {
config.logger.error(`${colors.red(`ws proxy error:`)}\n${err.stack}`, {
timestamp: true,
error: err
})
.end()
res.end()
}
})

if (opts.configure) {
Expand Down
2 changes: 0 additions & 2 deletions packages/vite/src/node/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1130,8 +1130,6 @@ export function stripBomTag(content: string): string {
return content
}

export const isTS = (filename: string): boolean => /\.[cm]?ts$/.test(filename)

const windowsDrivePathPrefixRE = /^[A-Za-z]:[/\\]/

/**
Expand Down
10 changes: 9 additions & 1 deletion playground/resolve/__tests__/resolve.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { isBuild, page } from '~utils'
import { isBuild, isWindows, page } from '~utils'

test('bom import', async () => {
expect(await page.textContent('.utf8-bom')).toMatch('[success]')
Expand Down Expand Up @@ -75,6 +75,14 @@ test('filename with dot', async () => {
expect(await page.textContent('.dot')).toMatch('[success]')
})

test.runIf(isWindows)('drive-relative path', async () => {
expect(await page.textContent('.drive-relative')).toMatch('[success]')
})

test('absolute path', async () => {
expect(await page.textContent('.absolute')).toMatch('[success]')
})

test('browser field', async () => {
expect(await page.textContent('.browser')).toMatch('[success]')
})
Expand Down
1 change: 1 addition & 0 deletions playground/resolve/absolute.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default '[success] absolute'
1 change: 1 addition & 0 deletions playground/resolve/drive-relative.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default '[success] drive relative'
7 changes: 7 additions & 0 deletions playground/resolve/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ <h2>
<h2>Resolve file name containing dot</h2>
<p class="dot">fail</p>

<h2>Resolve drive-relative path (Windows only)</h2>
<p class="drive-relative">fail</p>

<h2>Resolve absolute path</h2>
<p class="absolute">fail</p>

<h2>Browser Field</h2>
<p class="browser">fail</p>

Expand Down Expand Up @@ -89,6 +95,7 @@ <h2>resolve package that contains # in path</h2>
<p class="path-contains-sharp-symbol"></p>

<script type="module">
import '@generated-content-virtual-file'
function text(selector, text) {
document.querySelector(selector).textContent = text
}
Expand Down
43 changes: 43 additions & 0 deletions playground/resolve/vite.config.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,26 @@
const path = require('node:path')
const { normalizePath } = require('vite')

const virtualFile = '@virtual-file'
const virtualId = '\0' + virtualFile

const customVirtualFile = '@custom-virtual-file'
const { a } = require('./config-dep')

const generatedContentVirtualFile = '@generated-content-virtual-file'
const generatedContentImports = [
{
specifier: normalizePath(
path.resolve(__dirname, './drive-relative.js').replace(/^[a-zA-Z]:/, '')
),
elementQuery: '.drive-relative'
},
{
specifier: normalizePath(path.resolve(__dirname, './absolute.js')),
elementQuery: '.absolute'
}
]

module.exports = {
resolve: {
extensions: ['.mjs', '.js', '.es', '.ts'],
Expand Down Expand Up @@ -39,6 +56,32 @@ module.exports = {
return `export const msg = "[success] from custom virtual file"`
}
}
},
{
name: 'generated-content',
resolveId(id) {
if (id === generatedContentVirtualFile) {
return id
}
},
load(id) {
if (id === generatedContentVirtualFile) {
const tests = generatedContentImports
.map(
({ specifier, elementQuery }, i) =>
`import content${i} from ${JSON.stringify(specifier)}\n` +
`text(${JSON.stringify(elementQuery)}, content${i})`
)
.join('\n')

return (
'function text(selector, text) {\n' +
' document.querySelector(selector).textContent = text\n' +
'}\n\n' +
tests
)
}
}
}
],
optimizeDeps: {
Expand Down
Loading

0 comments on commit 65edbee

Please sign in to comment.