mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-22 05:35:13 +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 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 capitalize from 'lodash/capitalize'
|
||||
import env from 'std-env'
|
||||
@ -172,7 +172,7 @@ export default (): CommonConfiguration => ({
|
||||
modulesDir: [
|
||||
'node_modules'
|
||||
],
|
||||
appDir: path.resolve(__dirname, '../../../app'),
|
||||
appDir: APP_DIR,
|
||||
dir: {
|
||||
assets: 'assets',
|
||||
app: 'app',
|
||||
|
@ -1,3 +1,6 @@
|
||||
import { resolve } from 'path'
|
||||
export * from './core'
|
||||
|
||||
export const APP_DIR = resolve(__dirname, 'app')
|
||||
|
||||
export const getBuilder = () => import('./builder')
|
||||
|
@ -114,8 +114,8 @@ function baseAlias (ctx: WebpackConfigContext) {
|
||||
const { options, isServer } = ctx
|
||||
|
||||
ctx.alias = {
|
||||
app: options.appDir,
|
||||
'nuxt-build': options.buildDir,
|
||||
'nuxt/app': options.appDir,
|
||||
'~build': options.buildDir,
|
||||
'vue-meta': require.resolve(`vue-meta${isServer ? '' : '/dist/vue-meta.esm.browser.js'}`),
|
||||
...options.alias,
|
||||
...ctx.alias
|
||||
|
Loading…
Reference in New Issue
Block a user