From 7fdcee3252b46aef76692e48fe7986a138b3e8ee Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Wed, 29 Sep 2021 11:10:46 +0100 Subject: [PATCH] feat(kit): support templates with `getContents()` for nuxt 2 (#587) --- packages/kit/package.json | 2 ++ packages/kit/src/module/define.ts | 37 +++++++++++++++++++++++- packages/kit/src/module/utils.ts | 47 +++++++++++++++++++++++++++++-- packages/kit/src/types/hooks.ts | 10 ++----- packages/kit/src/types/nuxt.ts | 2 ++ packages/nuxt3/package.json | 2 -- packages/nuxt3/src/core/app.ts | 23 ++------------- yarn.lock | 4 +-- 8 files changed, 90 insertions(+), 37 deletions(-) diff --git a/packages/kit/package.json b/packages/kit/package.json index 6f428974f1..b9c878e428 100644 --- a/packages/kit/package.json +++ b/packages/kit/package.json @@ -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", diff --git a/packages/kit/src/module/define.ts b/packages/kit/src/module/define.ts index 885cd1fc80..71c1dd8650 100644 --- a/packages/kit/src/module/define.ts +++ b/packages/kit/src/module/define.ts @@ -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 (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) } diff --git a/packages/kit/src/module/utils.ts b/packages/kit/src/module/utils.ts index 69f25149f4..03fc82bd22 100644 --- a/packages/kit/src/module/utils.ts +++ b/packages/kit/src/module/utils.ts @@ -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 +} diff --git a/packages/kit/src/types/hooks.ts b/packages/kit/src/types/hooks.ts index 3fedabef6f..a1fb625d61 100644 --- a/packages/kit/src/types/hooks.ts +++ b/packages/kit/src/types/hooks.ts @@ -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 @@ -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 { 'builder:extendPlugins': (plugins: NuxtOptions['plugins']) => HookResult 'build:templates': (templates: { templateVars: Record, - templatesFiles: TemplateFile[], + templatesFiles: NuxtTemplate[], resolve: (...args: string[]) => string }) => HookResult 'build:extendRoutes': (routes: any[], resolve: (...args: string[]) => string) => HookResult diff --git a/packages/kit/src/types/nuxt.ts b/packages/kit/src/types/nuxt.ts index c139ed11d7..2f218d73a9 100644 --- a/packages/kit/src/types/nuxt.ts +++ b/packages/kit/src/types/nuxt.ts @@ -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 */ diff --git a/packages/nuxt3/package.json b/packages/nuxt3/package.json index 4580abc3a8..8221f5b7be 100644 --- a/packages/nuxt3/package.json +++ b/packages/nuxt3/package.json @@ -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" }, diff --git a/packages/nuxt3/src/core/app.ts b/packages/nuxt3/src/core/app.ts index 697ebf3023..eef3c73632 100644 --- a/packages/nuxt3/src/core/app.ts +++ b/packages/nuxt3/src/core/app.ts @@ -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 { 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)) -} diff --git a/yarn.lock b/yarn.lock index faffc10eb9..d7d2eef482 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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