Skip to content

Commit

Permalink
feat: support configuring publicDir via config
Browse files Browse the repository at this point in the history
close #1799
  • Loading branch information
yyx990803 committed Jan 29, 2021
1 parent f4719e4 commit 470ceb8
Show file tree
Hide file tree
Showing 15 changed files with 52 additions and 33 deletions.
9 changes: 8 additions & 1 deletion docs/config/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ export default ({ command, mode }) => {
- **Type:** `string`
- **Default:** `process.cwd()`

Project root directory. Can be an absolute path, or a path relative from the location of the config file itself.
Project root directory (where `index.html` is located). Can be an absolute path, or a path relative from the location of the config file itself.

See [Project Root](/guide/#project-root) for more details.

Expand All @@ -111,6 +111,13 @@ export default ({ command, mode }) => {

See [Public Base Path](/guide/build#public-base-path) for more details.

### publicDir

- **Type:** `string`
- **Default:** `"public"`

Directory to serve as plain static assets. Files in this directory are served at `/` during dev and copied to the root of `outDir` during build, and are always served or copied as-is without transform. The value can be either an absolute file system path or a path relative to project root.

### mode

- **Type:** `string`
Expand Down
2 changes: 2 additions & 0 deletions docs/guide/features.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,8 @@ If you have assets that are:

Then you can place the asset in a special `public` directory under your project root. Assets in this directory will be served at root path `/` during dev, and copied to the root of the dist directory as-is.

The directory defaults to `<root>/public`, but can be configured via the [`publicDir` option](/config/#publicdir).

Note that:

- You should always reference `public` assets using root absolute path - for example, `public/icon.png` should be referenced in source code as `/icon.png`.
Expand Down
2 changes: 1 addition & 1 deletion docs/guide/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ One thing you may have noticed is that in a Vite project, `index.html` is front-

Vite treats `index.html` as source code and part of the module graph. It resolves `<script type="module" src="...">` that references your JavaScript source code. Even inline `<script type="module">` and CSS referenced via `<link href>` also enjoy Vite-specific features. In addition, URLs inside `index.html` are automatically rebased so there's no need for special `%PUBLIC_URL%` placeholders.

Similar to static http servers, Vite has the concept of a "root directory" from which your files are served from. Absolute URLs in your source code will be resolved using the project root as base, so you can write code as if you are working with a normal static file server (except way more powerful!). Vite is also capable of handling dependencies that resolve to out-of-root file system locations, which makes it usable even in a monorepo-based setup.
Similar to static http servers, Vite has the concept of a "root directory" from which your files are served from. You will see it referenced as `<root>` throughout the rest of the docs. Absolute URLs in your source code will be resolved using the project root as base, so you can write code as if you are working with a normal static file server (except way more powerful!). Vite is also capable of handling dependencies that resolve to out-of-root file system locations, which makes it usable even in a monorepo-based setup.

Vite also supports [multi-page apps](./build#multi-page-app) with multiple `.html` entry points.

Expand Down
8 changes: 5 additions & 3 deletions packages/playground/assets/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,20 @@ <h1>Assets</h1>

<p class="base"></p>

<h2>Raw References from /public</h2>
<h2>Raw References from publicDir</h2>
<ul>
<li class="raw-js"></li>
<script src="/raw.js"></script>
<li class="raw-css">Raw CSS from /public should load (this should be red)</li>
<li class="raw-css">
Raw CSS from publicDir should load (this should be red)
</li>
</ul>

<h2>Asset Imports from JS</h2>
<ul>
<li>Relative: <code class="asset-import-relative"></code></li>
<li>Absolute: <code class="asset-import-absolute"></code></li>
<li>From /public: <code class="public-import"></code></li>
<li>From publicDir: <code class="public-import"></code></li>
</ul>

<h2>CSS url references</h2>
Expand Down
File renamed without changes
File renamed without changes.
File renamed without changes.
1 change: 1 addition & 0 deletions packages/playground/assets/vite.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*/
module.exports = {
base: '/foo/',
publicDir: 'static',
build: {
outDir: 'dist/foo'
}
Expand Down
5 changes: 2 additions & 3 deletions packages/vite/src/node/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,6 @@ async function doBuild(
}

const outDir = resolve(options.outDir)
const publicDir = resolve('public')

// inject ssr arg to plugin load/transform hooks
const plugins = (ssr
Expand Down Expand Up @@ -395,8 +394,8 @@ async function doBuild(
)
}
}
if (fs.existsSync(publicDir)) {
copyDir(publicDir, outDir)
if (fs.existsSync(config.publicDir)) {
copyDir(config.publicDir, outDir)
}
}

Expand Down
13 changes: 11 additions & 2 deletions packages/vite/src/node/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,13 @@ export interface UserConfig {
* @default '/'
*/
base?: string
/**
* Directory to serve as plain static assets. Files in this directory are
* served and copied to build dist dir as-is without transform. The value
* can be either an absolute file system path or a path relative to <root>.
* @default 'public'
*/
publicDir?: string
/**
* Explicitly set a mode to run in. This will override the default mode for
* each command, and can be overridden by the command line --mode option.
Expand Down Expand Up @@ -146,6 +153,8 @@ export type ResolvedConfig = Readonly<
configFile: string | undefined
inlineConfig: UserConfig
root: string
base: string
publicDir: string
command: 'build' | 'serve'
mode: string
isProduction: boolean
Expand All @@ -157,7 +166,6 @@ export type ResolvedConfig = Readonly<
build: ResolvedBuildOptions
assetsInclude: (file: string) => boolean
logger: Logger
base: string
createResolver: (options?: {
asSrc?: boolean
tryIndex?: boolean | string
Expand Down Expand Up @@ -339,6 +347,8 @@ export async function resolveConfig(
configFile: configFile ? normalizePath(configFile) : undefined,
inlineConfig,
root: resolvedRoot,
base: BASE_URL,
publicDir: path.resolve(resolvedRoot, config.publicDir || 'public'),
command,
mode,
isProduction,
Expand All @@ -358,7 +368,6 @@ export async function resolveConfig(
return DEFAULT_ASSETS_RE.test(file) || assetsFilter(file)
},
logger,
base: BASE_URL,
createResolver
}

Expand Down
25 changes: 14 additions & 11 deletions packages/vite/src/node/plugins/asset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export function assetPlugin(config: ResolvedConfig): Plugin {
}
// imports to absolute urls pointing to files in /public
// will fail to resolve in the main resolver. handle them here.
const publicFile = checkPublicFile(id, config.root)
const publicFile = checkPublicFile(id, config)
if (publicFile) {
return id
}
Expand All @@ -43,7 +43,7 @@ export function assetPlugin(config: ResolvedConfig): Plugin {

// raw requests, read from disk
if (/(\?|&)raw\b/.test(id)) {
const file = checkPublicFile(id, config.root) || cleanUrl(id)
const file = checkPublicFile(id, config) || cleanUrl(id)
// raw query, read file and return as string
return `export default ${JSON.stringify(
await fsp.readFile(file, 'utf-8')
Expand Down Expand Up @@ -100,13 +100,16 @@ export function assetPlugin(config: ResolvedConfig): Plugin {
}
}

export function checkPublicFile(url: string, root: string): string | undefined {
export function checkPublicFile(
url: string,
{ publicDir }: ResolvedConfig
): string | undefined {
// note if the file is in /public, the resolver would have returned it
// as-is so it's not going to be a fully resolved path.
if (!url.startsWith('/')) {
return
}
const publicFile = path.posix.join(root, 'public', cleanUrl(url))
const publicFile = path.join(publicDir, cleanUrl(url))
if (fs.existsSync(publicFile)) {
return publicFile
} else {
Expand All @@ -126,20 +129,20 @@ export function fileToUrl(
}
}

function fileToDevUrl(id: string, { root, base }: ResolvedConfig) {
function fileToDevUrl(id: string, config: ResolvedConfig) {
let rtn: string
if (checkPublicFile(id, root)) {
if (checkPublicFile(id, config)) {
// in public dir, keep the url as-is
rtn = id
} else if (id.startsWith(root)) {
} else if (id.startsWith(config.root)) {
// in project root, infer short public path
rtn = '/' + path.posix.relative(root, id)
rtn = '/' + path.posix.relative(config.root, id)
} else {
// outside of project root, use absolute fs path
// (this is special handled by the serve static middleware
rtn = path.posix.join(FS_PREFIX + id)
}
return base + rtn.replace(/^\//, '')
return config.base + rtn.replace(/^\//, '')
}

const assetCache = new WeakMap<ResolvedConfig, Map<string, string>>()
Expand All @@ -154,7 +157,7 @@ async function fileToBuiltUrl(
pluginContext: PluginContext,
skipPublicCheck = false
): Promise<string> {
if (!skipPublicCheck && checkPublicFile(id, config.root)) {
if (!skipPublicCheck && checkPublicFile(id, config)) {
return config.base + id.slice(1)
}

Expand Down Expand Up @@ -206,7 +209,7 @@ export async function urlToBuiltUrl(
config: ResolvedConfig,
pluginContext: PluginContext
): Promise<string> {
if (checkPublicFile(url, config.root)) {
if (checkPublicFile(url, config)) {
return config.base + url.slice(1)
}
const file = url.startsWith('/')
Expand Down
6 changes: 3 additions & 3 deletions packages/vite/src/node/plugins/html.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin {
const [preHooks, postHooks] = resolveHtmlTransforms(config.plugins)
const processedHtml = new Map<string, string>()
const isExcludedUrl = (url: string) =>
isExternalUrl(url) || isDataUrl(url) || checkPublicFile(url, config.root)
isExternalUrl(url) || isDataUrl(url) || checkPublicFile(url, config)

return {
name: 'vite:build-html',
Expand Down Expand Up @@ -154,7 +154,7 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin {
const { src, isModule } = getScriptInfo(node)

const url = src && src.value && src.value.content
if (url && checkPublicFile(url, config.root)) {
if (url && checkPublicFile(url, config)) {
// referencing public dir url, prefix with base
s.overwrite(
src!.value!.loc.start.offset,
Expand Down Expand Up @@ -197,7 +197,7 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin {
} else {
assetUrls.push(p)
}
} else if (checkPublicFile(url, config.root)) {
} else if (checkPublicFile(url, config)) {
s.overwrite(
p.value.loc.start.offset,
p.value.loc.end.offset,
Expand Down
2 changes: 1 addition & 1 deletion packages/vite/src/node/plugins/importAnalysis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin {
url.startsWith('/') &&
!config.assetsInclude(cleanUrl(url)) &&
!url.endsWith('.json') &&
checkPublicFile(url, config.root)
checkPublicFile(url, config)
) {
throw new Error(
`Cannot import non-asset file ${url} which is inside /public.` +
Expand Down
2 changes: 1 addition & 1 deletion packages/vite/src/node/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,7 @@ export async function createServer(
// serve static files under /public
// this applies before the transform middleware so that these files are served
// as-is without transforms.
middlewares.use(servePublicMiddleware(path.join(root, 'public')))
middlewares.use(servePublicMiddleware(config.publicDir))

// main transform middleware
middlewares.use(transformMiddleware(server))
Expand Down
10 changes: 3 additions & 7 deletions packages/vite/src/node/server/transformRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,11 @@ export interface TransformOptions {

export async function transformRequest(
url: string,
{
config: { root, logger },
pluginContainer,
moduleGraph,
watcher
}: ViteDevServer,
{ config, pluginContainer, moduleGraph, watcher }: ViteDevServer,
options: TransformOptions = {}
): Promise<TransformResult | null> {
url = removeTimestampQuery(url)
const { root, logger } = config
const prettyUrl = isDebug ? prettifyUrl(url, root) : ''
const ssr = !!options.ssr

Expand Down Expand Up @@ -99,7 +95,7 @@ export async function transformRequest(
}
}
if (code == null) {
if (checkPublicFile(url, root)) {
if (checkPublicFile(url, config)) {
throw new Error(
`Failed to load url ${url} (resolved id: ${id}). ` +
`This file is in /public and will be copied as-is during build without ` +
Expand Down

0 comments on commit 470ceb8

Please sign in to comment.