Repository: nuxt/nuxt.js (ts)

Issue:
Allow specific names in publicRuntimeConfig
Hi and thanks for your work on Nuxt.

**Request**

As I mentioned in the official Nuxt `Discord`, can you please consider allowing users to use words/names such as `app` in project configuration?

It's short, strong meaning and definitely one of the most common for a configuration within a project. I think this approach that doesn't allow the use of such common names in the project configuration can be very confusing and problematic for debugging.

**Code example**

This worked fine until version `2.15.0`, so it's kind of a `breaking-change`:

```js
// nuxt.config.js

export default {
  publicRuntimeConfig: {
    // Doesn't work after version '2.14.12'
    app: {
      title: 'New Title',
      titleSeparator: '-',
      titleTemplate: 'Title Template',
      description: 'New Description',
    },
  },
}
```

**Suggestion**

As Daniel said in Discord chat:

> Nuxt is using the `app` namespace for internal dynamic config.

It might make more sense to use alternatives under the hood (in Nuxt core such as `_app` or `__app`) and allow users to use the normal versions (such as `app`).

**Other examples**

This is allowed in `Next.js`.


Pull request description:
refactor: move internal app runtime config to `_app` namespace
## Types of changes
- [x] Bug fix (a non-breaking change which fixes an issue)

## Description
move internal runtime config to `_app`  from `app` - and add notification that this is reserved via TS types/IDE autocomplete

History: #8759, #9039

closes #9074

## Checklist:
- [x] All new and existing tests are passing.



Patch:
diff --git a/packages/config/src/options.js b/packages/config/src/options.js
index 8e4ab707e..18c1c2e0f 100644
--- a/packages/config/src/options.js
+++ b/packages/config/src/options.js
@@ -459,9 +459,9 @@ export function getNuxtConfig (_options) {
     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
+  // Expose app config to $config._app
   options.publicRuntimeConfig = options.publicRuntimeConfig || {}
-  options.publicRuntimeConfig.app = defu(options.publicRuntimeConfig.app, options.app)
+  options.publicRuntimeConfig._app = options.app
 
   // Generate staticAssets
   const { staticAssets } = options.generate
diff --git a/packages/types/config/runtime.d.ts b/packages/types/config/runtime.d.ts
index cc9c373c8..1b79156eb 100644
--- a/packages/types/config/runtime.d.ts
+++ b/packages/types/config/runtime.d.ts
@@ -4,7 +4,12 @@
 */
 
 export interface NuxtRuntimeConfig {
-  [key: string]: any
+  [key: string]: any;
+  /**
+   * This is used internally by Nuxt for dynamic configuration and should not be used.
+   * @internal
+   */
+  _app?: never;
 }
 
 export type NuxtOptionsRuntimeConfig = NuxtRuntimeConfig | ((env: typeof process.env) => NuxtRuntimeConfig)
diff --git a/packages/vue-app/template/client.js b/packages/vue-app/template/client.js
index 7c80fff8d..5045862cb 100644
--- a/packages/vue-app/template/client.js
+++ b/packages/vue-app/template/client.js
@@ -49,8 +49,8 @@ let router
 const NUXT = window.<%= globals.context %> || {}
 
 const $config = NUXT.config || {}
-if ($config.app) {
-  __webpack_public_path__ = urlJoin($config.app.cdnURL, $config.app.assetsPath)
+if ($config._app) {
+  __webpack_public_path__ = urlJoin($config._app.cdnURL, $config._app.assetsPath)
 }
 
 Object.assign(Vue.config, <%= serialize(vue.config) %>)<%= isTest ? '// eslint-disable-line' : '' %>
diff --git a/packages/vue-app/template/router.js b/packages/vue-app/template/router.js
index e8d75e2a1..6c3d6a50f 100644
--- a/packages/vue-app/template/router.js
+++ b/packages/vue-app/template/router.js
@@ -102,7 +102,7 @@ export const routerOptions = {
 }
 
 export function createRouter (ssrContext, config) {
-  const base = (config.app && config.app.basePath) || routerOptions.base
+  const base = (config._app && config._app.basePath) || routerOptions.base
   const router = new Router({ ...routerOptions, base  })
 
   // TODO: remove in Nuxt 3
diff --git a/packages/vue-app/template/server.js b/packages/vue-app/template/server.js
index 2fb81a668..1d62d425d 100644
--- a/packages/vue-app/template/server.js
+++ b/packages/vue-app/template/server.js
@@ -59,7 +59,7 @@ const createNext = ssrContext => (opts) => {
   }
   let fullPath = withQuery(opts.path, opts.query)
   const $config = ssrContext.runtimeConfig || {}
-  const routerBase = ($config.app && $config.app.basePath) || '<%= router.base %>'
+  const routerBase = ($config._app && $config._app.basePath) || '<%= router.base %>'
   if (!fullPath.startsWith('http') && (routerBase !== '/' && !fullPath.startsWith(routerBase))) {
     fullPath = joinURL(routerBase, fullPath)
   }
@@ -99,8 +99,8 @@ export default async (ssrContext) => {
   <% } %>
   // Public runtime config
   ssrContext.nuxt.config = ssrContext.runtimeConfig.public
-  if (ssrContext.nuxt.config.app) {
-    __webpack_public_path__ = joinURL(ssrContext.nuxt.config.app.cdnURL, ssrContext.nuxt.config.app.assetsPath)
+  if (ssrContext.nuxt.config._app) {
+    __webpack_public_path__ = joinURL(ssrContext.nuxt.config._app.cdnURL, ssrContext.nuxt.config._app.assetsPath)
   }
   // Create the app definition and the instance (created for each request)
   const { app, router<%= (store ? ', store' : '') %> } = await createApp(ssrContext, ssrContext.runtimeConfig.private)


Test patch:
diff --git a/packages/config/test/__snapshots__/options.test.js.snap b/packages/config/test/__snapshots__/options.test.js.snap
index 452047285..44e21c60e 100644
--- a/packages/config/test/__snapshots__/options.test.js.snap
+++ b/packages/config/test/__snapshots__/options.test.js.snap
@@ -323,7 +323,7 @@ Object {
   "plugins": Array [],
   "privateRuntimeConfig": Object {},
   "publicRuntimeConfig": Object {
-    "app": Object {
+    "_app": Object {
       "assetsPath": "/_nuxt/",
       "basePath": "/",
       "cdnURL": null,
diff --git a/test/dev/basic.dynamic.test.js b/test/dev/basic.dynamic.test.js
index c05803adf..a3102a459 100644
--- a/test/dev/basic.dynamic.test.js
+++ b/test/dev/basic.dynamic.test.js
@@ -55,9 +55,9 @@ describe('basic ssr', () => {
 
     expect(window.document.body.innerHTML).toContain('<h1>Index page</h1>')
 
-    expect(window.__NUXT__.config.app.basePath).toBe('/path/')
-    expect(window.__NUXT__.config.app.cdnURL).toBe('https://cdn.nuxtjs.org/')
-    expect(window.__NUXT__.config.app.assetsPath).toBe('/')
+    expect(window.__NUXT__.config._app.basePath).toBe('/path/')
+    expect(window.__NUXT__.config._app.cdnURL).toBe('https://cdn.nuxtjs.org/')
+    expect(window.__NUXT__.config._app.assetsPath).toBe('/')
 
     expect(fetchCount).toBeGreaterThan(0)
   })