mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-26 07:32:01 +00:00
feat: optional pages and refactor nuxt3 (#142)
This commit is contained in:
parent
d95e002d5b
commit
6b62d456d7
1
packages/app/src/_templates/app.mjs
Normal file
1
packages/app/src/_templates/app.mjs
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default } from '<%= app.main %>'
|
@ -1,28 +0,0 @@
|
|||||||
<%= nuxtOptions.vite ? "import('vite/dynamic-import-polyfill')" : '' %>
|
|
||||||
import { createSSRApp, nextTick } from 'vue'
|
|
||||||
import { createNuxt, applyPlugins } from '@nuxt/app'
|
|
||||||
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 nuxt.hooks.callHook('app:created', app)
|
|
||||||
await nuxt.hooks.callHook('app:beforeMount', app)
|
|
||||||
|
|
||||||
app.mount('#__nuxt')
|
|
||||||
|
|
||||||
await 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
|
|
||||||
})
|
|
1
packages/app/src/_templates/entry.mjs
Normal file
1
packages/app/src/_templates/entry.mjs
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default } from '#app/entry'
|
@ -1,18 +0,0 @@
|
|||||||
import { createApp } from 'vue'
|
|
||||||
import { createNuxt, applyPlugins } from '@nuxt/app'
|
|
||||||
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 nuxt.hooks.callHook('app:created', app)
|
|
||||||
|
|
||||||
return app
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
import { $fetch } from 'ohmyfetch'
|
|
||||||
import logs from '#app/plugins/logs.client.dev'
|
|
||||||
import progress from '#app/plugins/progress.client'
|
|
||||||
<% const plugins = app.plugins.filter(p => p.mode === 'client').map(p => p.src) %>
|
|
||||||
<%= nxt.importSources(plugins) %>
|
|
||||||
|
|
||||||
if (!globalThis.$fetch) {
|
|
||||||
globalThis.$fetch = $fetch
|
|
||||||
}
|
|
||||||
|
|
||||||
const plugins = [
|
|
||||||
progress,
|
|
||||||
<%= plugins.map(nxt.importName).join(',\n\t') %>
|
|
||||||
]
|
|
||||||
|
|
||||||
if (process.dev) {
|
|
||||||
plugins.push(logs)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default plugins
|
|
@ -1,15 +1,23 @@
|
|||||||
import head from '#app/plugins/head'
|
import head from '#app/plugins/head'
|
||||||
import router from '#app/plugins/router'
|
|
||||||
import vuex from '#app/plugins/vuex'
|
|
||||||
import legacy from '#app/plugins/legacy'
|
import legacy from '#app/plugins/legacy'
|
||||||
|
import preload from '#app/plugins/preload.server'
|
||||||
|
|
||||||
<% const plugins = app.plugins.filter(p => p.mode === 'all').map(p => p.src) %>
|
<%= utils.importSources(app.plugins.map(p => p.src)) %>
|
||||||
<%= nxt.importSources(plugins) %>
|
|
||||||
|
|
||||||
export default [
|
const commonPlugins = [
|
||||||
head,
|
head,
|
||||||
router,
|
|
||||||
vuex,
|
|
||||||
legacy,
|
legacy,
|
||||||
<%= plugins.map(nxt.importName).join(',\n\t') %>
|
<%= app.plugins.filter(p => !p.mode || p.mode === 'all').map(p => utils.importName(p.src)).join(',\n ') %>
|
||||||
]
|
]
|
||||||
|
|
||||||
|
export const clientPluigns = [
|
||||||
|
...commonPlugins,<%= app.plugins.filter(p => p.mode === 'client').map(p => utils.importName(p.src)).join(',\n ') %>
|
||||||
|
]
|
||||||
|
|
||||||
|
export const serverPluigns = [
|
||||||
|
...commonPlugins,
|
||||||
|
preload,
|
||||||
|
<%= app.plugins.filter(p => p.mode === 'server').map(p => utils.importName(p.src)).join(',\n ') %>
|
||||||
|
]
|
||||||
|
|
||||||
|
export default process.client ? clientPluigns : serverPluigns
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
import preload from '#app/plugins/preload.server'
|
|
||||||
<% const plugins = app.plugins.filter(p => p.mode === 'server').map(p => p.src) %>
|
|
||||||
<%= nxt.importSources(plugins) %>
|
|
||||||
|
|
||||||
export default [
|
|
||||||
preload
|
|
||||||
<%= plugins.map(nxt.importName).join(',\n\t') %>
|
|
||||||
]
|
|
@ -1,2 +0,0 @@
|
|||||||
// TODO: Use webpack-virtual-modules
|
|
||||||
export default <%= nxt.serialize(app.routes.map(nxt.serializeRoute)) %>
|
|
@ -6,8 +6,8 @@
|
|||||||
<body {{ BODY_ATTRS }}>
|
<body {{ BODY_ATTRS }}>
|
||||||
{{ BODY_SCRIPTS_PREPEND }}
|
{{ BODY_SCRIPTS_PREPEND }}
|
||||||
<div id="__nuxt">{{ APP }}</div>
|
<div id="__nuxt">{{ APP }}</div>
|
||||||
<% if (nuxtOptions.vite && nuxtOptions.dev) { %><script type="module" src="/@vite/client"></script>
|
<% if (nuxt.options.vite && nuxt.options.dev) { %><script type="module" src="/@vite/client"></script>
|
||||||
<script type="module" src="/entry.client.mjs"></script><% } %>
|
<script type="module" src="/entry.mjs"></script><% } %>
|
||||||
{{ BODY_SCRIPTS }}
|
{{ BODY_SCRIPTS }}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
<!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>
|
|
@ -1,3 +0,0 @@
|
|||||||
<template>
|
|
||||||
<router-view />
|
|
||||||
</template>
|
|
47
packages/app/src/entry.ts
Normal file
47
packages/app/src/entry.ts
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import { createSSRApp, createApp, nextTick } from 'vue'
|
||||||
|
import { createNuxt, applyPlugins } from '@nuxt/app'
|
||||||
|
// @ts-ignore
|
||||||
|
import plugins from '#build/plugins'
|
||||||
|
// @ts-ignore
|
||||||
|
import App from '#build/app'
|
||||||
|
|
||||||
|
let entry: Function
|
||||||
|
|
||||||
|
if (process.server) {
|
||||||
|
entry = async function createNuxtAppServer (ssrContext = {}) {
|
||||||
|
const app = createApp(App)
|
||||||
|
|
||||||
|
const nuxt = createNuxt({ app, ssrContext })
|
||||||
|
|
||||||
|
await applyPlugins(nuxt, plugins)
|
||||||
|
|
||||||
|
await nuxt.hooks.callHook('app:created', app)
|
||||||
|
|
||||||
|
return app
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.client) {
|
||||||
|
entry = async function initApp () {
|
||||||
|
const app = createSSRApp(App)
|
||||||
|
|
||||||
|
const nuxt = createNuxt({ app })
|
||||||
|
|
||||||
|
await applyPlugins(nuxt, plugins)
|
||||||
|
|
||||||
|
await nuxt.hooks.callHook('app:created', app)
|
||||||
|
await nuxt.hooks.callHook('app:beforeMount', app)
|
||||||
|
|
||||||
|
app.mount('#__nuxt')
|
||||||
|
|
||||||
|
await nuxt.hooks.callHook('app:mounted', app)
|
||||||
|
await nextTick()
|
||||||
|
nuxt.isHydrating = false
|
||||||
|
}
|
||||||
|
|
||||||
|
entry().catch((error) => {
|
||||||
|
console.error('Error while mounting app:', error) // eslint-disable-line no-console
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ctx => entry(ctx)
|
@ -1 +0,0 @@
|
|||||||
export * from './index.ts'
|
|
@ -11,6 +11,8 @@ import type { TemplateOpts, PluginTemplateOpts } from '../types/module'
|
|||||||
*
|
*
|
||||||
* If a fileName is not provided or the template is string, target file name defaults to
|
* If a fileName is not provided or the template is string, target file name defaults to
|
||||||
* [dirName].[fileName].[pathHash].[ext].
|
* [dirName].[fileName].[pathHash].[ext].
|
||||||
|
*
|
||||||
|
* This file is available to import with `#build/${filename}`
|
||||||
*/
|
*/
|
||||||
export function addTemplate (tmpl: TemplateOpts | string) {
|
export function addTemplate (tmpl: TemplateOpts | string) {
|
||||||
const nuxt = useNuxt()
|
const nuxt = useNuxt()
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Nuxt } from './nuxt'
|
import { Nuxt, NuxtApp } from './nuxt'
|
||||||
import type { IncomingMessage, ServerResponse } from 'http'
|
import type { IncomingMessage, ServerResponse } from 'http'
|
||||||
import type { Compiler, Configuration, Stats } from 'webpack'
|
import type { Compiler, Configuration, Stats } from 'webpack'
|
||||||
import type { NuxtConfig, NuxtOptions } from '..'
|
import type { NuxtConfig, NuxtOptions } from '..'
|
||||||
@ -23,6 +23,11 @@ export interface NuxtHooks {
|
|||||||
// Don't break usage of untyped hooks
|
// Don't break usage of untyped hooks
|
||||||
[key: string]: (...args: any[]) => HookResult
|
[key: string]: (...args: any[]) => HookResult
|
||||||
|
|
||||||
|
// nuxt3
|
||||||
|
'app:resolve': (app: NuxtApp) => HookResult
|
||||||
|
'app:templates': (app: NuxtApp) => HookResult
|
||||||
|
'builder:generateApp': () => HookResult
|
||||||
|
|
||||||
// @nuxt/builder
|
// @nuxt/builder
|
||||||
'build:before':
|
'build:before':
|
||||||
(builder: Builder, buildOptions: NuxtOptions['build']) => HookResult
|
(builder: Builder, buildOptions: NuxtOptions['build']) => HookResult
|
||||||
|
@ -22,3 +22,24 @@ export interface Nuxt {
|
|||||||
/** The production or development server */
|
/** The production or development server */
|
||||||
server?: any
|
server?: any
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface NuxtPlugin {
|
||||||
|
src: string
|
||||||
|
mode?: 'server' | 'client' | 'all',
|
||||||
|
options?: Record<string, any>
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NuxtTemplate {
|
||||||
|
path: string // Relative path of destination
|
||||||
|
src?: string // Absolute path to source file
|
||||||
|
compile?: (data: Record<string, any>) => string
|
||||||
|
data?: any
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NuxtApp {
|
||||||
|
main?: string
|
||||||
|
dir: string
|
||||||
|
extensions: string[]
|
||||||
|
plugins: NuxtPlugin[]
|
||||||
|
templates: NuxtTemplate[]
|
||||||
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { existsSync, lstatSync } from 'fs'
|
import { existsSync, lstatSync } from 'fs'
|
||||||
import { resolve, join } from 'upath'
|
import { resolve, join } from 'upath'
|
||||||
|
import globby from 'globby'
|
||||||
|
|
||||||
export interface ResolveOptions {
|
export interface ResolveOptions {
|
||||||
/**
|
/**
|
||||||
@ -93,3 +94,11 @@ export function tryResolvePath (path: string, opts: ResolveOptions = {}) {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function resolveFiles (path: string, pattern: string) {
|
||||||
|
const files = await globby(pattern, {
|
||||||
|
cwd: path,
|
||||||
|
followSymbolicLinks: true
|
||||||
|
})
|
||||||
|
return files.map(p => resolve(path, p))
|
||||||
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import consola from 'consola'
|
import consola from 'consola'
|
||||||
|
|
||||||
const internalRegex = /^\.|\?|\.[mc]?js$|.ts$/
|
const internalRegex = /^\.|\?|\.[mc]?js$|.ts$|.json$/
|
||||||
|
|
||||||
export function autoMock () {
|
export function autoMock () {
|
||||||
return {
|
return {
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
"@nuxt/app": "^0.3.3",
|
"@nuxt/app": "^0.3.3",
|
||||||
"@nuxt/kit": "^0.5.3",
|
"@nuxt/kit": "^0.5.3",
|
||||||
"@nuxt/nitro": "^0.6.3",
|
"@nuxt/nitro": "^0.6.3",
|
||||||
|
"@nuxt/pages": "^0.1.0",
|
||||||
"@nuxt/vite-builder": "^0.3.3",
|
"@nuxt/vite-builder": "^0.3.3",
|
||||||
"@nuxt/webpack-builder": "^0.3.4",
|
"@nuxt/webpack-builder": "^0.3.4",
|
||||||
"chokidar": "^3.5.1",
|
"chokidar": "^3.5.1",
|
||||||
|
@ -1,79 +1,103 @@
|
|||||||
import { resolve } from 'path'
|
import { resolve, join, relative, dirname } from 'upath'
|
||||||
|
import globby from 'globby'
|
||||||
|
import lodashTemplate from 'lodash/template'
|
||||||
import defu from 'defu'
|
import defu from 'defu'
|
||||||
import { tryResolvePath } from '@nuxt/kit'
|
import { tryResolvePath, resolveFiles, Nuxt, NuxtApp, NuxtTemplate, NuxtPlugin } from '@nuxt/kit'
|
||||||
import { Builder } from './builder'
|
import { mkdirp, writeFile, readFile } from 'fs-extra'
|
||||||
import { NuxtRoute, resolvePagesRoutes } from './pages'
|
import * as templateUtils from './template.utils'
|
||||||
import { NuxtPlugin, resolvePlugins } from './plugins'
|
|
||||||
|
|
||||||
export interface NuxtApp {
|
export function createApp (nuxt: Nuxt, options: Partial<NuxtApp> = {}): NuxtApp {
|
||||||
main?: string
|
return defu(options, {
|
||||||
routes: NuxtRoute[]
|
|
||||||
dir: string
|
|
||||||
extensions: string[]
|
|
||||||
plugins: NuxtPlugin[]
|
|
||||||
templates: Record<string, string>
|
|
||||||
pages?: {
|
|
||||||
dir: string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scan project structure
|
|
||||||
export async function createApp (
|
|
||||||
builder: Builder,
|
|
||||||
options: Partial<NuxtApp> = {}
|
|
||||||
): Promise<NuxtApp> {
|
|
||||||
const { nuxt } = builder
|
|
||||||
|
|
||||||
// Create base app object
|
|
||||||
const app: NuxtApp = defu(options, {
|
|
||||||
dir: nuxt.options.srcDir,
|
dir: nuxt.options.srcDir,
|
||||||
extensions: nuxt.options.extensions,
|
extensions: nuxt.options.extensions,
|
||||||
routes: [],
|
|
||||||
plugins: [],
|
plugins: [],
|
||||||
templates: {},
|
templates: {}
|
||||||
pages: {
|
|
||||||
dir: 'pages'
|
|
||||||
}
|
|
||||||
} as NuxtApp)
|
} as NuxtApp)
|
||||||
|
}
|
||||||
|
|
||||||
// Resolve app.main
|
export async function generateApp (nuxt: Nuxt, app: NuxtApp) {
|
||||||
|
// Resolve app
|
||||||
|
await resolveApp(nuxt, app)
|
||||||
|
|
||||||
|
// Scan templates
|
||||||
|
const templatesDir = join(nuxt.options.appDir, '_templates')
|
||||||
|
const templateFiles = await globby(join(templatesDir, '/**'))
|
||||||
|
app.templates = templateFiles
|
||||||
|
.filter(src => !src.endsWith('.d.ts'))
|
||||||
|
.map(src => ({
|
||||||
|
src,
|
||||||
|
path: relative(templatesDir, src),
|
||||||
|
data: {}
|
||||||
|
} as NuxtTemplate))
|
||||||
|
|
||||||
|
// Extend templates
|
||||||
|
await nuxt.callHook('app:templates', app)
|
||||||
|
|
||||||
|
// Generate templates
|
||||||
|
await Promise.all(app.templates.map(t => generateTemplate(t, nuxt.options.buildDir, {
|
||||||
|
utils: templateUtils,
|
||||||
|
nuxt,
|
||||||
|
app
|
||||||
|
})))
|
||||||
|
|
||||||
|
await nuxt.callHook('app:templatesGenerated', app)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function resolveApp (nuxt: Nuxt, app: NuxtApp) {
|
||||||
const resolveOptions = {
|
const resolveOptions = {
|
||||||
base: nuxt.options.srcDir,
|
base: nuxt.options.srcDir,
|
||||||
alias: nuxt.options.alias,
|
alias: nuxt.options.alias,
|
||||||
extensions: nuxt.options.extensions
|
extensions: nuxt.options.extensions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Resolve main (app.vue)
|
||||||
if (!app.main) {
|
if (!app.main) {
|
||||||
app.main = tryResolvePath('~/App', resolveOptions) ||
|
app.main = tryResolvePath('~/App', resolveOptions) || tryResolvePath('~/app', resolveOptions)
|
||||||
tryResolvePath('~/app', resolveOptions)
|
|
||||||
}
|
}
|
||||||
|
if (!app.main) {
|
||||||
// Resolve pages/
|
|
||||||
if (app.pages) {
|
|
||||||
app.routes.push(...(await resolvePagesRoutes(builder, app)))
|
|
||||||
}
|
|
||||||
if (app.routes.length) {
|
|
||||||
// Add 404 page is not added
|
|
||||||
const page404 = app.routes.find(route => route.name === '404')
|
|
||||||
if (!page404) {
|
|
||||||
app.routes.push({
|
|
||||||
name: '404',
|
|
||||||
path: '/:catchAll(.*)*',
|
|
||||||
file: resolve(nuxt.options.appDir, 'pages/404.vue'),
|
|
||||||
children: []
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback app.main
|
|
||||||
if (!app.main && app.routes.length) {
|
|
||||||
app.main = resolve(nuxt.options.appDir, 'app.pages.vue')
|
|
||||||
} else if (!app.main) {
|
|
||||||
app.main = resolve(nuxt.options.appDir, 'app.tutorial.vue')
|
app.main = resolve(nuxt.options.appDir, 'app.tutorial.vue')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve plugins/
|
// Resolve plugins
|
||||||
app.plugins = await resolvePlugins(builder, app)
|
app.plugins = [
|
||||||
|
...nuxt.options.plugins,
|
||||||
|
...await resolvePlugins(nuxt)
|
||||||
|
]
|
||||||
|
|
||||||
return app
|
// Extend app
|
||||||
|
await nuxt.callHook('app:resolve', app)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function generateTemplate (tmpl: NuxtTemplate, destDir: string, ctx) {
|
||||||
|
let compiledSrc: string = ''
|
||||||
|
const data = { ...ctx, ...tmpl.data }
|
||||||
|
if (tmpl.src) {
|
||||||
|
try {
|
||||||
|
const srcContents = await readFile(tmpl.src, 'utf-8')
|
||||||
|
compiledSrc = lodashTemplate(srcContents, {})(data)
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error compiling template: ', tmpl)
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
} else if (tmpl.compile) {
|
||||||
|
compiledSrc = tmpl.compile(data)
|
||||||
|
}
|
||||||
|
const dest = join(destDir, tmpl.path)
|
||||||
|
await mkdirp(dirname(dest))
|
||||||
|
await writeFile(dest, compiledSrc)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function resolvePlugins (nuxt: Nuxt) {
|
||||||
|
const plugins = await resolveFiles(nuxt.options.srcDir, 'plugins/**/*.{js,ts}')
|
||||||
|
|
||||||
|
return plugins.map(src => ({
|
||||||
|
src,
|
||||||
|
mode: getPluginMode(src)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPluginMode (src: string) {
|
||||||
|
const [, mode = 'all'] = src.match(/\.(server|client)(\.\w+)*$/) || []
|
||||||
|
return mode as NuxtPlugin['mode']
|
||||||
}
|
}
|
||||||
|
@ -1,117 +1,48 @@
|
|||||||
import { join, relative, resolve } from 'upath'
|
import chokidar from 'chokidar'
|
||||||
import fsExtra from 'fs-extra'
|
|
||||||
import { debounce } from 'lodash'
|
|
||||||
import { Nuxt } from '@nuxt/kit'
|
import { Nuxt } from '@nuxt/kit'
|
||||||
|
import { emptyDir } from 'fs-extra'
|
||||||
|
import { createApp, generateApp } from './app'
|
||||||
|
|
||||||
import {
|
export async function build (nuxt: Nuxt) {
|
||||||
templateData,
|
// Clear buildDir once
|
||||||
compileTemplates,
|
await emptyDir(nuxt.options.buildDir)
|
||||||
scanTemplates,
|
|
||||||
NuxtTemplate
|
|
||||||
} from './template'
|
|
||||||
import { createWatcher, WatchCallback } from './watch'
|
|
||||||
import { createApp, NuxtApp } from './app'
|
|
||||||
import Ignore from './utils/ignore'
|
|
||||||
|
|
||||||
export class Builder {
|
const app = createApp(nuxt)
|
||||||
nuxt: Nuxt
|
await generateApp(nuxt, app)
|
||||||
globals: any
|
|
||||||
ignore: Ignore
|
|
||||||
templates: NuxtTemplate[]
|
|
||||||
app: NuxtApp
|
|
||||||
|
|
||||||
constructor (nuxt: Nuxt) {
|
|
||||||
this.nuxt = nuxt
|
|
||||||
this.ignore = new Ignore({
|
|
||||||
rootDir: nuxt.options.srcDir,
|
|
||||||
ignoreArray: nuxt.options.ignore.concat(
|
|
||||||
relative(nuxt.options.rootDir, nuxt.options.buildDir)
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
build () {
|
|
||||||
return _build(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
close () {
|
|
||||||
// TODO: close watchers
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extends VueRouter
|
|
||||||
async function _build (builder: Builder) {
|
|
||||||
const { nuxt } = builder
|
|
||||||
|
|
||||||
if (!nuxt.options.dev) {
|
|
||||||
await fsExtra.emptyDir(nuxt.options.buildDir)
|
|
||||||
}
|
|
||||||
await fsExtra.emptyDir(resolve(nuxt.options.buildDir, 'dist'))
|
|
||||||
await generate(builder)
|
|
||||||
|
|
||||||
if (nuxt.options.dev) {
|
if (nuxt.options.dev) {
|
||||||
watch(builder)
|
watch(nuxt)
|
||||||
|
nuxt.hook('builder:watch', async (event, path) => {
|
||||||
|
if (event !== 'change' && /app|plugins/i.test(path)) {
|
||||||
|
await generateApp(nuxt, app)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
nuxt.hook('builder:generateApp', () => generateApp(nuxt, app))
|
||||||
}
|
}
|
||||||
|
|
||||||
await bundle(builder)
|
await bundle(nuxt)
|
||||||
|
await nuxt.callHook('build:done', { nuxt })
|
||||||
await nuxt.callHook('build:done', builder)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function watch (builder: Builder) {
|
function watch (nuxt: Nuxt) {
|
||||||
const { nuxt, ignore } = builder
|
const watcher = chokidar.watch(nuxt.options.srcDir, {
|
||||||
|
...nuxt.options.watchers.chokidar,
|
||||||
// Watch internal templates
|
cwd: nuxt.options.srcDir,
|
||||||
const options = nuxt.options.watchers.chokidar
|
ignoreInitial: true,
|
||||||
const nuxtAppWatcher = createWatcher(nuxt.options.appDir, { ...options, cwd: nuxt.options.appDir }, ignore)
|
ignored: [
|
||||||
nuxtAppWatcher.watchAll(debounce(() => compileTemplates(builder.templates, nuxt.options.buildDir), 100))
|
'.nuxt',
|
||||||
|
'.output',
|
||||||
// Watch user app
|
'node_modules'
|
||||||
// TODO: handle multiples app dirs
|
]
|
||||||
const appPattern = `${builder.app.dir}/**/*{${nuxt.options.extensions.join(',')}}`
|
})
|
||||||
const appWatcher = createWatcher(appPattern, { ...options, cwd: builder.app.dir }, ignore)
|
const watchHook = (event, path) => nuxt.callHook('builder:watch', event, path)
|
||||||
// appWatcher.debug('srcDir')
|
watcher.on('all', watchHook)
|
||||||
const refreshTemplates = debounce(() => generate(builder), 100)
|
nuxt.hook('close', () => watcher.close())
|
||||||
// Watch for App.vue creation
|
return watcher
|
||||||
appWatcher.watch(/^(A|a)pp\.[a-z]{2,3}/, refreshTemplates, ['add', 'unlink'])
|
|
||||||
// Watch for page changes
|
|
||||||
appWatcher.watch(new RegExp(`^${nuxt.options.dir.pages}/`), refreshTemplates, ['add', 'unlink'])
|
|
||||||
// Watch for plugins changes
|
|
||||||
appWatcher.watch(/^plugins/, refreshTemplates, ['add', 'unlink'])
|
|
||||||
|
|
||||||
// Shared Watcher
|
|
||||||
const watchHook: WatchCallback = (event, path) => builder.nuxt.callHook('builder:watch', event, path)
|
|
||||||
const watchHookDebounced = debounce(watchHook, 100)
|
|
||||||
appWatcher.watchAll(watchHookDebounced)
|
|
||||||
nuxtAppWatcher.watchAll(watchHookDebounced)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function generate (builder: Builder) {
|
async function bundle (nuxt: Nuxt) {
|
||||||
const { nuxt } = builder
|
|
||||||
|
|
||||||
builder.app = await createApp(builder)
|
|
||||||
// Todo: Call app:created hook
|
|
||||||
|
|
||||||
const templatesDir = join(builder.nuxt.options.appDir, '_templates')
|
|
||||||
const appTemplates = await scanTemplates(templatesDir, templateData(builder))
|
|
||||||
// Todo: Call app:templates hook
|
|
||||||
|
|
||||||
builder.templates = [...appTemplates]
|
|
||||||
|
|
||||||
await compileTemplates(builder.templates, nuxt.options.buildDir)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function bundle ({ nuxt }: Builder) {
|
|
||||||
// @ts-ignore
|
|
||||||
const useVite = !!nuxt.options.vite
|
const useVite = !!nuxt.options.vite
|
||||||
const { bundle } = await (useVite ? import('@nuxt/vite-builder') : import('@nuxt/webpack-builder'))
|
const { bundle } = await (useVite ? import('@nuxt/vite-builder') : import('@nuxt/webpack-builder'))
|
||||||
return bundle(nuxt)
|
return bundle(nuxt)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getBuilder (nuxt: Nuxt) {
|
|
||||||
return new Builder(nuxt)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function build (nuxt: Nuxt) {
|
|
||||||
return getBuilder(nuxt).build()
|
|
||||||
}
|
|
||||||
|
@ -49,6 +49,7 @@ export async function loadNuxt (opts: LoadNuxtOptions): Promise<Nuxt> {
|
|||||||
options.appDir = appDir
|
options.appDir = appDir
|
||||||
options._majorVersion = 3
|
options._majorVersion = 3
|
||||||
options.alias.vue = require.resolve('vue/dist/vue.esm-bundler.js')
|
options.alias.vue = require.resolve('vue/dist/vue.esm-bundler.js')
|
||||||
|
options.buildModules.push(require.resolve('@nuxt/pages/module'))
|
||||||
|
|
||||||
const nuxt = createNuxt(options)
|
const nuxt = createNuxt(options)
|
||||||
|
|
||||||
|
@ -1,26 +0,0 @@
|
|||||||
|
|
||||||
import { NuxtApp } from './app'
|
|
||||||
import { Builder } from './builder'
|
|
||||||
import { resolveFiles } from './utils'
|
|
||||||
|
|
||||||
export interface NuxtPlugin {
|
|
||||||
src: string
|
|
||||||
mode: 'server' | 'client' | 'all'
|
|
||||||
}
|
|
||||||
|
|
||||||
const MODES_REGEX = /\.(server|client)(\.\w+)*$/
|
|
||||||
const getPluginMode = (src: string) => {
|
|
||||||
const [, mode = 'all'] = src.match(MODES_REGEX) || []
|
|
||||||
|
|
||||||
return mode as NuxtPlugin['mode']
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function resolvePlugins (builder: Builder, app: NuxtApp) {
|
|
||||||
const plugins = await resolveFiles(builder, 'plugins/**/*.{js,ts}', app.dir)
|
|
||||||
|
|
||||||
return plugins.map(src => ({
|
|
||||||
src,
|
|
||||||
mode: getPluginMode(src)
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,58 +0,0 @@
|
|||||||
import fsExtra from 'fs-extra'
|
|
||||||
import globby from 'globby'
|
|
||||||
import lodashTemplate from 'lodash/template'
|
|
||||||
import { join, relative, dirname } from 'upath'
|
|
||||||
|
|
||||||
import * as nxt from './utils/nxt'
|
|
||||||
import type { Builder } from './builder'
|
|
||||||
|
|
||||||
export interface NuxtTemplate {
|
|
||||||
src: string // Absolute path to source file
|
|
||||||
path: string // Relative path of destination
|
|
||||||
data?: any
|
|
||||||
}
|
|
||||||
|
|
||||||
export function templateData (builder: Builder) {
|
|
||||||
return {
|
|
||||||
globals: builder.globals,
|
|
||||||
app: builder.app,
|
|
||||||
nuxtOptions: builder.nuxt.options,
|
|
||||||
nxt
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function compileTemplate (tmpl: NuxtTemplate, destDir: string) {
|
|
||||||
const srcContents = await fsExtra.readFile(tmpl.src, 'utf-8')
|
|
||||||
let compiledSrc: string
|
|
||||||
try {
|
|
||||||
compiledSrc = lodashTemplate(srcContents, {})(tmpl.data)
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Error compiling template: ', tmpl)
|
|
||||||
throw err
|
|
||||||
}
|
|
||||||
const dest = join(destDir, tmpl.path)
|
|
||||||
// consola.log('Compile template', dest)
|
|
||||||
await fsExtra.mkdirp(dirname(dest))
|
|
||||||
await fsExtra.writeFile(dest, compiledSrc)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function compileTemplates (templates: NuxtTemplate[], destDir: string) {
|
|
||||||
return Promise.all(templates.map(t => compileTemplate(t, destDir)))
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function scanTemplates (dir: string, data?: Record<string, any>) {
|
|
||||||
const templateFiles = (await globby(join(dir, '/**')))
|
|
||||||
|
|
||||||
return templateFiles.filter(src => !src.endsWith('.d.ts')).map(src => ({
|
|
||||||
src,
|
|
||||||
path: relative(dir, src),
|
|
||||||
data
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
export function watchTemplate (template: NuxtTemplate, _watcher: any, _cb: () => any) {
|
|
||||||
template.data = new Proxy(template.data, {
|
|
||||||
// TODO: deep watch option changes
|
|
||||||
})
|
|
||||||
// TODO: Watch fs changes
|
|
||||||
}
|
|
@ -1,8 +1,6 @@
|
|||||||
import { basename, extname } from 'path'
|
import { basename, extname } from 'path'
|
||||||
import hash from 'hash-sum'
|
import hash from 'hash-sum'
|
||||||
import { camelCase } from 'scule'
|
import { camelCase } from 'scule'
|
||||||
import { NuxtRoute } from '../pages'
|
|
||||||
// NXT is a set of utils for serializing JavaScript data to JS code
|
|
||||||
|
|
||||||
export const serialize = data => JSON.stringify(data, null, 2).replace(/"{(.+)}"/g, '$1')
|
export const serialize = data => JSON.stringify(data, null, 2).replace(/"{(.+)}"/g, '$1')
|
||||||
|
|
||||||
@ -19,25 +17,3 @@ export const importSources = (sources: string | string[], { lazy = false } = {})
|
|||||||
return `import ${importName(src)} from '${src}'`
|
return `import ${importName(src)} from '${src}'`
|
||||||
}).join('\n')
|
}).join('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SerializedRoute {
|
|
||||||
name?: string
|
|
||||||
path: string
|
|
||||||
children: SerializedRoute[]
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
__file: string
|
|
||||||
component: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export const serializeRoute = (route: NuxtRoute): SerializedRoute => {
|
|
||||||
return {
|
|
||||||
name: route.name,
|
|
||||||
path: route.path,
|
|
||||||
children: route.children.map(serializeRoute),
|
|
||||||
// TODO: avoid exposing to prod, using process.env.NODE_ENV ?
|
|
||||||
__file: route.file,
|
|
||||||
component: `{() => import('${route.file}' /* webpackChunkName: '${route.name}' */)}`
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,79 +0,0 @@
|
|||||||
import path from 'path'
|
|
||||||
import fs from 'fs-extra'
|
|
||||||
import ignore from 'ignore'
|
|
||||||
|
|
||||||
type IgnoreInstance = ReturnType<typeof ignore>
|
|
||||||
type IgnoreOptions = Parameters<typeof ignore>[0]
|
|
||||||
|
|
||||||
interface Options {
|
|
||||||
rootDir: string
|
|
||||||
ignore?: IgnoreInstance
|
|
||||||
ignoreArray?: Array<string | IgnoreInstance>
|
|
||||||
ignoreOptions?: IgnoreOptions
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class Ignore {
|
|
||||||
rootDir: string
|
|
||||||
ignore?: IgnoreInstance
|
|
||||||
ignoreArray?: Array<string | IgnoreInstance>
|
|
||||||
ignoreFile?: string
|
|
||||||
ignoreOptions?: IgnoreOptions
|
|
||||||
|
|
||||||
constructor ({ ignoreArray, ignoreOptions, rootDir }: Options) {
|
|
||||||
this.rootDir = rootDir
|
|
||||||
this.ignoreOptions = ignoreOptions
|
|
||||||
this.ignoreArray = ignoreArray
|
|
||||||
this.addIgnoresRules()
|
|
||||||
}
|
|
||||||
|
|
||||||
static get IGNORE_FILENAME () {
|
|
||||||
return '.nuxtignore'
|
|
||||||
}
|
|
||||||
|
|
||||||
findIgnoreFile () {
|
|
||||||
if (!this.ignoreFile) {
|
|
||||||
const ignoreFile = path.resolve(this.rootDir, Ignore.IGNORE_FILENAME)
|
|
||||||
if (fs.existsSync(ignoreFile) && fs.statSync(ignoreFile).isFile()) {
|
|
||||||
this.ignoreFile = ignoreFile
|
|
||||||
this.ignore = ignore(this.ignoreOptions)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return this.ignoreFile
|
|
||||||
}
|
|
||||||
|
|
||||||
readIgnoreFile () {
|
|
||||||
if (this.findIgnoreFile() && this.ignoreFile) {
|
|
||||||
return fs.readFileSync(this.ignoreFile, 'utf8')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
addIgnoresRules () {
|
|
||||||
const content = this.readIgnoreFile()
|
|
||||||
if (!this.ignore) {
|
|
||||||
this.ignore = ignore(this.ignoreOptions)
|
|
||||||
}
|
|
||||||
if (content) {
|
|
||||||
this.ignore.add(content)
|
|
||||||
}
|
|
||||||
if (this.ignoreArray && this.ignoreArray.length > 0) {
|
|
||||||
this.ignore.add(this.ignoreArray)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
filter (paths: string[] = []) {
|
|
||||||
if (this.ignore) {
|
|
||||||
return this.ignore.filter(([] as string[]).concat(paths))
|
|
||||||
}
|
|
||||||
return paths
|
|
||||||
}
|
|
||||||
|
|
||||||
ignores (pathname: string) {
|
|
||||||
return this.ignore && this.ignore.ignores(pathname)
|
|
||||||
}
|
|
||||||
|
|
||||||
reload () {
|
|
||||||
delete this.ignore
|
|
||||||
delete this.ignoreFile
|
|
||||||
this.addIgnoresRules()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
import { resolve } from 'path'
|
|
||||||
import globby from 'globby'
|
|
||||||
import { Builder } from '../builder'
|
|
||||||
|
|
||||||
// TODO: move to core resolver
|
|
||||||
export async function resolveFiles (builder: Builder, pattern: string, srcDir: string) {
|
|
||||||
const { nuxt } = builder
|
|
||||||
|
|
||||||
return builder.ignore.filter(await globby(pattern, {
|
|
||||||
cwd: srcDir,
|
|
||||||
followSymbolicLinks: nuxt.options.build.followSymlinks
|
|
||||||
})).map(p => resolve(srcDir, p))
|
|
||||||
}
|
|
@ -1,51 +0,0 @@
|
|||||||
import chokidar, { WatchOptions } from 'chokidar'
|
|
||||||
import defu from 'defu'
|
|
||||||
import consola from 'consola'
|
|
||||||
|
|
||||||
import Ignore from './utils/ignore'
|
|
||||||
|
|
||||||
export type WatchEvent = 'add' | 'addDir' | 'change' | 'unlink' | 'unlinkDir'
|
|
||||||
|
|
||||||
export type WatchCallback = (event: WatchEvent, path: string) => void
|
|
||||||
export type WatchFilter = (event: WatchEvent, path: string) => boolean | null
|
|
||||||
|
|
||||||
export function createWatcher (
|
|
||||||
pattern: string,
|
|
||||||
options?: WatchOptions,
|
|
||||||
ignore?: Ignore
|
|
||||||
) {
|
|
||||||
const opts = defu(options!, {
|
|
||||||
ignored: [],
|
|
||||||
ignoreInitial: true
|
|
||||||
})
|
|
||||||
const watcher = chokidar.watch(pattern, opts)
|
|
||||||
const watchAll = (cb: WatchCallback, filter?: WatchFilter) => {
|
|
||||||
watcher.on('all', (event, path: string) => {
|
|
||||||
if (ignore && ignore.ignores(path)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (!filter || filter(event, path)) {
|
|
||||||
cb(event, path)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const watch = (pattern: string | RegExp, cb: WatchCallback, events?: WatchEvent[]) =>
|
|
||||||
watchAll(cb, (event, path) => path.match(pattern) && (!events || events.includes(event)))
|
|
||||||
|
|
||||||
const debug = (tag: string = '[Watcher]') => {
|
|
||||||
consola.log(tag, 'Watching ', pattern)
|
|
||||||
watchAll((event, path) => {
|
|
||||||
consola.log(tag, event, path)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
watchAll,
|
|
||||||
watch,
|
|
||||||
debug,
|
|
||||||
close: () => watcher.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export type Watcher = ReturnType<typeof createWatcher>
|
|
12
packages/pages/build.config.ts
Normal file
12
packages/pages/build.config.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { defineBuildConfig } from 'unbuild'
|
||||||
|
|
||||||
|
export default defineBuildConfig({
|
||||||
|
declaration: true,
|
||||||
|
entries: [
|
||||||
|
'src/module',
|
||||||
|
{ input: 'src/runtime/', outDir: 'dist/runtime', format: 'esm' }
|
||||||
|
],
|
||||||
|
dependencies: [
|
||||||
|
'vue-router'
|
||||||
|
]
|
||||||
|
})
|
1
packages/pages/module.js
Normal file
1
packages/pages/module.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
module.exports = require('./dist/module')
|
24
packages/pages/package.json
Normal file
24
packages/pages/package.json
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"name": "@nuxt/pages",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"repository": "nuxt/framework",
|
||||||
|
"license": "MIT",
|
||||||
|
"types": "./dist/module.d.ts",
|
||||||
|
"files": [
|
||||||
|
"dist",
|
||||||
|
"module.js"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"prepack": "unbuild"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@nuxt/kit": "^0.5.3",
|
||||||
|
"globby": "^11.0.3",
|
||||||
|
"ufo": "^0.7.5",
|
||||||
|
"upath": "^2.0.1",
|
||||||
|
"vue-router": "^4.0.6"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"unbuild": "^0.2.3"
|
||||||
|
}
|
||||||
|
}
|
59
packages/pages/src/module.ts
Normal file
59
packages/pages/src/module.ts
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import { existsSync } from 'fs'
|
||||||
|
import { defineNuxtModule } from '@nuxt/kit'
|
||||||
|
import { resolve } from 'upath'
|
||||||
|
import { resolvePagesRoutes } from './utils'
|
||||||
|
|
||||||
|
export default defineNuxtModule({
|
||||||
|
name: 'router',
|
||||||
|
setup (_options, nuxt) {
|
||||||
|
const runtimeDir = resolve(__dirname, 'runtime')
|
||||||
|
const pagesDir = resolve(nuxt.options.srcDir, nuxt.options.dir.pages)
|
||||||
|
const routerPlugin = resolve(runtimeDir, 'router')
|
||||||
|
|
||||||
|
nuxt.hook('builder:watch', async (event, path) => {
|
||||||
|
// Regenerate templates when adding or removing pages (plugin and routes)
|
||||||
|
if (event !== 'change' && path.startsWith('pages/')) {
|
||||||
|
await nuxt.callHook('builder:generateApp')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
nuxt.hook('app:resolve', (app) => {
|
||||||
|
if (!existsSync(pagesDir)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
app.plugins.push({ src: routerPlugin })
|
||||||
|
if (app.main.includes('app.tutorial')) {
|
||||||
|
app.main = resolve(runtimeDir, 'app.vue')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
nuxt.hook('app:templates', async (app) => {
|
||||||
|
if (!existsSync(pagesDir)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve routes
|
||||||
|
const routes = await resolvePagesRoutes(nuxt)
|
||||||
|
|
||||||
|
// Add 404 page is not added
|
||||||
|
const page404 = routes.find(route => route.name === '404')
|
||||||
|
if (!page404) {
|
||||||
|
routes.push({
|
||||||
|
name: '404',
|
||||||
|
path: '/:catchAll(.*)*',
|
||||||
|
file: resolve(runtimeDir, '404.vue'),
|
||||||
|
children: []
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add routes.js
|
||||||
|
app.templates.push({
|
||||||
|
path: 'routes.js',
|
||||||
|
compile: () => {
|
||||||
|
const serializedRoutes = routes.map(route => ({ ...route, component: `{() => import('${route.file}')}` }))
|
||||||
|
return `export default ${JSON.stringify(serializedRoutes, null, 2).replace(/"{(.+)}"/g, '$1')}`
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
@ -1,6 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<!-- TODO: Move this page to @nuxt/nice -->
|
|
||||||
404 | Page Not Found
|
404 | Page Not Found
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
@ -1,3 +1,3 @@
|
|||||||
<template>
|
<template>
|
||||||
<Nuxt />
|
<RouterView />
|
||||||
</template>
|
</template>
|
@ -5,11 +5,11 @@ import {
|
|||||||
createMemoryHistory,
|
createMemoryHistory,
|
||||||
RouterLink
|
RouterLink
|
||||||
} from 'vue-router'
|
} from 'vue-router'
|
||||||
import NuxtPage from './NuxtPage.vue'
|
// @ts-ignore
|
||||||
|
import NuxtPage from './page.vue'
|
||||||
import type { Plugin } from '@nuxt/app'
|
import type { Plugin } from '@nuxt/app'
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import routes from '#build/routes'
|
import routes from '#build/routes'
|
||||||
// @ts-ignore
|
|
||||||
|
|
||||||
export default <Plugin> function router (nuxt) {
|
export default <Plugin> function router (nuxt) {
|
||||||
const { app } = nuxt
|
const { app } = nuxt
|
@ -1,10 +1,7 @@
|
|||||||
import { resolve, extname, relative } from 'path'
|
import { extname, relative, resolve } from 'upath'
|
||||||
import { encodePath } from 'ufo'
|
import { encodePath } from 'ufo'
|
||||||
import { NuxtApp } from './app'
|
import { Nuxt, resolveFiles } from '@nuxt/kit'
|
||||||
import { Builder } from './builder'
|
|
||||||
import { resolveFiles } from './utils'
|
|
||||||
|
|
||||||
// Check if name has [slug]
|
|
||||||
export interface NuxtRoute {
|
export interface NuxtRoute {
|
||||||
name?: string
|
name?: string
|
||||||
path: string
|
path: string
|
||||||
@ -12,14 +9,12 @@ export interface NuxtRoute {
|
|||||||
children: NuxtRoute[]
|
children: NuxtRoute[]
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: should be const
|
|
||||||
enum SegmentParserState {
|
enum SegmentParserState {
|
||||||
initial,
|
initial,
|
||||||
static,
|
static,
|
||||||
dynamic,
|
dynamic,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: should be const
|
|
||||||
enum SegmentTokenType {
|
enum SegmentTokenType {
|
||||||
static,
|
static,
|
||||||
dynamic,
|
dynamic,
|
||||||
@ -30,19 +25,17 @@ interface SegmentToken {
|
|||||||
value: string
|
value: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function resolvePagesRoutes (builder: Builder, app: NuxtApp) {
|
export async function resolvePagesRoutes (nuxt: Nuxt) {
|
||||||
const pagesDir = resolve(app.dir, app.pages!.dir)
|
const pagesDir = resolve(nuxt.options.srcDir, nuxt.options.dir.pages)
|
||||||
const pagesPattern = `${app.pages!.dir}/**/*{${app.extensions.join(',')}}`
|
const files = await resolveFiles(pagesDir, `**/*{${nuxt.options.extensions.join(',')}}`)
|
||||||
const files = await resolveFiles(builder, pagesPattern, app.dir)
|
|
||||||
|
|
||||||
// Sort to make sure parent are listed first
|
// Sort to make sure parent are listed first
|
||||||
return generateRoutesFromFiles(files.sort(), pagesDir)
|
files.sort()
|
||||||
|
|
||||||
|
return generateRoutesFromFiles(files, pagesDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function generateRoutesFromFiles (
|
export function generateRoutesFromFiles (files: string[], pagesDir: string): NuxtRoute[] {
|
||||||
files: string[],
|
|
||||||
pagesDir: string
|
|
||||||
): NuxtRoute[] {
|
|
||||||
const routes: NuxtRoute[] = []
|
const routes: NuxtRoute[] = []
|
||||||
|
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
@ -57,7 +50,7 @@ export function generateRoutesFromFiles (
|
|||||||
children: []
|
children: []
|
||||||
}
|
}
|
||||||
|
|
||||||
// array where routes should be added, useful when adding child routes
|
// Array where routes should be added, useful when adding child routes
|
||||||
let parent = routes
|
let parent = routes
|
||||||
|
|
||||||
for (let i = 0; i < segments.length; i++) {
|
for (let i = 0; i < segments.length; i++) {
|
@ -1,7 +1,6 @@
|
|||||||
import { resolve } from 'path'
|
import { resolve } from 'path'
|
||||||
import * as vite from 'vite'
|
import * as vite from 'vite'
|
||||||
import vuePlugin from '@vitejs/plugin-vue'
|
import vuePlugin from '@vitejs/plugin-vue'
|
||||||
import { watch } from 'chokidar'
|
|
||||||
import { mkdirp, writeFile } from 'fs-extra'
|
import { mkdirp, writeFile } from 'fs-extra'
|
||||||
import debounce from 'debounce'
|
import debounce from 'debounce'
|
||||||
import consola from 'consola'
|
import consola from 'consola'
|
||||||
@ -29,7 +28,7 @@ export async function buildServer (ctx: ViteBuildContext) {
|
|||||||
outDir: 'dist/server',
|
outDir: 'dist/server',
|
||||||
ssr: true,
|
ssr: true,
|
||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
input: resolve(ctx.nuxt.options.buildDir, 'entry.server.mjs'),
|
input: resolve(ctx.nuxt.options.buildDir, 'entry.mjs'),
|
||||||
onwarn (warning, rollupWarn) {
|
onwarn (warning, rollupWarn) {
|
||||||
if (!['UNUSED_EXTERNAL_IMPORT'].includes(warning.code)) {
|
if (!['UNUSED_EXTERNAL_IMPORT'].includes(warning.code)) {
|
||||||
rollupWarn(warning)
|
rollupWarn(warning)
|
||||||
@ -48,7 +47,7 @@ export async function buildServer (ctx: ViteBuildContext) {
|
|||||||
const serverDist = resolve(ctx.nuxt.options.buildDir, 'dist/server')
|
const serverDist = resolve(ctx.nuxt.options.buildDir, 'dist/server')
|
||||||
await mkdirp(serverDist)
|
await mkdirp(serverDist)
|
||||||
|
|
||||||
await writeFile(resolve(serverDist, 'server.js'), 'module.exports = require("./entry.server")', 'utf8')
|
await writeFile(resolve(serverDist, 'server.js'), 'module.exports = require("./entry")', 'utf8')
|
||||||
await writeFile(resolve(serverDist, 'client.manifest.json'), 'false', 'utf8')
|
await writeFile(resolve(serverDist, 'client.manifest.json'), 'false', 'utf8')
|
||||||
|
|
||||||
const onBuild = () => ctx.nuxt.callHook('build:resources', wpfs)
|
const onBuild = () => ctx.nuxt.callHook('build:resources', wpfs)
|
||||||
@ -67,19 +66,6 @@ export async function buildServer (ctx: ViteBuildContext) {
|
|||||||
|
|
||||||
await build()
|
await build()
|
||||||
|
|
||||||
const watcher = watch([
|
ctx.nuxt.hook('builder:watch', () => build())
|
||||||
ctx.nuxt.options.buildDir,
|
ctx.nuxt.hook('app:templatesGenerated', () => build())
|
||||||
ctx.nuxt.options.srcDir,
|
|
||||||
ctx.nuxt.options.rootDir
|
|
||||||
], {
|
|
||||||
ignored: [
|
|
||||||
'**/dist/server/**'
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
watcher.on('change', () => build())
|
|
||||||
|
|
||||||
ctx.nuxt.hook('close', async () => {
|
|
||||||
await watcher.close()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
@ -64,7 +64,7 @@ export async function bundle (nuxt: Nuxt) {
|
|||||||
|
|
||||||
nuxt.hook('vite:serverCreated', (server: vite.ViteDevServer) => {
|
nuxt.hook('vite:serverCreated', (server: vite.ViteDevServer) => {
|
||||||
const start = Date.now()
|
const start = Date.now()
|
||||||
warmupViteServer(server, ['/entry.client.mjs']).then(() => {
|
warmupViteServer(server, ['/entry.mjs']).then(() => {
|
||||||
consola.info(`Vite warmed up in ${Date.now() - start}ms`)
|
consola.info(`Vite warmed up in ${Date.now() - start}ms`)
|
||||||
}).catch(consola.error)
|
}).catch(consola.error)
|
||||||
})
|
})
|
||||||
|
@ -23,7 +23,7 @@ function baseConfig (ctx: WebpackConfigContext) {
|
|||||||
|
|
||||||
ctx.config = {
|
ctx.config = {
|
||||||
name: ctx.name,
|
name: ctx.name,
|
||||||
entry: { app: [resolve(options.buildDir, `entry.${ctx.name}`)] },
|
entry: { app: [resolve(options.buildDir, 'entry')] },
|
||||||
module: { rules: [] },
|
module: { rules: [] },
|
||||||
plugins: [],
|
plugins: [],
|
||||||
externals: [],
|
externals: [],
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
...
|
Hello
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
16
yarn.lock
16
yarn.lock
@ -1741,6 +1741,19 @@ __metadata:
|
|||||||
languageName: unknown
|
languageName: unknown
|
||||||
linkType: soft
|
linkType: soft
|
||||||
|
|
||||||
|
"@nuxt/pages@^0.1.0, @nuxt/pages@workspace:packages/pages":
|
||||||
|
version: 0.0.0-use.local
|
||||||
|
resolution: "@nuxt/pages@workspace:packages/pages"
|
||||||
|
dependencies:
|
||||||
|
"@nuxt/kit": ^0.5.3
|
||||||
|
globby: ^11.0.3
|
||||||
|
ufo: ^0.7.5
|
||||||
|
unbuild: ^0.2.3
|
||||||
|
upath: ^2.0.1
|
||||||
|
vue-router: ^4.0.6
|
||||||
|
languageName: unknown
|
||||||
|
linkType: soft
|
||||||
|
|
||||||
"@nuxt/vite-builder@^0.3.3, @nuxt/vite-builder@workspace:packages/vite":
|
"@nuxt/vite-builder@^0.3.3, @nuxt/vite-builder@workspace:packages/vite":
|
||||||
version: 0.0.0-use.local
|
version: 0.0.0-use.local
|
||||||
resolution: "@nuxt/vite-builder@workspace:packages/vite"
|
resolution: "@nuxt/vite-builder@workspace:packages/vite"
|
||||||
@ -10663,6 +10676,7 @@ __metadata:
|
|||||||
"@nuxt/app": ^0.3.3
|
"@nuxt/app": ^0.3.3
|
||||||
"@nuxt/kit": ^0.5.3
|
"@nuxt/kit": ^0.5.3
|
||||||
"@nuxt/nitro": ^0.6.3
|
"@nuxt/nitro": ^0.6.3
|
||||||
|
"@nuxt/pages": ^0.1.0
|
||||||
"@nuxt/vite-builder": ^0.3.3
|
"@nuxt/vite-builder": ^0.3.3
|
||||||
"@nuxt/webpack-builder": ^0.3.4
|
"@nuxt/webpack-builder": ^0.3.4
|
||||||
"@types/fs-extra": ^9.0.11
|
"@types/fs-extra": ^9.0.11
|
||||||
@ -14864,7 +14878,7 @@ typescript@^4.2.4:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"vue-router@npm:^4.0.8":
|
"vue-router@npm:^4.0.6, vue-router@npm:^4.0.8":
|
||||||
version: 4.0.8
|
version: 4.0.8
|
||||||
resolution: "vue-router@npm:4.0.8"
|
resolution: "vue-router@npm:4.0.8"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
Loading…
Reference in New Issue
Block a user