mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-29 17:07:22 +00:00
refactor: move app to src with nuxt/app import
this refactor allows distributing app with esm modules instead of ts
This commit is contained in:
parent
a6f9fb4c7a
commit
454b8c332c
27
packages/nuxt3/src/app/_templates/entry.client.ts
Normal file
27
packages/nuxt3/src/app/_templates/entry.client.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { createSSRApp, nextTick } from 'vue'
|
||||||
|
import { createNuxt, applyPlugins } from 'nuxt/app/nuxt'
|
||||||
|
import plugins from './plugins'
|
||||||
|
import clientPlugins from './plugins.client'
|
||||||
|
import App from '<%= app.main %>'
|
||||||
|
|
||||||
|
async function initApp () {
|
||||||
|
const app = createSSRApp(App)
|
||||||
|
|
||||||
|
const nuxt = createNuxt({ app })
|
||||||
|
|
||||||
|
await applyPlugins(nuxt, plugins)
|
||||||
|
await applyPlugins(nuxt, clientPlugins)
|
||||||
|
|
||||||
|
await app.$nuxt.hooks.callHook('app:created', app)
|
||||||
|
await app.$nuxt.hooks.callHook('app:beforeMount', app)
|
||||||
|
|
||||||
|
app.mount('#<%= globals.id %>')
|
||||||
|
|
||||||
|
await app.$nuxt.hooks.callHook('app:mounted', app)
|
||||||
|
await nextTick()
|
||||||
|
nuxt.isHydrating = false
|
||||||
|
}
|
||||||
|
|
||||||
|
initApp().catch((error) => {
|
||||||
|
console.error('Error while mounting app:', error) // eslint-disable-line no-console
|
||||||
|
})
|
23
packages/nuxt3/src/app/_templates/entry.server.ts
Normal file
23
packages/nuxt3/src/app/_templates/entry.server.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { createApp } from 'vue'
|
||||||
|
|
||||||
|
import { createNuxt, applyPlugins } from 'nuxt/app/nuxt'
|
||||||
|
import plugins from './plugins'
|
||||||
|
import serverPlugins from './plugins.server'
|
||||||
|
import App from '<%= app.main %>'
|
||||||
|
|
||||||
|
export default async function createNuxtAppServer (ssrContext = {}) {
|
||||||
|
const app = createApp(App)
|
||||||
|
|
||||||
|
const nuxt = createNuxt({ app, ssrContext })
|
||||||
|
|
||||||
|
await applyPlugins(nuxt, plugins)
|
||||||
|
await applyPlugins(nuxt, serverPlugins)
|
||||||
|
|
||||||
|
await app.$nuxt.hooks.callHook('app:created', app)
|
||||||
|
|
||||||
|
nuxt.hooks.hook('vue-renderer:done',
|
||||||
|
() => nuxt.hooks.callHook('app:rendered', app)
|
||||||
|
)
|
||||||
|
|
||||||
|
return app
|
||||||
|
}
|
3
packages/nuxt3/src/app/_templates/layouts/default.vue
Normal file
3
packages/nuxt3/src/app/_templates/layouts/default.vue
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<template>
|
||||||
|
<Nuxt />
|
||||||
|
</template>
|
12
packages/nuxt3/src/app/_templates/plugins.client.ts
Normal file
12
packages/nuxt3/src/app/_templates/plugins.client.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import logs from 'nuxt/app/plugins/logs.client.dev'
|
||||||
|
import progress from 'nuxt/app/plugins/progress.client'
|
||||||
|
|
||||||
|
const plugins = [
|
||||||
|
progress
|
||||||
|
]
|
||||||
|
|
||||||
|
if (process.dev) {
|
||||||
|
plugins.push(logs)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default plugins
|
5
packages/nuxt3/src/app/_templates/plugins.server.ts
Normal file
5
packages/nuxt3/src/app/_templates/plugins.server.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import preload from 'nuxt/app/plugins/preload.server'
|
||||||
|
|
||||||
|
export default [
|
||||||
|
preload
|
||||||
|
]
|
9
packages/nuxt3/src/app/_templates/plugins.ts
Normal file
9
packages/nuxt3/src/app/_templates/plugins.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import router from 'nuxt/app/plugins/router'
|
||||||
|
import vuex from 'nuxt/app/plugins/vuex'
|
||||||
|
import legacy from 'nuxt/app/plugins/legacy'
|
||||||
|
|
||||||
|
export default [
|
||||||
|
router,
|
||||||
|
vuex,
|
||||||
|
legacy
|
||||||
|
]
|
2
packages/nuxt3/src/app/_templates/routes.js
Normal file
2
packages/nuxt3/src/app/_templates/routes.js
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
// TODO: Use webpack-virtual-modules
|
||||||
|
export default <%= app.templates.routes %>
|
@ -0,0 +1,9 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html {{ HTML_ATTRS }}>
|
||||||
|
<head {{ HEAD_ATTRS }}>
|
||||||
|
{{ HEAD }}
|
||||||
|
</head>
|
||||||
|
<body {{ BODY_ATTRS }}>
|
||||||
|
<div id="__nuxt">{{ APP }}</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
23
packages/nuxt3/src/app/_templates/views/error.html
Normal file
23
packages/nuxt3/src/app/_templates/views/error.html
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Server error</title>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no" name=viewport>
|
||||||
|
<style>
|
||||||
|
.__nuxt-error-page{padding: 1rem;background:#f7f8fb;color:#47494e;text-align:center;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;font-family:sans-serif;font-weight:100!important;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;-webkit-font-smoothing:antialiased;position:absolute;top:0;left:0;right:0;bottom:0}.__nuxt-error-page .error{max-width:450px}.__nuxt-error-page .title{font-size:24px;font-size:1.5rem;margin-top:15px;color:#47494e;margin-bottom:8px}.__nuxt-error-page .description{color:#7f828b;line-height:21px;margin-bottom:10px}.__nuxt-error-page a{color:#7f828b!important;text-decoration:none}.__nuxt-error-page .logo{position:fixed;left:12px;bottom:12px}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="__nuxt-error-page">
|
||||||
|
<div class="error">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="90" height="90" fill="#DBE1EC" viewBox="0 0 48 48"><path d="M22 30h4v4h-4zm0-16h4v12h-4zm1.99-10C12.94 4 4 12.95 4 24s8.94 20 19.99 20S44 35.05 44 24 35.04 4 23.99 4zM24 40c-8.84 0-16-7.16-16-16S15.16 8 24 8s16 7.16 16 16-7.16 16-16 16z"/></svg>
|
||||||
|
<div class="title">Server error</div>
|
||||||
|
<div class="description">{{ message }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="logo">
|
||||||
|
<a href="https://nuxtjs.org" target="_blank" rel="noopener">Nuxt.js</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
3
packages/nuxt3/src/app/app.pages.vue
Normal file
3
packages/nuxt3/src/app/app.pages.vue
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<template>
|
||||||
|
<router-view />
|
||||||
|
</template>
|
6
packages/nuxt3/src/app/app.tutorial.vue
Normal file
6
packages/nuxt3/src/app/app.tutorial.vue
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<template>
|
||||||
|
<div class="welcome">
|
||||||
|
<h1>Welcome to Nuxt 3</h1>
|
||||||
|
<p>You can start adding a <code>pages/index.vue</code> or <code>app.vue</code></p>
|
||||||
|
</div>
|
||||||
|
</template>
|
38
packages/nuxt3/src/app/composables/data.ts
Normal file
38
packages/nuxt3/src/app/composables/data.ts
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import { getCurrentInstance, reactive, isReactive } from 'vue'
|
||||||
|
import { useNuxt } from 'nuxt/app'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a unique string suitable for syncing data between server and client.
|
||||||
|
* @param nuxt (optional) A Nuxt instance
|
||||||
|
* @param vm (optional) A Vue component - by default it will use the current instance
|
||||||
|
*/
|
||||||
|
export function useSSRRef (nuxt = useNuxt(), vm = getCurrentInstance()): string {
|
||||||
|
if (process.server) {
|
||||||
|
if (!vm.attrs['data-ssr-ref']) {
|
||||||
|
nuxt._refCtr = nuxt._refCtr || 1
|
||||||
|
vm.attrs['data-ssr-ref'] = String(nuxt._refCtr++)
|
||||||
|
}
|
||||||
|
return vm.attrs['data-ssr-ref'] as string
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.client) {
|
||||||
|
return vm.vnode.el?.dataset?.ssrRef || String(Math.random()) /* TODO: unique value for multiple calls */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows accessing reactive data that can be synced between server and client.
|
||||||
|
* @param nuxt (optional) A Nuxt instance
|
||||||
|
* @param vm (optional) A Vue component - by default it will use the current instance
|
||||||
|
*/
|
||||||
|
export function useData (nuxt = useNuxt(), vm = getCurrentInstance()): ReturnType<typeof reactive> {
|
||||||
|
const ssrRef = useSSRRef(nuxt, vm)
|
||||||
|
|
||||||
|
nuxt.payload.data = nuxt.payload.data || {}
|
||||||
|
|
||||||
|
if (!isReactive(nuxt.payload.data[ssrRef])) {
|
||||||
|
nuxt.payload.data[ssrRef] = reactive(nuxt.payload.data[ssrRef] || {})
|
||||||
|
}
|
||||||
|
|
||||||
|
return nuxt.payload.data[ssrRef]
|
||||||
|
}
|
105
packages/nuxt3/src/app/composables/fetch.ts
Normal file
105
packages/nuxt3/src/app/composables/fetch.ts
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
import { Ref, toRef, onMounted, watch, getCurrentInstance, onUnmounted } from 'vue'
|
||||||
|
import { Nuxt, useNuxt } from 'nuxt/app'
|
||||||
|
import { httpFetch } from '../utils/fetch'
|
||||||
|
import { useData } from './data'
|
||||||
|
|
||||||
|
export type HTTPRequest = string | { method: string, url: string }
|
||||||
|
export type FetchRequest<T> = HTTPRequest | ((ctx: Nuxt) => HTTPRequest | Promise<T>)
|
||||||
|
|
||||||
|
export interface FetchOptions {
|
||||||
|
server?: boolean
|
||||||
|
defer?: boolean
|
||||||
|
fetcher?: Function
|
||||||
|
key?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FetchObj<T> {
|
||||||
|
data: Ref<T>
|
||||||
|
fetch: Function
|
||||||
|
error?: any
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useFetch (defaults?: FetchOptions) {
|
||||||
|
const nuxt = useNuxt()
|
||||||
|
const vm = getCurrentInstance()
|
||||||
|
|
||||||
|
let data = useData(nuxt, vm)
|
||||||
|
let dataRef = 1
|
||||||
|
|
||||||
|
const onMountedCbs = []
|
||||||
|
|
||||||
|
if (process.client) {
|
||||||
|
onMounted(() => {
|
||||||
|
onMountedCbs.forEach((cb) => { cb() })
|
||||||
|
onMountedCbs.splice(0, onMountedCbs.length)
|
||||||
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
onMountedCbs.splice(0, onMountedCbs.length)
|
||||||
|
data = null
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return async function fetch<T = any> (request: FetchRequest<T>, options?: FetchOptions): Promise<FetchObj<T>> {
|
||||||
|
options = {
|
||||||
|
server: true,
|
||||||
|
defer: false,
|
||||||
|
fetcher: httpFetch,
|
||||||
|
...defaults,
|
||||||
|
...options
|
||||||
|
}
|
||||||
|
|
||||||
|
const key = String(dataRef++)
|
||||||
|
|
||||||
|
const fetch = async () => {
|
||||||
|
const _request = typeof request === 'function'
|
||||||
|
? request(nuxt)
|
||||||
|
: request
|
||||||
|
|
||||||
|
if (_request instanceof Promise) {
|
||||||
|
// Let user resolve if request is promise
|
||||||
|
data[key] = await _request
|
||||||
|
} else if (_request && (typeof _request === 'string' || _request.url)) {
|
||||||
|
// Make HTTP request when request is string (url) or { url, ...opts }
|
||||||
|
data[key] = await options.fetcher(_request)
|
||||||
|
} else {
|
||||||
|
// Invalid request
|
||||||
|
throw new Error('Invalid fetch request: ' + _request)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const clientOnly = options.server === false
|
||||||
|
|
||||||
|
// Client side
|
||||||
|
if (process.client) {
|
||||||
|
// 1. Hydration (server: true): no fetch
|
||||||
|
// 2. Initial load (server: false): fetch on mounted
|
||||||
|
if (nuxt.isHydrating && !options.server) {
|
||||||
|
// Fetch on mounted (initial load or deferred fetch)
|
||||||
|
onMountedCbs.push(fetch)
|
||||||
|
} else if (!nuxt.isHydrating) {
|
||||||
|
if (options.defer) {
|
||||||
|
// 3. Navigation (defer: true): fetch on mounted
|
||||||
|
onMountedCbs.push(fetch)
|
||||||
|
} else {
|
||||||
|
// 4. Navigation (defer: false): await fetch
|
||||||
|
await fetch()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Watch request
|
||||||
|
if (typeof request === 'function') {
|
||||||
|
watch(request, fetch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Server side
|
||||||
|
if (process.server && !clientOnly) {
|
||||||
|
await fetch()
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: toRef<any, string>(data, key),
|
||||||
|
fetch
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
23
packages/nuxt3/src/app/composables/hydrate.ts
Normal file
23
packages/nuxt3/src/app/composables/hydrate.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { useNuxt } from 'nuxt/app'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows full control of the hydration cycle to set and receive data from the server.
|
||||||
|
* @param key a unique key to identify the data in the Nuxt payload
|
||||||
|
* @param get a function that returns the value to set the initial data
|
||||||
|
* @param set a function that will receive the data on the client-side
|
||||||
|
*/
|
||||||
|
export const useHydration = <T>(key: string, get: () => T, set: (value: T) => void) => {
|
||||||
|
const nuxt = useNuxt()
|
||||||
|
|
||||||
|
if (process.server) {
|
||||||
|
nuxt.hooks.hook('app:rendered', () => {
|
||||||
|
nuxt.payload[key] = get()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.client) {
|
||||||
|
nuxt.hooks.hook('app:created', () => {
|
||||||
|
set(nuxt.payload[key])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
3
packages/nuxt3/src/app/composables/index.ts
Normal file
3
packages/nuxt3/src/app/composables/index.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export { useFetch } from './fetch'
|
||||||
|
export { useData } from './data'
|
||||||
|
export { useHydration } from './hydrate'
|
10
packages/nuxt3/src/app/declarations/node.d.ts
vendored
Normal file
10
packages/nuxt3/src/app/declarations/node.d.ts
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
declare module NodeJS {
|
||||||
|
interface Process {
|
||||||
|
browser: boolean
|
||||||
|
client: boolean
|
||||||
|
mode: 'spa' | 'universal'
|
||||||
|
modern: boolean
|
||||||
|
server: boolean
|
||||||
|
static: boolean
|
||||||
|
}
|
||||||
|
}
|
6
packages/nuxt3/src/app/declarations/nuxt-build.d.ts
vendored
Normal file
6
packages/nuxt3/src/app/declarations/nuxt-build.d.ts
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
declare module '~build/routes' {
|
||||||
|
import { RouteRecordRaw } from 'vue-router'
|
||||||
|
|
||||||
|
const _default: RouteRecordRaw[]
|
||||||
|
export default _default
|
||||||
|
}
|
5
packages/nuxt3/src/app/declarations/shim.d.ts
vendored
Normal file
5
packages/nuxt3/src/app/declarations/shim.d.ts
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
declare module '*.vue' {
|
||||||
|
import { Component } from 'vue'
|
||||||
|
const component: Component
|
||||||
|
export default component
|
||||||
|
}
|
7
packages/nuxt3/src/app/declarations/types.ts
Normal file
7
packages/nuxt3/src/app/declarations/types.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { Nuxt } from 'nuxt/app'
|
||||||
|
|
||||||
|
declare module 'vue' {
|
||||||
|
interface App {
|
||||||
|
$nuxt: Nuxt
|
||||||
|
}
|
||||||
|
}
|
3
packages/nuxt3/src/app/declarations/window.d.ts
vendored
Normal file
3
packages/nuxt3/src/app/declarations/window.d.ts
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
interface Window {
|
||||||
|
__NUXT__?: Record<string, any>
|
||||||
|
}
|
2
packages/nuxt3/src/app/index.ts
Normal file
2
packages/nuxt3/src/app/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export { useNuxt } from './nuxt/composables'
|
||||||
|
export * from './nuxt'
|
37
packages/nuxt3/src/app/nuxt/composables.ts
Normal file
37
packages/nuxt3/src/app/nuxt/composables.ts
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import { getCurrentInstance } from 'vue'
|
||||||
|
import type { Nuxt } from 'nuxt/app'
|
||||||
|
|
||||||
|
let currentNuxtInstance: Nuxt
|
||||||
|
|
||||||
|
export const setNuxtInstance = (nuxt: Nuxt) => {
|
||||||
|
currentNuxtInstance = nuxt
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures that the setup function passed in has access to the Nuxt instance via `useNuxt`.
|
||||||
|
* @param nuxt A Nuxt instance
|
||||||
|
* @param setup The function to call
|
||||||
|
*/
|
||||||
|
export async function callWithNuxt (nuxt: Nuxt, setup: () => any) {
|
||||||
|
setNuxtInstance(nuxt)
|
||||||
|
const p = setup()
|
||||||
|
setNuxtInstance(undefined)
|
||||||
|
await p
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current Nuxt instance.
|
||||||
|
*/
|
||||||
|
export function useNuxt () {
|
||||||
|
const vm = getCurrentInstance()
|
||||||
|
|
||||||
|
if (!vm && !currentNuxtInstance) {
|
||||||
|
throw new Error('nuxt instance unavailable')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!vm) {
|
||||||
|
return currentNuxtInstance
|
||||||
|
}
|
||||||
|
|
||||||
|
return vm.appContext.app.$nuxt
|
||||||
|
}
|
89
packages/nuxt3/src/app/nuxt/index.ts
Normal file
89
packages/nuxt3/src/app/nuxt/index.ts
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
import Hookable from 'hookable'
|
||||||
|
import type { App } from 'vue'
|
||||||
|
import { defineGetter } from '../utils'
|
||||||
|
import { callWithNuxt } from './composables'
|
||||||
|
|
||||||
|
export interface Nuxt {
|
||||||
|
app: App
|
||||||
|
globalName: string
|
||||||
|
|
||||||
|
hooks: Hookable
|
||||||
|
hook: Hookable['hook']
|
||||||
|
callHook: Hookable['callHook']
|
||||||
|
|
||||||
|
[key: string]: any
|
||||||
|
|
||||||
|
ssrContext?: Record<string, any>
|
||||||
|
payload: {
|
||||||
|
serverRendered?: true,
|
||||||
|
data?: object
|
||||||
|
rendered?: Function
|
||||||
|
[key: string]: any
|
||||||
|
}
|
||||||
|
|
||||||
|
provide: (name: string, value: any) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Plugin {
|
||||||
|
(nuxt: Nuxt, provide?: Nuxt['provide']): Promise<void> | void
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateOptions {
|
||||||
|
app: Nuxt['app']
|
||||||
|
ssrContext?: Nuxt['ssrContext']
|
||||||
|
globalName?: Nuxt['globalName']
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createNuxt (options: CreateOptions) {
|
||||||
|
const nuxt: Nuxt = {
|
||||||
|
app: undefined,
|
||||||
|
provide: undefined,
|
||||||
|
globalName: 'nuxt',
|
||||||
|
state: {},
|
||||||
|
payload: {},
|
||||||
|
isHydrating: process.client,
|
||||||
|
...options
|
||||||
|
} as any as Nuxt
|
||||||
|
|
||||||
|
nuxt.hooks = new Hookable()
|
||||||
|
nuxt.hook = nuxt.hooks.hook
|
||||||
|
nuxt.callHook = nuxt.hooks.callHook
|
||||||
|
|
||||||
|
nuxt.provide = (name: string, value: any) => {
|
||||||
|
const $name = '$' + name
|
||||||
|
defineGetter(nuxt.app, $name, value)
|
||||||
|
defineGetter(nuxt.app.config.globalProperties, $name, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
nuxt.provide('nuxt', nuxt)
|
||||||
|
|
||||||
|
// Expose nuxt to the renderContext
|
||||||
|
if (nuxt.ssrContext) {
|
||||||
|
nuxt.ssrContext.nuxt = nuxt
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.server) {
|
||||||
|
nuxt.payload = {
|
||||||
|
serverRendered: true // TODO: legacy
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expose to server renderer to create window.__NUXT__
|
||||||
|
nuxt.ssrContext.payload = nuxt.payload
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.client) {
|
||||||
|
nuxt.payload = window.__NUXT__ || {}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nuxt
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyPlugin (nuxt: Nuxt, plugin: Plugin) {
|
||||||
|
return callWithNuxt(nuxt, () => plugin(nuxt))
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function applyPlugins (nuxt: Nuxt, plugins: Plugin[]) {
|
||||||
|
for (const plugin of plugins) {
|
||||||
|
await applyPlugin(nuxt, plugin)
|
||||||
|
}
|
||||||
|
}
|
25
packages/nuxt3/src/app/plugins/legacy.ts
Normal file
25
packages/nuxt3/src/app/plugins/legacy.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import type { App } from 'vue'
|
||||||
|
import type { Plugin } from 'nuxt/app'
|
||||||
|
|
||||||
|
export type LegacyApp = App<Element> & {
|
||||||
|
$root: LegacyApp
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: plugins second argument (inject)
|
||||||
|
// TODO: payload.serverRrendered
|
||||||
|
|
||||||
|
export default <Plugin> function legacy ({ app }) {
|
||||||
|
app.$nuxt.context = {}
|
||||||
|
|
||||||
|
if (process.client) {
|
||||||
|
const legacyApp = { ...app } as LegacyApp
|
||||||
|
legacyApp.$root = legacyApp
|
||||||
|
window[app.$nuxt.globalName] = legacyApp
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.server) {
|
||||||
|
const { ssrContext } = app.$nuxt
|
||||||
|
app.$nuxt.context.req = ssrContext.req
|
||||||
|
app.$nuxt.context.res = ssrContext.res
|
||||||
|
}
|
||||||
|
}
|
14
packages/nuxt3/src/app/plugins/logs.client.dev.ts
Normal file
14
packages/nuxt3/src/app/plugins/logs.client.dev.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
/* eslint-disable no-console */
|
||||||
|
import type { Plugin } from 'nuxt/app'
|
||||||
|
|
||||||
|
export default <Plugin> function logs ({ app }) {
|
||||||
|
// Only activate in development
|
||||||
|
const logs = app.$nuxt.payload.logs || []
|
||||||
|
if (logs.length > 0) {
|
||||||
|
const ssrLogStyle = 'background: #003C3C;border-radius: 0.5em;color: white;font-weight: bold;padding: 2px 0.5em;'
|
||||||
|
console.groupCollapsed && console.groupCollapsed('%cNuxt Server Logs', ssrLogStyle)
|
||||||
|
logs.forEach(logObj => (console[logObj.type] || console.log)(...logObj.args))
|
||||||
|
delete app.$nuxt.payload.logs
|
||||||
|
console.groupEnd && console.groupEnd()
|
||||||
|
}
|
||||||
|
}
|
11
packages/nuxt3/src/app/plugins/preload.server.ts
Normal file
11
packages/nuxt3/src/app/plugins/preload.server.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import type { Plugin } from 'nuxt/app'
|
||||||
|
|
||||||
|
export default <Plugin> function preload ({ app }) {
|
||||||
|
app.mixin({
|
||||||
|
beforeCreate () {
|
||||||
|
const { _registeredComponents } = this.$nuxt.ssrContext
|
||||||
|
const { __moduleIdentifier } = this.$options
|
||||||
|
_registeredComponents.add(__moduleIdentifier)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
44
packages/nuxt3/src/app/plugins/progress.client.ts
Normal file
44
packages/nuxt3/src/app/plugins/progress.client.ts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import type { Plugin } from 'nuxt/app'
|
||||||
|
|
||||||
|
export default <Plugin> function progressbar ({ app }) {
|
||||||
|
const { $nuxt } = app
|
||||||
|
$nuxt.hooks.hook('app:mounted', () => {
|
||||||
|
const el = document.createElement('div')
|
||||||
|
el.id = 'nuxt-progress'
|
||||||
|
document.body.appendChild(el)
|
||||||
|
el.style.position = 'fixed'
|
||||||
|
el.style.backgroundColor = 'black'
|
||||||
|
el.style.height = '2px'
|
||||||
|
el.style.top = '0px'
|
||||||
|
el.style.left = '0px'
|
||||||
|
el.style.transition = 'width 0.1s, opacity 0.4s'
|
||||||
|
const duration = 3000
|
||||||
|
const progress = 10000 / Math.floor(duration)
|
||||||
|
let timeout
|
||||||
|
let interval
|
||||||
|
$nuxt.hooks.hook('page:start', () => {
|
||||||
|
if (timeout) { return }
|
||||||
|
timeout = setTimeout(() => {
|
||||||
|
let width = 10
|
||||||
|
el.style.opacity = '100%'
|
||||||
|
el.style.width = '10%'
|
||||||
|
interval = setInterval(() => {
|
||||||
|
if (width >= 100) { return }
|
||||||
|
width = Math.floor(width + progress)
|
||||||
|
el.style.width = `${width}%`
|
||||||
|
}, 100)
|
||||||
|
}, 200)
|
||||||
|
})
|
||||||
|
$nuxt.hooks.hook('page:finish', () => {
|
||||||
|
timeout && clearTimeout(timeout)
|
||||||
|
timeout = null
|
||||||
|
interval && clearInterval(interval)
|
||||||
|
interval = null
|
||||||
|
el.style.width = '100%'
|
||||||
|
el.style.opacity = '0%'
|
||||||
|
setTimeout(() => {
|
||||||
|
el.style.width = '0%'
|
||||||
|
}, 500)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
13
packages/nuxt3/src/app/plugins/router/NuxtChild.vue
Normal file
13
packages/nuxt3/src/app/plugins/router/NuxtChild.vue
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<template>
|
||||||
|
<RouterView v-slot="{ Component }">
|
||||||
|
<transition name="page" mode="out-in">
|
||||||
|
<component :is="Component" :key="$route.path" />
|
||||||
|
</transition>
|
||||||
|
</RouterView>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'NuxtChild'
|
||||||
|
}
|
||||||
|
</script>
|
17
packages/nuxt3/src/app/plugins/router/NuxtPage.vue
Normal file
17
packages/nuxt3/src/app/plugins/router/NuxtPage.vue
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<template>
|
||||||
|
<RouterView v-slot="{ Component }">
|
||||||
|
<transition name="page" mode="out-in">
|
||||||
|
<!-- <keep-alive> -->
|
||||||
|
<Suspense @pending="$nuxt.hooks.callHook('page:start')" @resolve="$nuxt.hooks.callHook('page:finish')">
|
||||||
|
<component :is="Component" :key="$route.path" />
|
||||||
|
</Suspense>
|
||||||
|
<!-- <keep-alive -->
|
||||||
|
</transition>
|
||||||
|
</RouterView>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'NuxtPage'
|
||||||
|
}
|
||||||
|
</script>
|
58
packages/nuxt3/src/app/plugins/router/index.ts
Normal file
58
packages/nuxt3/src/app/plugins/router/index.ts
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import { shallowRef } from 'vue'
|
||||||
|
import {
|
||||||
|
createRouter,
|
||||||
|
createWebHistory,
|
||||||
|
createMemoryHistory,
|
||||||
|
RouterLink
|
||||||
|
} from 'vue-router'
|
||||||
|
import type { Plugin } from 'nuxt/app'
|
||||||
|
import NuxtPage from './NuxtPage.vue'
|
||||||
|
import NuxtChild from './NuxtChild.vue'
|
||||||
|
|
||||||
|
import routes from '~build/routes'
|
||||||
|
|
||||||
|
export default <Plugin> function router (nuxt) {
|
||||||
|
const { app } = nuxt
|
||||||
|
|
||||||
|
// TODO: move this outside this plugin
|
||||||
|
if (!routes.length) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
app.component('NuxtPage', NuxtPage)
|
||||||
|
app.component('NuxtChild', NuxtChild)
|
||||||
|
app.component('NuxtLink', RouterLink)
|
||||||
|
|
||||||
|
const routerHistory = process.client
|
||||||
|
? createWebHistory()
|
||||||
|
: createMemoryHistory()
|
||||||
|
|
||||||
|
const router = createRouter({
|
||||||
|
history: routerHistory,
|
||||||
|
routes
|
||||||
|
})
|
||||||
|
app.use(router)
|
||||||
|
|
||||||
|
const previousRoute = shallowRef(router.currentRoute.value)
|
||||||
|
router.afterEach((_to, from) => {
|
||||||
|
previousRoute.value = from
|
||||||
|
})
|
||||||
|
|
||||||
|
Object.defineProperty(app.config.globalProperties, 'previousRoute', {
|
||||||
|
get: () => previousRoute.value
|
||||||
|
})
|
||||||
|
|
||||||
|
nuxt.hooks.hook('app:created', async () => {
|
||||||
|
if (process.server) {
|
||||||
|
router.push(nuxt.ssrContext.url)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await router.isReady()
|
||||||
|
if (!router.currentRoute.value.matched.length) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
24
packages/nuxt3/src/app/plugins/vuex.ts
Normal file
24
packages/nuxt3/src/app/plugins/vuex.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { createVuex, defineStore, useStore } from 'vuex5/dist/vuex.esm'
|
||||||
|
import type { Plugin } from 'nuxt/app'
|
||||||
|
import { useHydration } from 'nuxt/app/composables'
|
||||||
|
|
||||||
|
export default <Plugin> function ({ app }) {
|
||||||
|
const vuex = createVuex({ })
|
||||||
|
|
||||||
|
app.use(vuex)
|
||||||
|
|
||||||
|
useHydration('vuex',
|
||||||
|
() => vuex.registry,
|
||||||
|
state => () => {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log('vuex.replaceStateTree', state)
|
||||||
|
// vuex.replaceStateTree(state)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createStore (arg1, arg2) {
|
||||||
|
const store = defineStore(arg1, arg2)
|
||||||
|
|
||||||
|
return () => useStore(store)
|
||||||
|
}
|
15
packages/nuxt3/src/app/utils/fetch.ts
Normal file
15
packages/nuxt3/src/app/utils/fetch.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// TODO: Move to a nuxt-contrib utility
|
||||||
|
import destr from 'destr'
|
||||||
|
|
||||||
|
// TODO: polyfill by env (nuxt) not by util
|
||||||
|
const _fetch = process.server ? require('node-fetch') : global.fetch
|
||||||
|
|
||||||
|
export async function httpFetch (path: string) {
|
||||||
|
const res = await _fetch(path)
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new Error(res)
|
||||||
|
}
|
||||||
|
const data = await res.text()
|
||||||
|
|
||||||
|
return destr(data)
|
||||||
|
}
|
3
packages/nuxt3/src/app/utils/index.ts
Normal file
3
packages/nuxt3/src/app/utils/index.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export function defineGetter<K extends string | number | symbol, V> (obj: Record<K, V>, key: K, val: V) {
|
||||||
|
Object.defineProperty(obj, key, { get: () => val })
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
import path from 'path'
|
|
||||||
import type { WatchOptions as ChokidarWatchOptions } from 'chokidar'
|
import type { WatchOptions as ChokidarWatchOptions } from 'chokidar'
|
||||||
import type express from 'express'
|
import type express from 'express'
|
||||||
import type { configHooksT } from 'hookable/types/types'
|
import type { configHooksT } from 'hookable'
|
||||||
|
import { APP_DIR } from 'src/index'
|
||||||
import ignore from 'ignore'
|
import ignore from 'ignore'
|
||||||
import capitalize from 'lodash/capitalize'
|
import capitalize from 'lodash/capitalize'
|
||||||
import env from 'std-env'
|
import env from 'std-env'
|
||||||
@ -172,7 +172,7 @@ export default (): CommonConfiguration => ({
|
|||||||
modulesDir: [
|
modulesDir: [
|
||||||
'node_modules'
|
'node_modules'
|
||||||
],
|
],
|
||||||
appDir: path.resolve(__dirname, '../../../app'),
|
appDir: APP_DIR,
|
||||||
dir: {
|
dir: {
|
||||||
assets: 'assets',
|
assets: 'assets',
|
||||||
app: 'app',
|
app: 'app',
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
import { resolve } from 'path'
|
||||||
export * from './core'
|
export * from './core'
|
||||||
|
|
||||||
|
export const APP_DIR = resolve(__dirname, 'app')
|
||||||
|
|
||||||
export const getBuilder = () => import('./builder')
|
export const getBuilder = () => import('./builder')
|
||||||
|
@ -114,8 +114,8 @@ function baseAlias (ctx: WebpackConfigContext) {
|
|||||||
const { options, isServer } = ctx
|
const { options, isServer } = ctx
|
||||||
|
|
||||||
ctx.alias = {
|
ctx.alias = {
|
||||||
app: options.appDir,
|
'nuxt/app': options.appDir,
|
||||||
'nuxt-build': options.buildDir,
|
'~build': options.buildDir,
|
||||||
'vue-meta': require.resolve(`vue-meta${isServer ? '' : '/dist/vue-meta.esm.browser.js'}`),
|
'vue-meta': require.resolve(`vue-meta${isServer ? '' : '/dist/vue-meta.esm.browser.js'}`),
|
||||||
...options.alias,
|
...options.alias,
|
||||||
...ctx.alias
|
...ctx.alias
|
||||||
|
Loading…
Reference in New Issue
Block a user