feat(kit): support templates with getContents() for nuxt 2 (#587)

This commit is contained in:
Daniel Roe 2021-09-29 11:10:46 +01:00 committed by GitHub
parent 337bd3d2b4
commit 7fdcee3252
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 90 additions and 37 deletions

View File

@ -13,6 +13,7 @@
"prepack": "unbuild"
},
"devDependencies": {
"@types/lodash": "^4.14.173",
"unbuild": "latest"
},
"dependencies": {
@ -24,6 +25,7 @@
"hash-sum": "^2.0.0",
"hookable": "^5.0.0",
"jiti": "^1.12.3",
"lodash": "^4.17.21",
"pathe": "^0.2.0",
"rc9": "^1.2.0",
"scule": "^0.2.1",

View File

@ -1,8 +1,10 @@
import { promises as fsp } from 'fs'
import defu from 'defu'
import { applyDefaults } from 'untyped'
import { useNuxt, nuxtCtx } from '../nuxt'
import type { Nuxt } from '../types/nuxt'
import type { Nuxt, NuxtTemplate } from '../types/nuxt'
import type { NuxtModule, LegacyNuxtModule, ModuleOptions } from '../types/module'
import { compileTemplate, isNuxt2, templateUtils } from './utils'
/**
* Define a Nuxt module, automatically merging defaults with user provided options, installing
@ -48,6 +50,39 @@ export function defineNuxtModule<OptionsT extends ModuleOptions> (input: NuxtMod
}
}
if (isNuxt2()) {
// Support virtual templates with getContents() by writing them to .nuxt directory
let virtualTemplates: NuxtTemplate[]
nuxt.hook('builder:prepared', (_builder, buildOptions) => {
virtualTemplates = []
buildOptions.templates.forEach((template, index, arr) => {
if (!template.getContents) { return }
// Remove template from template array to handle it ourselves
arr.splice(index, 1)
virtualTemplates.push(template)
})
})
nuxt.hook('build:templates', async (templates) => {
const context = {
nuxt,
utils: templateUtils,
app: {
dir: nuxt.options.srcDir,
extensions: nuxt.options.extensions,
plugins: nuxt.options.plugins,
templates: [
...templates.templatesFiles,
...virtualTemplates
]
}
}
for await (const template of virtualTemplates) {
const contents = await compileTemplate({ ...template, src: '' }, context)
await fsp.writeFile(template.dst, contents)
}
})
}
// Call setup
return mod.setup.call(null, resolvedOptions, nuxt)
}

View File

@ -1,8 +1,10 @@
import fs from 'fs'
import { basename, parse, resolve } from 'pathe'
import { existsSync, promises as fsp } from 'fs'
import { basename, extname, parse, resolve } from 'pathe'
import lodashTemplate from 'lodash/template'
import hash from 'hash-sum'
import type { WebpackPluginInstance, Configuration as WebpackConfig } from 'webpack'
import type { Plugin as VitePlugin, UserConfig as ViteConfig } from 'vite'
import { camelCase } from 'scule'
import { useNuxt } from '../nuxt'
import type { NuxtTemplate, NuxtPlugin, NuxtPluginTemplate } from '../types/nuxt'
@ -42,7 +44,7 @@ export function normalizeTemplate (template: NuxtTemplate | string): NuxtTemplat
// Use src if provided
if (template.src) {
if (!fs.existsSync(template.src)) {
if (!existsSync(template.src)) {
throw new Error('Template not found: ' + template.src)
}
if (!template.filename) {
@ -259,3 +261,42 @@ export function addVitePlugin (plugin: VitePlugin, options?: ExtendViteConfigOpt
export function isNuxt2 (nuxt?: any) {
return (nuxt || useNuxt()).version?.startsWith('v2')
}
export async function compileTemplate (template: NuxtTemplate, ctx: any) {
const data = { ...ctx, ...template.options }
if (template.src) {
try {
const srcContents = await fsp.readFile(template.src, 'utf-8')
return lodashTemplate(srcContents, {})(data)
} catch (err) {
console.error('Error compiling template: ', template)
throw err
}
}
if (template.getContents) {
return template.getContents(data)
}
throw new Error('Invalid template: ' + JSON.stringify(template))
}
const serialize = data => JSON.stringify(data, null, 2).replace(/"{(.+)}"/g, '$1')
const importName = (src: string) => `${camelCase(basename(src, extname(src))).replace(/[^a-zA-Z?\d\s:]/g, '')}_${hash(src)}`
const importSources = (sources: string | string[], { lazy = false } = {}) => {
if (!Array.isArray(sources)) {
sources = [sources]
}
return sources.map((src) => {
if (lazy) {
return `const ${importName(src)} = () => import('${src}' /* webpackChunkName: '${src}' */)`
}
return `import ${importName(src)} from '${src}'`
}).join('\n')
}
export const templateUtils = {
serialize,
importName,
importSources
}

View File

@ -3,6 +3,7 @@ import type { HookCallback } from 'hookable'
import type { Compiler, Configuration, Stats } from 'webpack'
import type { NuxtConfig, NuxtOptions } from '..'
import type { ModuleContainer } from '../module/container'
import { NuxtTemplate } from '../types/nuxt'
import { Nuxt, NuxtApp } from './nuxt'
type HookResult = Promise<void> | void
@ -11,13 +12,6 @@ type Builder = any
type Generator = any
type Server = any
type TemplateFile = string | {
src?: string
dst?: string
custom?: boolean
options?: any
}
type WatchEvent = 'add' | 'addDir' | 'change' | 'unlink' | 'unlinkDir'
interface PreloadFile {
asType: 'script' | 'style' | 'font'
@ -49,7 +43,7 @@ export interface NuxtHooks extends Record<string, HookCallback> {
'builder:extendPlugins': (plugins: NuxtOptions['plugins']) => HookResult
'build:templates': (templates: {
templateVars: Record<string, any>,
templatesFiles: TemplateFile[],
templatesFiles: NuxtTemplate[],
resolve: (...args: string[]) => string
}) => HookResult
'build:extendRoutes': (routes: any[], resolve: (...args: string[]) => string) => HookResult

View File

@ -23,6 +23,8 @@ export interface Nuxt {
export interface NuxtTemplate {
/** @deprecated filename */
fileName?: string
/** @deprecated whether template is custom or a nuxt core template */
custom?: boolean
/** resolved output file path (generated) */
dst?: string
/** The target filename once the template is copied into the Nuxt buildDir */

View File

@ -33,7 +33,6 @@
"hash-sum": "^2.0.0",
"hookable": "^5.0.0",
"ignore": "^5.1.8",
"lodash": "^4.17.21",
"nuxi": "^0.10.0",
"ohmyfetch": "^0.3.1",
"pathe": "^0.2.0",
@ -47,7 +46,6 @@
"devDependencies": {
"@types/fs-extra": "^9.0.13",
"@types/hash-sum": "^1.0.0",
"@types/lodash": "^4.14.173",
"unbuild": "latest",
"vue-meta": "next"
},

View File

@ -1,11 +1,9 @@
import { resolve } from 'pathe'
import lodashTemplate from 'lodash/template'
import defu from 'defu'
import { tryResolvePath, resolveFiles, Nuxt, NuxtApp, NuxtTemplate, normalizePlugin, normalizeTemplate } from '@nuxt/kit'
import { readFile, writeFile } from 'fs-extra'
import { tryResolvePath, resolveFiles, Nuxt, NuxtApp, normalizePlugin, normalizeTemplate, compileTemplate, templateUtils } from '@nuxt/kit'
import { writeFile } from 'fs-extra'
import * as defaultTemplates from '../app/templates'
import * as templateUtils from './template.utils'
export function createApp (nuxt: Nuxt, options: Partial<NuxtApp> = {}): NuxtApp {
return defu(options, {
@ -77,20 +75,3 @@ export async function resolveApp (nuxt: Nuxt, app: NuxtApp) {
// Extend app
await nuxt.callHook('app:resolve', app)
}
async function compileTemplate (template: NuxtTemplate, ctx: any) {
const data = { ...ctx, ...template.options }
if (template.src) {
try {
const srcContents = await readFile(template.src, 'utf-8')
return lodashTemplate(srcContents, {})(data)
} catch (err) {
console.error('Error compiling template: ', template)
throw err
}
}
if (template.getContents) {
return template.getContents(data)
}
throw new Error('Invalid template: ' + JSON.stringify(template))
}

View File

@ -1485,6 +1485,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "@nuxt/kit@workspace:packages/kit"
dependencies:
"@types/lodash": ^4.14.173
consola: ^2.15.3
create-require: ^1.1.1
defu: ^5.0.0
@ -1493,6 +1494,7 @@ __metadata:
hash-sum: ^2.0.0
hookable: ^5.0.0
jiti: ^1.12.3
lodash: ^4.17.21
pathe: ^0.2.0
rc9: ^1.2.0
scule: ^0.2.1
@ -10087,7 +10089,6 @@ fsevents@~2.3.2:
"@nuxt/webpack-builder": ^0.10.0
"@types/fs-extra": ^9.0.13
"@types/hash-sum": ^1.0.0
"@types/lodash": ^4.14.173
"@vue/reactivity": 3.2.16
"@vue/server-renderer": ^3.2.16
"@vue/shared": 3.2.16
@ -10100,7 +10101,6 @@ fsevents@~2.3.2:
hash-sum: ^2.0.0
hookable: ^5.0.0
ignore: ^5.1.8
lodash: ^4.17.21
nuxi: ^0.10.0
ohmyfetch: ^0.3.1
pathe: ^0.2.0