Skip to content
This repository has been archived by the owner on Apr 6, 2023. It is now read-only.

feat(kit): add updateTemplates utility #8413

Merged
merged 3 commits into from Oct 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/content/3.api/4.advanced/2.kit.md
Expand Up @@ -68,6 +68,7 @@ description: Nuxt Kit provides composable utilities to help interacting with Nux
[source code](https://github.com/nuxt/framework/blob/main/packages/kit/src/template.ts)

- `addTemplate(templateOptions)`
- `updateTemplates({ filter?: ResolvedNuxtTemplate => boolean })`

### Nitro

Expand Down
9 changes: 9 additions & 0 deletions packages/kit/src/template.ts
Expand Up @@ -71,3 +71,12 @@ export function normalizeTemplate (template: NuxtTemplate<any> | string): Resolv

return template as ResolvedNuxtTemplate<any>
}

/**
* Trigger rebuilding Nuxt templates
*
* You can pass a filter within the options to selectively regenerate a subset of templates.
*/
export function updateTemplates (options?: { filter?: (template: ResolvedNuxtTemplate<any>) => boolean }) {
return useNuxt().hooks.callHook('builder:generateApp', options)
}
15 changes: 11 additions & 4 deletions packages/nuxt/src/components/module.ts
@@ -1,6 +1,6 @@
import { statSync } from 'node:fs'
import { relative, resolve } from 'pathe'
import { defineNuxtModule, resolveAlias, addTemplate, addPluginTemplate } from '@nuxt/kit'
import { defineNuxtModule, resolveAlias, addTemplate, addPluginTemplate, updateTemplates } from '@nuxt/kit'
import type { Component, ComponentsDir, ComponentsOptions } from '@nuxt/schema'
import { distDir } from '../dirs'
import { componentsPluginTemplate, componentsTemplate, componentsTypeTemplate } from './templates'
Expand Down Expand Up @@ -122,12 +122,12 @@ export default defineNuxtModule<ComponentsOptions>({

nuxt.hook('vite:extendConfig', (config, { isClient }) => {
const mode = isClient ? 'client' : 'server'
;(config.resolve!.alias as any)['#components'] = resolve(nuxt.options.buildDir, `components.${mode}.mjs`)
; (config.resolve!.alias as any)['#components'] = resolve(nuxt.options.buildDir, `components.${mode}.mjs`)
})
nuxt.hook('webpack:config', (configs) => {
for (const config of configs) {
const mode = config.name === 'server' ? 'server' : 'client'
;(config.resolve!.alias as any)['#components'] = resolve(nuxt.options.buildDir, `components.${mode}.mjs`)
; (config.resolve!.alias as any)['#components'] = resolve(nuxt.options.buildDir, `components.${mode}.mjs`)
}
})

Expand Down Expand Up @@ -173,7 +173,14 @@ export default defineNuxtModule<ComponentsOptions>({
}
const fPath = resolve(nuxt.options.srcDir, path)
if (componentDirs.find(dir => fPath.startsWith(dir.path))) {
await nuxt.callHook('builder:generateApp')
await updateTemplates({
filter: template => [
'components.plugin.mjs',
'components.d.ts',
'components.server.mjs',
'components.client.mjs'
].includes(template.filename)
})
}
})

Expand Down
44 changes: 23 additions & 21 deletions packages/nuxt/src/core/app.ts
@@ -1,7 +1,7 @@
import { promises as fsp } from 'node:fs'
import { dirname, resolve } from 'pathe'
import defu from 'defu'
import type { Nuxt, NuxtApp, NuxtPlugin, NuxtTemplate } from '@nuxt/schema'
import type { Nuxt, NuxtApp, NuxtPlugin, NuxtTemplate, ResolvedNuxtTemplate } from '@nuxt/schema'
import { findPath, resolveFiles, normalizePlugin, normalizeTemplate, compileTemplate, templateUtils, tryResolveModule, resolvePath, resolveAlias } from '@nuxt/kit'

import * as defaultTemplates from './templates'
Expand All @@ -16,7 +16,7 @@ export function createApp (nuxt: Nuxt, options: Partial<NuxtApp> = {}): NuxtApp
} as unknown as NuxtApp) as NuxtApp
}

