diff --git a/README.md b/README.md index fe2ed00..9df12b5 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,7 @@ Our plan is to release the first version when all Vite templates are ready: Later we will add support for meta-frameworks: - [x] `Nuxt 3` template (from `v0.1.0`) +- [x] `Nuxt 3 Compatibility Version 4 (Nuxt 4)` template (from `v0.4.0`) - [x] `SvelteKit` template (from `v0.2.0`) - [ ] `Astro` template - [x] `Remix` template (from `v0.3.0`) diff --git a/TODO.md b/TODO.md index 7caf6ff..e11e09e 100644 --- a/TODO.md +++ b/TODO.md @@ -9,7 +9,8 @@ - [x] `react` and `react-ts` templates - [x] `preact` and `preact-ts` templates - [x] `solid` and `solid-ts` templates -- [x] `nuxt` template (from `v0.1.0`) +- [x] `nuxt 3` template (from `v0.1.0`) +- [x] `nuxt 3 v4-compat` template (from `v0.4.0`) - [x] `sveltekit` template (from `v0.2.0`) - [ ] `astro` template - [x] `remix` template (from `v0.3.0`) @@ -28,7 +29,8 @@ - [x] `react` and `react-ts` templates - [x] `preact` and `preact-ts` templates - [x] `solid` and `solid-ts` templates - - [x] `nuxt` template + - [x] `nuxt 3` template + - [x] `nuxt 3 v4-compat` template - [x] `sveltekit` template - [ ] `astro` template - [x] `remix` template diff --git a/src/customize/index.ts b/src/customize/index.ts index e21595c..0862b81 100644 --- a/src/customize/index.ts +++ b/src/customize/index.ts @@ -32,7 +32,10 @@ export async function customize(prompts: PromptsData) { await import('./svelte').then(({ customize }) => customize(prompts)) break case 'custom-nuxt': - await import('./nuxt').then(({ customize }) => customize(prompts)) + await import('./nuxt').then(({ customize }) => customize(prompts, false)) + break + case 'custom-nuxt-v4': + await import('./nuxt').then(({ customize }) => customize(prompts, true)) break case 'custom-remix': await import('./remix').then(({ customize }) => customize(prompts)) diff --git a/src/customize/nuxt.ts b/src/customize/nuxt.ts index ab871ba..8bba3a7 100644 --- a/src/customize/nuxt.ts +++ b/src/customize/nuxt.ts @@ -18,19 +18,20 @@ import { WorkboxVersion, } from '../versions' -export async function customize(prompts: PromptsData) { +export async function customize(prompts: PromptsData, v4: boolean) { const { cdProjectName, - templateDir, - rootPath, + templateDir: templateDirFolder, + rootPath: outputRootPath, customServiceWorker, prompt, pwaAssets, } = prompts + const [rootPath, templateDir] = getPaths(outputRootPath, templateDirFolder, v4) // cleanup target folder fs.rmSync(path.join(rootPath, 'app.vue'), { recursive: true }) - fs.rmSync(path.join(rootPath, 'nuxt.config.ts'), { recursive: true }) - fs.rmSync(path.join(rootPath, 'public'), { recursive: true }) + fs.rmSync(path.join(outputRootPath, 'nuxt.config.ts'), { recursive: true }) + fs.rmSync(path.join(outputRootPath, 'public'), { recursive: true }) // extract template-nuxt folder fs.copyFileSync(path.join(templateDir, 'app.vue'), path.join(rootPath, 'app.vue')) fs.mkdirSync(path.join(rootPath, 'layouts')) @@ -44,28 +45,51 @@ export async function customize(prompts: PromptsData) { path.join(templateDir, 'pages', 'index.vue'), path.join(rootPath, 'pages', 'index.vue'), ) - fs.mkdirSync(path.join(rootPath, 'public')) - fs.copyFileSync(path.join(templateDir, 'public', 'favicon.svg'), path.join(rootPath, 'public', 'favicon.svg')) - fs.copyFileSync(path.join(templateDir, 'pwa-assets.config.ts'), path.join(rootPath, 'pwa-assets.config.ts')) + fs.mkdirSync(path.join(outputRootPath, 'public')) + fs.copyFileSync(path.join(templateDir, 'public', 'favicon.svg'), path.join(outputRootPath, 'public', 'favicon.svg')) + + // v4 root inside app: public folder on root + if (v4 && pwaAssets) { + const pwaAssets = fs.readFileSync(path.join(templateDir, 'pwa-assets.config.ts'), 'utf-8') + fs.writeFileSync( + path.join(outputRootPath, 'app/pwa-assets.config.ts'), + pwaAssets.replace('images: [\'public/favicon.svg\'],', 'images: [\'../public/favicon.svg\'],'), + 'utf-8', + ) + } + else { + fs.copyFileSync(path.join(templateDir, 'pwa-assets.config.ts'), path.join(outputRootPath, 'pwa-assets.config.ts')) + } + if (customServiceWorker) { fs.mkdirSync(path.join(rootPath, 'service-worker')) fs.copyFileSync( path.join(templateDir, 'service-worker', `${prompt ? 'prompt' : 'claims'}-sw.ts`), path.join(rootPath, 'service-worker', 'sw.ts'), ) - fs.copyFileSync( - path.join(templateDir, 'service-worker', 'tsconfig.json'), - path.join(rootPath, 'service-worker', 'tsconfig.json'), - ) + if (v4) { + const tsConfig = fs.readFileSync(path.join(templateDir, 'service-worker', 'tsconfig.json'), 'utf-8') + fs.writeFileSync( + path.join(rootPath, 'service-worker', 'tsconfig.json'), + tsConfig.replace('"extends": "../tsconfig.json",', '"extends": "../../tsconfig.json",'), + 'utf-8', + ) + } + else { + fs.copyFileSync( + path.join(templateDir, 'service-worker', 'tsconfig.json'), + path.join(rootPath, 'service-worker', 'tsconfig.json'), + ) + } } // create nuxt.config.ts - createNuxtConf(prompts) + createNuxtConf(prompts, v4) // prepare package.json - const pkg = JSON.parse(fs.readFileSync(path.join(rootPath, 'package.json'), 'utf-8')) + const pkg = JSON.parse(fs.readFileSync(path.join(outputRootPath, 'package.json'), 'utf-8')) - const pkgManager = await detectPackageManager(rootPath).then(res => res?.name || 'npm') + const pkgManager = await detectPackageManager(outputRootPath).then(res => res?.name || 'npm') // dependencies pkg.dependencies ??= {} @@ -95,10 +119,10 @@ export async function customize(prompts: PromptsData) { includeDependencies(prompts, pkgManager === 'npm', pkg, true) // save package.json - fs.writeFileSync(path.join(rootPath, 'package.json'), JSON.stringify(pkg, null, 2), 'utf-8') + fs.writeFileSync(path.join(outputRootPath, 'package.json'), JSON.stringify(pkg, null, 2), 'utf-8') console.log('\n\nPWA configuration done. Now run:\n') - if (rootPath !== process.cwd()) { + if (outputRootPath !== process.cwd()) { console.log( ` cd ${ cdProjectName.includes(' ') ? `"${cdProjectName}"` : cdProjectName @@ -122,7 +146,7 @@ export async function customize(prompts: PromptsData) { console.log() } -function createNuxtConf(prompts: PromptsData) { +function createNuxtConf(prompts: PromptsData, v4: boolean) { const { rootPath, customServiceWorker, @@ -157,32 +181,7 @@ function createNuxtConf(prompts: PromptsData) { pwaOptions.client.installPrompt = true } - const nuxtConf = customServiceWorker - ? parseModule(`// https://nuxt.com/docs/api/configuration/nuxt-config -export default defineNuxtConfig({ - typescript: { - tsConfig: { - exclude: ['../service-worker'], - }, - }, - devtools: { enabled: true }, - nitro: { - prerender: { - routes: ['/'], - }, - }, -}) -`) - : parseModule(`// https://nuxt.com/docs/api/configuration/nuxt-config -export default defineNuxtConfig({ - devtools: { enabled: true }, - nitro: { - prerender: { - routes: ['/'], - }, - }, -}) -`) + const nuxtConf = prepareNuxtConf(customServiceWorker === true, v4) addNuxtModule(nuxtConf, '@vite-pwa/nuxt', 'pwa', pwaOptions) fs.writeFileSync( path.join(rootPath, 'nuxt.config.ts'), @@ -313,3 +312,77 @@ function createLayout(prompts: PromptsData) { return content } + +function prepareNuxtConfV3(customServiceWorker: boolean): ReturnType { + return customServiceWorker + ? parseModule(`// https://nuxt.com/docs/api/configuration/nuxt-config +export default defineNuxtConfig({ + typescript: { + tsConfig: { + exclude: ['../service-worker'], + }, + }, + devtools: { enabled: true }, + nitro: { + prerender: { + routes: ['/'], + }, + }, +}) +`) + : parseModule(`// https://nuxt.com/docs/api/configuration/nuxt-config +export default defineNuxtConfig({ + devtools: { enabled: true }, + nitro: { + prerender: { + routes: ['/'], + }, + }, +}) +`) +} + +function prepareNuxtConfV4(customServiceWorker: boolean): ReturnType { + return customServiceWorker + ? parseModule(`// https://nuxt.com/docs/api/configuration/nuxt-config +export default defineNuxtConfig({ + future: { + compatibilityVersion: 4 + }, + typescript: { + tsConfig: { + exclude: ['../app/service-worker'], + }, + }, + devtools: { enabled: true }, + nitro: { + prerender: { + routes: ['/'], + }, + }, +}) +`) + : parseModule(`// https://nuxt.com/docs/api/configuration/nuxt-config +export default defineNuxtConfig({ + future: { + compatibilityVersion: 4 + }, + devtools: { enabled: true }, + nitro: { + prerender: { + routes: ['/'], + }, + }, +}) +`) +} + +function getPaths(rootPath: string, templateDir: string, v4: boolean): [rootPath: string, templateDir: string] { + return v4 + ? [path.join(rootPath, 'app'), templateDir.replace('template-custom-nuxt-v4', 'template-custom-nuxt')] + : [rootPath, templateDir] +} + +function prepareNuxtConf(customSW: boolean, v4: boolean): ReturnType { + return v4 ? prepareNuxtConfV4(customSW) : prepareNuxtConfV3(customSW) +} diff --git a/src/index.ts b/src/index.ts index 4e9828a..04a5656 100644 --- a/src/index.ts +++ b/src/index.ts @@ -208,7 +208,7 @@ async function init() { inactive: 'no', }, { - type: (_, { variant }) => variant === 'custom-nuxt' ? 'toggle' : null, + type: (_, { variant }) => variant === 'custom-nuxt' || variant === 'custom-nuxt-v4' ? 'toggle' : null, name: 'installPWA', message: reset('Add PWA Install Prompt?'), initial: false, diff --git a/src/prompts.ts b/src/prompts.ts index 59db0a3..6e11c28 100644 --- a/src/prompts.ts +++ b/src/prompts.ts @@ -55,6 +55,13 @@ export const FRAMEWORKS = ([ customCommand: 'npm exec nuxi init TARGET_DIR', enabled: true, }, + { + name: 'custom-nuxt-v4', + display: 'Nuxt 4 ↗', + color: cyan, + customCommand: 'npx nuxi@latest init -t v4-compat TARGET_DIR', + enabled: true, + }, ], }, { diff --git a/src/types.ts b/src/types.ts index f33b230..38d2132 100644 --- a/src/types.ts +++ b/src/types.ts @@ -10,7 +10,7 @@ export interface Framework { export type FrameworkVariantKey = | 'vanilla' | 'vanilla-ts' - | 'vue' | 'vue-ts' | 'custom-nuxt' + | 'vue' | 'vue-ts' | 'custom-nuxt' | 'custom-nuxt-v4' | 'react' | 'react-ts' | 'preact' | 'preact-ts' | 'svelte' | 'svelte-ts' | 'custom-svelte-kit' diff --git a/src/versions.ts b/src/versions.ts index 7c691b0..af41785 100644 --- a/src/versions.ts +++ b/src/versions.ts @@ -1,8 +1,8 @@ export const WorkboxVersion = '^7.1.0' -export const VitePluginPWAVersion = '^0.20.0' +export const VitePluginPWAVersion = '^0.20.1' export const PWAAssetsVersion = '^0.2.4' -export const NuxtPWAModuleVersion = '^0.7.0' -export const SvelteKitPWAVersion = '^0.5.0' +export const NuxtPWAModuleVersion = '^0.9.1' +export const SvelteKitPWAVersion = '^0.6.0' export const RemixPWAVersion = '^0.1.0' export const TypeScriptVersion = '^5.4.5' export const VueTscVersion = '^2.0.16'