Skip to content

Commit

Permalink
feat: import public non-asset URL (#13422)
Browse files Browse the repository at this point in the history
  • Loading branch information
sapphi-red committed Jun 9, 2023
1 parent ebb8e29 commit 3a98558
Show file tree
Hide file tree
Showing 6 changed files with 57 additions and 15 deletions.
4 changes: 2 additions & 2 deletions packages/vite/src/node/plugins/asset.ts
Expand Up @@ -29,7 +29,7 @@ import { FS_PREFIX } from '../constants'
export const assetUrlRE = /__VITE_ASSET__([a-z\d]+)__(?:\$_(.*?)__)?/g

const rawRE = /(?:\?|&)raw(?:&|$)/
const urlRE = /(\?|&)url(?:&|$)/
export const urlRE = /(\?|&)url(?:&|$)/
const jsSourceMapRE = /\.[cm]?js\.map$/
const unnededFinalQueryCharRE = /[?&]$/

Expand Down Expand Up @@ -147,7 +147,7 @@ export function assetPlugin(config: ResolvedConfig): Plugin {
},

resolveId(id) {
if (!config.assetsInclude(cleanUrl(id))) {
if (!config.assetsInclude(cleanUrl(id)) && !urlRE.test(id)) {
return
}
// imports to absolute urls pointing to files in /public
Expand Down
16 changes: 11 additions & 5 deletions packages/vite/src/node/plugins/importAnalysis.ts
Expand Up @@ -55,7 +55,7 @@ import {
} from '../ssr/ssrExternal'
import { getDepsOptimizer, optimizedDepNeedsInterop } from '../optimizer'
import { ERR_CLOSED_SERVER } from '../server/pluginContainer'
import { checkPublicFile } from './asset'
import { checkPublicFile, urlRE } from './asset'
import {
ERR_OUTDATED_OPTIMIZED_DEP,
throwOutdatedRequest,
Expand Down Expand Up @@ -507,14 +507,20 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin {
// warn imports to non-asset /public files
if (
specifier[0] === '/' &&
!config.assetsInclude(cleanUrl(specifier)) &&
!specifier.endsWith('.json') &&
!(
config.assetsInclude(cleanUrl(specifier)) ||
urlRE.test(specifier)
) &&
checkPublicFile(specifier, config)
) {
throw new Error(
`Cannot import non-asset file ${specifier} which is inside /public.` +
`Cannot import non-asset file ${specifier} which is inside /public. ` +
`JS/CSS files inside /public are copied as-is on build and ` +
`can only be referenced via <script src> or <link href> in html.`,
`can only be referenced via <script src> or <link href> in html. ` +
`If you want to get the URL of that file, use ${injectQuery(
specifier,
'url',
)} instead.`,
)
}

Expand Down
25 changes: 17 additions & 8 deletions packages/vite/src/node/server/middlewares/transform.ts
Expand Up @@ -37,6 +37,7 @@ import {
} from '../../plugins/optimizedDeps'
import { ERR_CLOSED_SERVER } from '../pluginContainer'
import { getDepsOptimizer } from '../../optimizer'
import { urlRE } from '../../plugins/asset'

const debugCache = createDebugger('vite:cache')

Expand Down Expand Up @@ -136,14 +137,22 @@ export function transformMiddleware(

if (isImportRequest(url)) {
const rawUrl = removeImportQuery(url)

warning =
'Assets in public cannot be imported from JavaScript.\n' +
`Instead of ${colors.cyan(
rawUrl,
)}, put the file in the src directory, and use ${colors.cyan(
rawUrl.replace(publicPath, '/src/'),
)} instead.`
if (urlRE.test(url)) {
warning =
`Assets in the public directory are served at the root path.\n` +
`Instead of ${colors.cyan(rawUrl)}, use ${colors.cyan(
rawUrl.replace(publicPath, '/'),
)}.`
} else {
warning =
'Assets in public directory cannot be imported from JavaScript.\n' +
`If you intend to import that asset, put the file in the src directory, and use ${colors.cyan(
rawUrl.replace(publicPath, '/src/'),
)} instead of ${colors.cyan(rawUrl)}.\n` +
`If you intend to use the URL of that asset, use ${colors.cyan(
injectQuery(rawUrl.replace(publicPath, '/'), 'url'),
)}.`
}
} else {
warning =
`files in the public directory are served at the root path.\n` +
Expand Down
13 changes: 13 additions & 0 deletions playground/assets/__tests__/assets.spec.ts
Expand Up @@ -100,6 +100,19 @@ describe('asset imports from js', () => {
test('from /public', async () => {
expect(await page.textContent('.public-import')).toMatch(iconMatch)
})

test('from /public (json)', async () => {
expect(await page.textContent('.public-json-import')).toMatch(
'/foo/foo.json',
)
expect(await page.textContent('.public-json-import-content'))
.toMatchInlineSnapshot(`
"{
\\"foo\\": \\"bar\\"
}
"
`)
})
})

describe('css url() references', () => {
Expand Down
11 changes: 11 additions & 0 deletions playground/assets/index.html
Expand Up @@ -26,6 +26,10 @@ <h2>Asset Imports from JS</h2>
<li>Relative: <code class="asset-import-relative"></code></li>
<li>Absolute: <code class="asset-import-absolute"></code></li>
<li>From publicDir: <code class="public-import"></code></li>
<li>
From publicDir (json): <code class="public-json-import"></code> Content:
<code class="public-json-import-content"></code>
</li>
</ul>

<h2>CSS url references</h2>
Expand Down Expand Up @@ -378,6 +382,13 @@ <h3>assets in noscript</h3>
import publicUrl from '/icon.png'
text('.public-import', publicUrl)

import publicJsonUrl from '/foo.json?url'
text('.public-json-import', publicJsonUrl)
;(async () => {
const res = await fetch(publicJsonUrl)
text('.public-json-import-content', await res.text())
})()

import svgFrag from './nested/fragment.svg'
text('.svg-frag-import-path', svgFrag)
document.querySelector('.svg-frag-import').src = svgFrag + '#icon-heart-view'
Expand Down
3 changes: 3 additions & 0 deletions playground/assets/static/foo.json
@@ -0,0 +1,3 @@
{
"foo": "bar"
}

0 comments on commit 3a98558

Please sign in to comment.