export async function generateApp (nuxt: Nuxt, app: NuxtApp) {
export async function generateApp (nuxt: Nuxt, app: NuxtApp, options: { filter?: (template: ResolvedNuxtTemplate<any>) => boolean } = {}) {
// Resolve app
await resolveApp(nuxt, app)

Expand All @@ -31,25 +31,27 @@ export async function generateApp (nuxt: Nuxt, app: NuxtApp) {

// Compile templates into vfs
const templateContext = { utils: templateUtils, nuxt, app }
await Promise.all(app.templates.map(async (template) => {
const contents = await compileTemplate(template, templateContext)

const fullPath = template.dst || resolve(nuxt.options.buildDir, template.filename!)
nuxt.vfs[fullPath] = contents

const aliasPath = '#build/' + template.filename!.replace(/\.\w+$/, '')
nuxt.vfs[aliasPath] = contents

// In case a non-normalized absolute path is called for on Windows
if (process.platform === 'win32') {
nuxt.vfs[fullPath.replace(/\//g, '\\')] = contents
}

if (template.write) {
await fsp.mkdir(dirname(fullPath), { recursive: true })
await fsp.writeFile(fullPath, contents, 'utf8')
}
}))
await Promise.all((app.templates as Array<ReturnType<typeof normalizeTemplate>>)
.filter(template => !options.filter || options.filter(template))
.map(async (template) => {
const contents = await compileTemplate(template, templateContext)

const fullPath = template.dst || resolve(nuxt.options.buildDir, template.filename!)
nuxt.vfs[fullPath] = contents

const aliasPath = '#build/' + template.filename!.replace(/\.\w+$/, '')
nuxt.vfs[aliasPath] = contents

// In case a non-normalized absolute path is called for on Windows
if (process.platform === 'win32') {
nuxt.vfs[fullPath.replace(/\//g, '\\')] = contents
}

if (template.write) {
await fsp.mkdir(dirname(fullPath), { recursive: true })
await fsp.writeFile(fullPath, contents, 'utf8')
}
}))

await nuxt.callHook('app:templatesGenerated', app)
}
Expand Down
6 changes: 5 additions & 1 deletion packages/nuxt/src/core/builder.ts
Expand Up @@ -23,7 +23,11 @@ export async function build (nuxt: Nuxt) {
await generateApp()
}
})
nuxt.hook('builder:generateApp', generateApp)
nuxt.hook('builder:generateApp', (options) => {
// Bypass debounce if we are selectively invalidating templates
if (options) { return _generateApp(nuxt, app, options) }
return generateApp()
})
}

await nuxt.callHook('build:before', { nuxt }, nuxt.options.build)
Expand Down
12 changes: 9 additions & 3 deletions packages/nuxt/src/imports/module.ts
@@ -1,4 +1,4 @@
import { addVitePlugin, addWebpackPlugin, defineNuxtModule, addTemplate, resolveAlias, useNuxt, addPluginTemplate, logger } from '@nuxt/kit'
import { addVitePlugin, addWebpackPlugin, defineNuxtModule, addTemplate, resolveAlias, useNuxt, addPluginTemplate, logger, updateTemplates } from '@nuxt/kit'
import { isAbsolute, join, relative, resolve, normalize } from 'pathe'
import { createUnimport, Import, scanDirExports, toImports, Unimport } from 'unimport'
import { ImportsOptions, ImportPresetWithDeprecation } from '@nuxt/schema'
Expand Down Expand Up @@ -77,7 +77,6 @@ export default defineNuxtModule<Partial<ImportsOptions>>({
nuxt.options.alias['#imports'] = join(nuxt.options.buildDir, 'imports')

// Transpile and injection
// @ts-ignore temporary disabled due to #746
if (nuxt.options.dev && options.global) {
// Add all imports to globalThis in development mode
addPluginTemplate({
Expand Down Expand Up @@ -117,10 +116,17 @@ export default defineNuxtModule<Partial<ImportsOptions>>({
})

// Watch composables/ directory
const templates = [
'types/imports.d.ts',
'imports.d.ts',
'imports.mjs'
]
nuxt.hook('builder:watch', async (_, path) => {
const _resolved = resolve(nuxt.options.srcDir, path)
if (composablesDirs.find(dir => _resolved.startsWith(dir))) {
await nuxt.callHook('builder:generateApp')
await updateTemplates({
filter: template => templates.includes(template.filename)
})
}
})

Expand Down
4 changes: 2 additions & 2 deletions packages/nuxt/src/pages/module.ts
@@ -1,5 +1,5 @@
import { existsSync } from 'node:fs'
import { defineNuxtModule, addTemplate, addPlugin, addVitePlugin, addWebpackPlugin, findPath, addComponent } from '@nuxt/kit'
import { defineNuxtModule, addTemplate, addPlugin, addVitePlugin, addWebpackPlugin, findPath, addComponent, updateTemplates } from '@nuxt/kit'
import { relative, resolve } from 'pathe'
import { genString, genImport, genObjectFromRawEntries } from 'knitwork'
import escapeRE from 'escape-string-regexp'
Expand Down Expand Up @@ -43,7 +43,7 @@ export default defineNuxtModule({

const pathPattern = new RegExp(`(^|\\/)(${dirs.map(escapeRE).join('|')})/`)
if (event !== 'change' && path.match(pathPattern)) {
await nuxt.callHook('builder:generateApp')
await updateTemplates()
}
})

Expand Down
8 changes: 6 additions & 2 deletions packages/schema/src/types/hooks.ts
Expand Up @@ -6,7 +6,7 @@ import type { InlineConfig as ViteInlineConfig, ViteDevServer } from 'vite'
import type { Manifest } from 'vue-bundle-renderer'
import type { EventHandler } from 'h3'
import type { ModuleContainer } from './module'
import type { NuxtTemplate, Nuxt, NuxtApp } from './nuxt'
import type { NuxtTemplate, Nuxt, NuxtApp, ResolvedNuxtTemplate } from './nuxt'
import type { Preset as ImportPreset, Import } from 'unimport'
import type { NuxtConfig, NuxtOptions } from './config'
import type { Nitro, NitroConfig } from 'nitropack'
Expand Down Expand Up @@ -66,6 +66,10 @@ export interface ImportPresetWithDeprecation extends ImportPreset {
names?: string[]
}

export interface GenerateAppOptions {
filter?: (template: ResolvedNuxtTemplate<any>) => boolean
}

export interface NuxtHooks {
// Kit
'kit:compatibility': (compatibility: NuxtCompatibility, issues: NuxtCompatibilityIssues) => HookResult
Expand All @@ -74,7 +78,7 @@ export interface NuxtHooks {
'app:resolve': (app: NuxtApp) => HookResult
'app:templates': (app: NuxtApp) => HookResult
'app:templatesGenerated': (app: NuxtApp) => HookResult
'builder:generateApp': () => HookResult
'builder:generateApp': (options?: GenerateAppOptions) => HookResult
'pages:extend': (pages: NuxtPage[]) => HookResult
'build:manifest': (manifest: Manifest) => HookResult
'server:devHandler': (handler: EventHandler) => HookResult
Expand Down