fix: allow relative public paths (#8935)

This commit is contained in:
Daniel Roe 2021-03-10 11:48:41 +00:00 committed by GitHub
parent d55864da83
commit 69b5264ef3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 60 additions and 14 deletions

View File

@ -18,7 +18,7 @@
"lodash": "^4.17.21", "lodash": "^4.17.21",
"rc9": "^1.2.0", "rc9": "^1.2.0",
"std-env": "^2.3.0", "std-env": "^2.3.0",
"ufo": "^0.6.9" "ufo": "^0.6.10"
}, },
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"

View File

@ -5,7 +5,7 @@ import defu from 'defu'
import consola from 'consola' import consola from 'consola'
import destr from 'destr' import destr from 'destr'
import { TARGETS, MODES, createRequire, guardDir, isNonEmptyString, isPureObject, isUrl, getMainModule, getPKG } from '@nuxt/utils' import { TARGETS, MODES, createRequire, guardDir, isNonEmptyString, isPureObject, isUrl, getMainModule, getPKG } from '@nuxt/utils'
import { joinURL, normalizeURL, withTrailingSlash } from 'ufo' import { isRelative, joinURL, normalizeURL, withTrailingSlash } from 'ufo'
import { defaultNuxtConfigFile, getDefaultNuxtConfig } from './config' import { defaultNuxtConfigFile, getDefaultNuxtConfig } from './config'
export function getNuxtConfig (_options) { export function getNuxtConfig (_options) {
@ -452,9 +452,11 @@ export function getNuxtConfig (_options) {
// App config (internal for nuxt2 at this stage) // App config (internal for nuxt2 at this stage)
const useCDN = isUrl(options.build.publicPath) && !options.dev const useCDN = isUrl(options.build.publicPath) && !options.dev
const isRelativePublicPath = isRelative(options.build.publicPath)
options.app = defu(options.app, { options.app = defu(options.app, {
basePath: options.router.base, basePath: options.router.base,
assetsPath: useCDN ? '/' : joinURL(options.router.base, options.build.publicPath), assetsPath: isRelativePublicPath ? options.build.publicPath : useCDN ? '/' : joinURL(options.router.base, options.build.publicPath),
cdnURL: useCDN ? options.build.publicPath : null cdnURL: useCDN ? options.build.publicPath : null
}) })
// Expose app config to $config.app // Expose app config to $config.app

View File

@ -16,7 +16,7 @@
"fs-extra": "^9.1.0", "fs-extra": "^9.1.0",
"html-minifier": "^4.0.0", "html-minifier": "^4.0.0",
"node-html-parser": "^2.2.1", "node-html-parser": "^2.2.1",
"ufo": "^0.6.9" "ufo": "^0.6.10"
}, },
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"

View File

@ -24,7 +24,7 @@
"serve-placeholder": "^1.2.3", "serve-placeholder": "^1.2.3",
"serve-static": "^1.14.1", "serve-static": "^1.14.1",
"server-destroy": "^1.0.1", "server-destroy": "^1.0.1",
"ufo": "^0.6.9" "ufo": "^0.6.10"
}, },
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"

View File

@ -23,7 +23,7 @@ export default class Server {
this.publicPath = isUrl(this.options.build.publicPath) this.publicPath = isUrl(this.options.build.publicPath)
? this.options.build._publicPath ? this.options.build._publicPath
: this.options.build.publicPath : this.options.build.publicPath.replace(/^\.+\//, '/')
// Runtime shared resources // Runtime shared resources
this.resources = {} this.resources = {}

View File

@ -19,7 +19,7 @@
"serialize-javascript": "^5.0.1", "serialize-javascript": "^5.0.1",
"signal-exit": "^3.0.3", "signal-exit": "^3.0.3",
"ua-parser-js": "^0.7.24", "ua-parser-js": "^0.7.24",
"ufo": "^0.6.9" "ufo": "^0.6.10"
}, },
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"

View File

