Skip to content

Commit 3a98558

Browse files
authored
feat: import public non-asset URL (#13422)
1 parent ebb8e29 commit 3a98558

File tree

6 files changed

+57
-15
lines changed

6 files changed

+57
-15
lines changed

packages/vite/src/node/plugins/asset.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import { FS_PREFIX } from '../constants'
2929
export const assetUrlRE = /__VITE_ASSET__([a-z\d]+)__(?:\$_(.*?)__)?/g
3030

3131
const rawRE = /(?:\?|&)raw(?:&|$)/
32-
const urlRE = /(\?|&)url(?:&|$)/
32+
export const urlRE = /(\?|&)url(?:&|$)/
3333
const jsSourceMapRE = /\.[cm]?js\.map$/
3434
const unnededFinalQueryCharRE = /[?&]$/
3535

@@ -147,7 +147,7 @@ export function assetPlugin(config: ResolvedConfig): Plugin {
147147
},
148148

149149
resolveId(id) {
150-
if (!config.assetsInclude(cleanUrl(id))) {
150+
if (!config.assetsInclude(cleanUrl(id)) && !urlRE.test(id)) {
151151
return
152152
}
153153
// imports to absolute urls pointing to files in /public

packages/vite/src/node/plugins/importAnalysis.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ import {
5555
} from '../ssr/ssrExternal'
5656
import { getDepsOptimizer, optimizedDepNeedsInterop } from '../optimizer'
5757
import { ERR_CLOSED_SERVER } from '../server/pluginContainer'
58-
import { checkPublicFile } from './asset'
58+
import { checkPublicFile, urlRE } from './asset'
5959
import {
6060
ERR_OUTDATED_OPTIMIZED_DEP,
6161
throwOutdatedRequest,
@@ -507,14 +507,20 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin {
507507
// warn imports to non-asset /public files
508508
if (
509509
specifier[0] === '/' &&
510-
!config.assetsInclude(cleanUrl(specifier)) &&
511-
!specifier.endsWith('.json') &&
510+
!(
511+
config.assetsInclude(cleanUrl(specifier)) ||
512+
urlRE.test(specifier)
513+
) &&
512514
checkPublicFile(specifier, config)
513515
) {
514516
throw new Error(
515-
`Cannot import non-asset file ${specifier} which is inside /public.` +
517+
`Cannot import non-asset file ${specifier} which is inside /public. ` +
516518
`JS/CSS files inside /public are copied as-is on build and ` +
517-
`can only be referenced via <script src> or <link href> in html.`,
519+
`can only be referenced via <script src> or <link href> in html. ` +
520+
`If you want to get the URL of that file, use ${injectQuery(
521+
specifier,
522+
'url',
523+
)} instead.`,
518524
)
519525
}
520526

packages/vite/src/node/server/middlewares/transform.ts

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import {
3737
} from '../../plugins/optimizedDeps'
3838
import { ERR_CLOSED_SERVER } from '../pluginContainer'
3939
import { getDepsOptimizer } from '../../optimizer'
40+
import { urlRE } from '../../plugins/asset'
4041

4142
const debugCache = createDebugger('vite:cache')
4243

@@ -136,14 +137,22 @@ export function transformMiddleware(
136137

137138
if (isImportRequest(url)) {
138139
const rawUrl = removeImportQuery(url)
139-
140-
warning =
141-
'Assets in public cannot be imported from JavaScript.\n' +
142-
`Instead of ${colors.cyan(
143-
rawUrl,
144-
)}, put the file in the src directory, and use ${colors.cyan(
145-
rawUrl.replace(publicPath, '/src/'),
146-
)} instead.`
140+
if (urlRE.test(url)) {
141+
warning =
142+
`Assets in the public directory are served at the root path.\n` +
143+
`Instead of ${colors.cyan(rawUrl)}, use ${colors.cyan(
144+
rawUrl.replace(publicPath, '/'),
145+
)}.`
146+
} else {
147+
warning =
148+
'Assets in public directory cannot be imported from JavaScript.\n' +
149+
`If you intend to import that asset, put the file in the src directory, and use ${colors.cyan(
150+
rawUrl.replace(publicPath, '/src/'),
151+
)} instead of ${colors.cyan(rawUrl)}.\n` +
152+
`If you intend to use the URL of that asset, use ${colors.cyan(
153+
injectQuery(rawUrl.replace(publicPath, '/'), 'url'),
154+
)}.`
155+
}
147156
} else {
148157
warning =
149158
`files in the public directory are served at the root path.\n` +

playground/assets/__tests__/assets.spec.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,19 @@ describe('asset imports from js', () => {
100100
test('from /public', async () => {
101101
expect(await page.textContent('.public-import')).toMatch(iconMatch)
102102
})
103+
104+
test('from /public (json)', async () => {
105+
expect(await page.textContent('.public-json-import')).toMatch(
106+
'/foo/foo.json',
107+
)
108+
expect(await page.textContent('.public-json-import-content'))
109+
.toMatchInlineSnapshot(`
110+
"{
111+
\\"foo\\": \\"bar\\"
112+
}
113+
"
114+
`)
115+
})
103116
})
104117

105118
describe('css url() references', () => {

playground/assets/index.html

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ <h2>Asset Imports from JS</h2>
2626
<li>Relative: <code class="asset-import-relative"></code></li>
2727
<li>Absolute: <code class="asset-import-absolute"></code></li>
2828
<li>From publicDir: <code class="public-import"></code></li>
29+
<li>
30+
From publicDir (json): <code class="public-json-import"></code> Content:
31+
<code class="public-json-import-content"></code>
32+
</li>
2933
</ul>
3034

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

385+
import publicJsonUrl from '/foo.json?url'
386+
text('.public-json-import', publicJsonUrl)
387+
;(async () => {
388+
const res = await fetch(publicJsonUrl)
389+
text('.public-json-import-content', await res.text())
390+
})()
391+
381392
import svgFrag from './nested/fragment.svg'
382393
text('.svg-frag-import-path', svgFrag)
383394
document.querySelector('.svg-frag-import').src = svgFrag + '#icon-heart-view'

playground/assets/static/foo.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"foo": "bar"
3+
}

0 commit comments

Comments
 (0)