From 69b5264ef3a30cde093a0b5b3acb9750d2f3f84a Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Wed, 10 Mar 2021 11:48:41 +0000 Subject: [PATCH] fix: allow relative public paths (#8935) --- packages/config/package.json | 2 +- packages/config/src/options.js | 6 ++- packages/generator/package.json | 2 +- packages/server/package.json | 2 +- packages/server/src/server.js | 2 +- packages/utils/package.json | 2 +- packages/utils/src/lang.js | 4 +- packages/vue-app/package.json | 2 +- packages/vue-renderer/package.json | 2 +- packages/vue-renderer/src/renderers/modern.js | 3 +- packages/webpack/package.json | 1 + packages/webpack/src/config/base.js | 3 +- test/dev/basic.relative.test.js | 41 +++++++++++++++++++ yarn.lock | 2 +- 14 files changed, 60 insertions(+), 14 deletions(-) create mode 100644 test/dev/basic.relative.test.js diff --git a/packages/config/package.json b/packages/config/package.json index 452e033d86..a9d53fd812 100644 --- a/packages/config/package.json +++ b/packages/config/package.json @@ -18,7 +18,7 @@ "lodash": "^4.17.21", "rc9": "^1.2.0", "std-env": "^2.3.0", - "ufo": "^0.6.9" + "ufo": "^0.6.10" }, "publishConfig": { "access": "public" diff --git a/packages/config/src/options.js b/packages/config/src/options.js index 58a0981f80..234308ec9a 100644 --- a/packages/config/src/options.js +++ b/packages/config/src/options.js @@ -5,7 +5,7 @@ import defu from 'defu' import consola from 'consola' import destr from 'destr' 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' export function getNuxtConfig (_options) { @@ -452,9 +452,11 @@ export function getNuxtConfig (_options) { // App config (internal for nuxt2 at this stage) const useCDN = isUrl(options.build.publicPath) && !options.dev + const isRelativePublicPath = isRelative(options.build.publicPath) + options.app = defu(options.app, { 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 }) // Expose app config to $config.app diff --git a/packages/generator/package.json b/packages/generator/package.json index 43212833e9..8cab1110c6 100644 --- a/packages/generator/package.json +++ b/packages/generator/package.json @@ -16,7 +16,7 @@ "fs-extra": "^9.1.0", "html-minifier": "^4.0.0", "node-html-parser": "^2.2.1", - "ufo": "^0.6.9" + "ufo": "^0.6.10" }, "publishConfig": { "access": "public" diff --git a/packages/server/package.json b/packages/server/package.json index dfd04c1c31..88806d0039 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -24,7 +24,7 @@ "serve-placeholder": "^1.2.3", "serve-static": "^1.14.1", "server-destroy": "^1.0.1", - "ufo": "^0.6.9" + "ufo": "^0.6.10" }, "publishConfig": { "access": "public" diff --git a/packages/server/src/server.js b/packages/server/src/server.js index 7553e0b602..70c734dd25 100644 --- a/packages/server/src/server.js +++ b/packages/server/src/server.js @@ -23,7 +23,7 @@ export default class Server { this.publicPath = isUrl(this.options.build.publicPath) ? this.options.build._publicPath - : this.options.build.publicPath + : this.options.build.publicPath.replace(/^\.+\//, '/') // Runtime shared resources this.resources = {} diff --git a/packages/utils/package.json b/packages/utils/package.json index df5e640912..14a55816cd 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -19,7 +19,7 @@ "serialize-javascript": "^5.0.1", "signal-exit": "^3.0.3", "ua-parser-js": "^0.7.24", - "ufo": "^0.6.9" + "ufo": "^0.6.10" }, "publishConfig": { "access": "public" diff --git a/packages/utils/src/lang.js b/packages/utils/src/lang.js index ac9c3931c9..e0cdd6275f 100644 --- a/packages/utils/src/lang.js +++ b/packages/utils/src/lang.js @@ -1,4 +1,4 @@ -import { joinURL } from 'ufo' +import { hasProtocol, joinURL } from 'ufo' export const encodeHtml = function encodeHtml (str) { return str.replace(//g, '>') @@ -11,7 +11,7 @@ export const isNonEmptyString = obj => Boolean(obj && isString(obj)) export const isPureObject = obj => !Array.isArray(obj) && typeof obj === 'object' export const isUrl = function isUrl (url) { - return ['http', '//'].some(str => url.startsWith(str)) + return hasProtocol(url, true) } export const urlJoin = joinURL diff --git a/packages/vue-app/package.json b/packages/vue-app/package.json index 44ab428896..06176bfea4 100644 --- a/packages/vue-app/package.json +++ b/packages/vue-app/package.json @@ -14,7 +14,7 @@ ], "dependencies": { "node-fetch": "^2.6.1", - "ufo": "^0.6.9", + "ufo": "^0.6.10", "unfetch": "^4.2.0", "vue": "^2.6.12", "vue-client-only": "^2.0.0", diff --git a/packages/vue-renderer/package.json b/packages/vue-renderer/package.json index ca203ac9a1..875d903b76 100644 --- a/packages/vue-renderer/package.json +++ b/packages/vue-renderer/package.json @@ -15,7 +15,7 @@ "fs-extra": "^9.1.0", "lodash": "^4.17.21", "lru-cache": "^5.1.1", - "ufo": "^0.6.9", + "ufo": "^0.6.10", "vue": "^2.6.12", "vue-meta": "^2.4.0", "vue-server-renderer": "^2.6.12" diff --git a/packages/vue-renderer/src/renderers/modern.js b/packages/vue-renderer/src/renderers/modern.js index 71c06dd715..4daa9812f2 100644 --- a/packages/vue-renderer/src/renderers/modern.js +++ b/packages/vue-renderer/src/renderers/modern.js @@ -1,3 +1,4 @@ +import { isRelative } from 'ufo' import { isUrl, urlJoin, safariNoModuleFix } from '@nuxt/utils' import SSRRenderer from './ssr' @@ -6,7 +7,7 @@ export default class ModernRenderer extends SSRRenderer { super(serverContext) 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 () { diff --git a/packages/webpack/package.json b/packages/webpack/package.json index faf60f33f1..cbc04a2a13 100644 --- a/packages/webpack/package.json +++ b/packages/webpack/package.json @@ -42,6 +42,7 @@ "terser-webpack-plugin": "^4.2.3", "thread-loader": "^3.0.1", "time-fix-plugin": "^2.0.7", + "ufo": "^0.6.10", "url-loader": "^4.1.1", "vue-loader": "^15.9.6", "vue-style-loader": "^4.1.3", diff --git a/packages/webpack/src/config/base.js b/packages/webpack/src/config/base.js index 708eeda411..eedf61600a 100644 --- a/packages/webpack/src/config/base.js +++ b/packages/webpack/src/config/base.js @@ -10,6 +10,7 @@ import TerserWebpackPlugin from 'terser-webpack-plugin' import WebpackBar from 'webpackbar' import env from 'std-env' import semver from 'semver' +import { isRelative } from 'ufo' import { TARGETS, isUrl, urlJoin, getPKG, tryResolve, requireModule, resolveModule } from '@nuxt/utils' @@ -208,7 +209,7 @@ export default class WebpackBaseConfig { filename: this.getFileName('app'), futureEmitAssets: true, // TODO: Remove when using webpack 5 chunkFilename: this.getFileName('chunk'), - publicPath: isUrl(publicPath) ? publicPath : urlJoin(router.base, publicPath) + publicPath: isUrl(publicPath) ? publicPath : isRelative(publicPath) ? publicPath.replace(/^\.+\//, '/') : urlJoin(router.base, publicPath) } } diff --git a/test/dev/basic.relative.test.js b/test/dev/basic.relative.test.js new file mode 100644 index 0000000000..92ea889147 --- /dev/null +++ b/test/dev/basic.relative.test.js @@ -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('