@ -1,4 +1,4 @@
import { joinURL } from 'ufo' import { hasProtocol, joinURL } from 'ufo'
export const encodeHtml = function encodeHtml (str) { export const encodeHtml = function encodeHtml (str) {
return str.replace(/</g, '&lt;').replace(/>/g, '&gt;') return str.replace(/</g, '&lt;').replace(/>/g, '&gt;')
@ -11,7 +11,7 @@ export const isNonEmptyString = obj => Boolean(obj && isString(obj))
export const isPureObject = obj => !Array.isArray(obj) && typeof obj === 'object' export const isPureObject = obj => !Array.isArray(obj) && typeof obj === 'object'
export const isUrl = function isUrl (url) { export const isUrl = function isUrl (url) {
return ['http', '//'].some(str => url.startsWith(str)) return hasProtocol(url, true)
} }
export const urlJoin = joinURL export const urlJoin = joinURL

View File

@ -14,7 +14,7 @@
], ],
"dependencies": { "dependencies": {
"node-fetch": "^2.6.1", "node-fetch": "^2.6.1",
"ufo": "^0.6.9", "ufo": "^0.6.10",
"unfetch": "^4.2.0", "unfetch": "^4.2.0",
"vue": "^2.6.12", "vue": "^2.6.12",
"vue-client-only": "^2.0.0", "vue-client-only": "^2.0.0",

View File

@ -15,7 +15,7 @@
"fs-extra": "^9.1.0", "fs-extra": "^9.1.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"lru-cache": "^5.1.1", "lru-cache": "^5.1.1",
"ufo": "^0.6.9", "ufo": "^0.6.10",
"vue": "^2.6.12", "vue": "^2.6.12",
"vue-meta": "^2.4.0", "vue-meta": "^2.4.0",
"vue-server-renderer": "^2.6.12" "vue-server-renderer": "^2.6.12"

View File

@ -1,3 +1,4 @@
import { isRelative } from 'ufo'
import { isUrl, urlJoin, safariNoModuleFix } from '@nuxt/utils' import { isUrl, urlJoin, safariNoModuleFix } from '@nuxt/utils'
import SSRRenderer from './ssr' import SSRRenderer from './ssr'
@ -6,7 +7,7 @@ export default class ModernRenderer extends SSRRenderer {
super(serverContext) super(serverContext)
const { build: { publicPath }, router: { base } } = this.options const { build: { publicPath }, router: { base } } = this.options
this.publicPath = isUrl(publicPath) ? publicPath : urlJoin(base, publicPath) this.publicPath = isUrl(publicPath) || isRelative(publicPath) ? publicPath : urlJoin(base, publicPath)
} }
get assetsMapping () { get assetsMapping () {

View File

@ -42,6 +42,7 @@
"terser-webpack-plugin": "^4.2.3", "terser-webpack-plugin": "^4.2.3",
"thread-loader": "^3.0.1", "thread-loader": "^3.0.1",
"time-fix-plugin": "^2.0.7", "time-fix-plugin": "^2.0.7",
"ufo": "^0.6.10",
"url-loader": "^4.1.1", "url-loader": "^4.1.1",
"vue-loader": "^15.9.6", "vue-loader": "^15.9.6",
"vue-style-loader": "^4.1.3", "vue-style-loader": "^4.1.3",

View File

@ -10,6 +10,7 @@ import TerserWebpackPlugin from 'terser-webpack-plugin'
import WebpackBar from 'webpackbar' import WebpackBar from 'webpackbar'
import env from 'std-env' import env from 'std-env'
import semver from 'semver' import semver from 'semver'
import { isRelative } from 'ufo'
import { TARGETS, isUrl, urlJoin, getPKG, tryResolve, requireModule, resolveModule } from '@nuxt/utils' import { TARGETS, isUrl, urlJoin, getPKG, tryResolve, requireModule, resolveModule } from '@nuxt/utils'
@ -208,7 +209,7 @@ export default class WebpackBaseConfig {
filename: this.getFileName('app'), filename: this.getFileName('app'),
futureEmitAssets: true, // TODO: Remove when using webpack 5 futureEmitAssets: true, // TODO: Remove when using webpack 5
chunkFilename: this.getFileName('chunk'), chunkFilename: this.getFileName('chunk'),
publicPath: isUrl(publicPath) ? publicPath : urlJoin(router.base, publicPath) publicPath: isUrl(publicPath) ? publicPath : isRelative(publicPath) ? publicPath.replace(/^\.+\//, '/') : urlJoin(router.base, publicPath)
} }
} }

View File

@ -0,0 +1,41 @@
import { loadFixture, getPort, Nuxt, rp } from '../utils'
let port
let nuxt
const url = route => 'http://localhost:' + port + route
const tests = [
['relative publicPath can be used in dev ssr', {
build: {
publicPath: './_nuxt/'
}
}],
['relative publicPath can be used in production ssr', {
dev: false,
build: {
publicPath: './_nuxt/'
}
}]
]
describe('basic ssr with relative path', () => {
tests.forEach(([name, options]) => test(name, async () => {
const config = await loadFixture('basic', options)
nuxt = new Nuxt(config)
await nuxt.ready()
port = await getPort()
await nuxt.server.listen(port, 'localhost')
const { html } = await nuxt.server.renderRoute('/')
expect(html).toContain('<img src="./_nuxt/img')
const { 1: imageSrc } = html.match(/<img src="\.(\/_nuxt\/img[^"]*)"/)
const { statusCode } = await rp(url(imageSrc))
expect(statusCode).toBe(200)
await nuxt.close()
}
))
})

View File

@ -12792,7 +12792,7 @@ ua-parser-js@^0.7.24:
resolved "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.24.tgz#8d3ecea46ed4f1f1d63ec25f17d8568105dc027c" resolved "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.24.tgz#8d3ecea46ed4f1f1d63ec25f17d8568105dc027c"
integrity sha512-yo+miGzQx5gakzVK3QFfN0/L9uVhosXBBO7qmnk7c2iw1IhL212wfA3zbnI54B0obGwC/5NWub/iT9sReMx+Fw== integrity sha512-yo+miGzQx5gakzVK3QFfN0/L9uVhosXBBO7qmnk7c2iw1IhL212wfA3zbnI54B0obGwC/5NWub/iT9sReMx+Fw==
ufo@^0.6.9: ufo@^0.6.10:
version "0.6.10" version "0.6.10"
resolved "https://registry.npmjs.org/ufo/-/ufo-0.6.10.tgz#c7ace9b8f72cb08c35e3a8c8edc76f062fbaa7d0" resolved "https://registry.npmjs.org/ufo/-/ufo-0.6.10.tgz#c7ace9b8f72cb08c35e3a8c8edc76f062fbaa7d0"
integrity sha512-sMbJnrBcKKsbVyr6++hb0n9lCmrMqkJrNnJIOJ3sckeqY6NMfAULcRGbBWcASSnN1HDV3YqiGCPzi9RVs511bw== integrity sha512-sMbJnrBcKKsbVyr6++hb0n9lCmrMqkJrNnJIOJ3sckeqY6NMfAULcRGbBWcASSnN1HDV3YqiGCPzi9RVs511bw==