2021-10-02 16:01:17 +00:00
|
|
|
import { promises as fsp } from 'fs'
|
2022-03-14 10:47:24 +00:00
|
|
|
import { dirname, resolve, basename, extname } from 'pathe'
|
2020-08-19 12:38:18 +00:00
|
|
|
import defu from 'defu'
|
2022-03-14 10:47:24 +00:00
|
|
|
import { kebabCase } from 'scule'
|
2022-03-09 10:51:32 +00:00
|
|
|
import type { Nuxt, NuxtApp, NuxtPlugin } from '@nuxt/schema'
|
2022-03-11 08:22:16 +00:00
|
|
|
import { findPath, resolveFiles, normalizePlugin, normalizeTemplate, compileTemplate, templateUtils, tryResolveModule } from '@nuxt/kit'
|
2021-09-21 14:55:07 +00:00
|
|
|
|
2021-10-13 20:41:21 +00:00
|
|
|
import * as defaultTemplates from './templates'
|
2021-04-02 11:47:01 +00:00
|
|
|
|
2021-05-20 11:42:41 +00:00
|
|
|
export function createApp (nuxt: Nuxt, options: Partial<NuxtApp> = {}): NuxtApp {
|
|
|
|
return defu(options, {
|
2020-08-19 13:06:27 +00:00
|
|
|
dir: nuxt.options.srcDir,
|
|
|
|
extensions: nuxt.options.extensions,
|
2021-02-19 01:08:45 +00:00
|
|
|
plugins: [],
|
2021-07-28 11:35:24 +00:00
|
|
|
templates: []
|
2021-04-04 22:15:34 +00:00
|
|
|
} as NuxtApp)
|
2021-05-20 11:42:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export async function generateApp (nuxt: Nuxt, app: NuxtApp) {
|
|
|
|
// Resolve app
|
|
|
|
await resolveApp(nuxt, app)
|
|
|
|
|
2021-07-28 11:35:24 +00:00
|
|
|
// User templates from options.build.templates
|
2021-09-21 14:55:07 +00:00
|
|
|
app.templates = Object.values(defaultTemplates).concat(nuxt.options.build.templates)
|
2021-06-16 11:22:01 +00:00
|
|
|
|
2021-07-28 11:35:24 +00:00
|
|
|
// Extend templates with hook
|
2021-05-20 11:42:41 +00:00
|
|
|
await nuxt.callHook('app:templates', app)
|
|
|
|
|
2021-07-28 11:35:24 +00:00
|
|
|
// Normalize templates
|
|
|
|
app.templates = app.templates.map(tmpl => normalizeTemplate(tmpl))
|
|
|
|
|
2021-07-15 10:18:34 +00:00
|
|
|
// Compile templates into vfs
|
|
|
|
const templateContext = { utils: templateUtils, nuxt, app }
|
|
|
|
await Promise.all(app.templates.map(async (template) => {
|
|
|
|
const contents = await compileTemplate(template, templateContext)
|
|
|
|
|
2021-07-28 11:35:24 +00:00
|
|
|
const fullPath = template.dst || resolve(nuxt.options.buildDir, template.filename)
|
2021-07-15 10:18:34 +00:00
|
|
|
nuxt.vfs[fullPath] = contents
|
|
|
|
|
2021-07-28 11:35:24 +00:00
|
|
|
const aliasPath = '#build/' + template.filename.replace(/\.\w+$/, '')
|
2021-07-15 10:18:34 +00:00
|
|
|
nuxt.vfs[aliasPath] = contents
|
2021-08-09 18:24:52 +00:00
|
|
|
|
|
|
|
// In case a non-normalized absolute path is called for on Windows
|
|
|
|
if (process.platform === 'win32') {
|
|
|
|
nuxt.vfs[fullPath.replace(/\//g, '\\')] = contents
|
|
|
|
}
|
2021-08-09 20:54:12 +00:00
|
|
|
|
|
|
|
if (template.write) {
|
2022-02-07 10:20:01 +00:00
|
|
|
await fsp.mkdir(dirname(fullPath), { recursive: true })
|
2021-10-02 16:01:17 +00:00
|
|
|
await fsp.writeFile(fullPath, contents, 'utf8')
|
2021-08-09 20:54:12 +00:00
|
|
|
}
|
2021-07-15 10:18:34 +00:00
|
|
|
}))
|
2021-05-20 11:42:41 +00:00
|
|
|
|
|
|
|
await nuxt.callHook('app:templatesGenerated', app)
|
|
|
|
}
|
2020-08-19 12:38:18 +00:00
|
|
|
|
2021-05-20 11:42:41 +00:00
|
|
|
export async function resolveApp (nuxt: Nuxt, app: NuxtApp) {
|
|
|
|
// Resolve main (app.vue)
|
2021-10-12 12:51:41 +00:00
|
|
|
if (!app.mainComponent) {
|
2022-02-07 21:00:20 +00:00
|
|
|
app.mainComponent = await findPath(['~/App', '~/app'])
|
2020-08-17 15:25:06 +00:00
|
|
|
}
|
2021-10-12 12:51:41 +00:00
|
|
|
if (!app.mainComponent) {
|
2022-03-11 08:22:16 +00:00
|
|
|
app.mainComponent = tryResolveModule('@nuxt/ui-templates/templates/welcome.vue')
|
2021-01-18 12:22:38 +00:00
|
|
|
}
|
2021-05-20 11:42:41 +00:00
|
|
|
|
2022-03-17 10:57:02 +00:00
|
|
|
// Resolve root component
|
|
|
|
if (!app.rootComponent) {
|
|
|
|
app.rootComponent = await findPath(['~/app.root', resolve(nuxt.options.appDir, 'components/nuxt-root.vue')])
|
|
|
|
}
|
2021-10-12 12:51:41 +00:00
|
|
|
|
2022-03-11 08:22:16 +00:00
|
|
|
// Resolve error component
|
|
|
|
if (!app.errorComponent) {
|
|
|
|
app.errorComponent = (await findPath(['~/error'])) || resolve(nuxt.options.appDir, 'components/nuxt-error-page.vue')
|
|
|
|
}
|
|
|
|
|
2022-03-16 20:36:30 +00:00
|
|
|
// Resolve layouts/ from all config layers
|
2022-03-14 10:47:24 +00:00
|
|
|
app.layouts = {}
|
2022-03-16 20:36:30 +00:00
|
|
|
for (const config of nuxt.options._layers.map(layer => layer.config)) {
|
2022-03-14 13:53:31 +00:00
|
|
|
const layoutFiles = await resolveFiles(config.srcDir, `${config.dir?.layouts || 'layouts'}/*{${nuxt.options.extensions.join(',')}}`)
|
2022-03-14 10:47:24 +00:00
|
|
|
for (const file of layoutFiles) {
|
|
|
|
const name = getNameFromPath(file)
|
|
|
|
app.layouts[name] = app.layouts[name] || { name, file }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-20 11:42:41 +00:00
|
|
|
// Resolve plugins
|
2022-03-16 20:36:30 +00:00
|
|
|
app.plugins = [
|
|
|
|
...nuxt.options.plugins.map(normalizePlugin)
|
|
|
|
]
|
|
|
|
for (const config of nuxt.options._layers.map(layer => layer.config)) {
|
2022-03-09 10:51:32 +00:00
|
|
|
app.plugins.push(...[
|
2022-03-16 20:36:30 +00:00
|
|
|
...(config.plugins || []),
|
2022-03-09 10:51:32 +00:00
|
|
|
...await resolveFiles(config.srcDir, [
|
|
|
|
'plugins/*.{ts,js,mjs,cjs,mts,cts}',
|
|
|
|
'plugins/*/index.*{ts,js,mjs,cjs,mts,cts}'
|
|
|
|
])
|
|
|
|
].map(plugin => normalizePlugin(plugin as NuxtPlugin)))
|
|
|
|
}
|
2022-03-16 20:36:30 +00:00
|
|
|
app.plugins = uniqueBy(app.plugins, 'src')
|
2021-05-20 11:42:41 +00:00
|
|
|
|
|
|
|
// Extend app
|
|
|
|
await nuxt.callHook('app:resolve', app)
|
|
|
|
}
|
2022-03-14 10:47:24 +00:00
|
|
|
|
|
|
|
function getNameFromPath (path: string) {
|
|
|
|
return kebabCase(basename(path).replace(extname(path), '')).replace(/["']/g, '')
|
|
|
|
}
|
2022-03-16 20:36:30 +00:00
|
|
|
|
|
|
|
function uniqueBy (arr: any[], uniqueKey: string) {
|
|
|
|
const seen = new Set<string>()
|
|
|
|
const res = []
|
|
|
|
for (const i of arr) {
|
|
|
|
const key = i[uniqueKey]
|
|
|
|
if (seen.has(key)) { continue }
|
|
|
|
res.push(i)
|
|
|
|
seen.add(key)
|
|
|
|
}
|
|
|
|
return res
|
|
|
|
}
|