From aea35e244af16b54d4ea10641d3afae3c34a7d50 Mon Sep 17 00:00:00 2001 From: Bobbie Goede Date: Fri, 6 Dec 2024 16:28:13 +0100 Subject: [PATCH 1/5] feat: support `dir` and `compatibilityVersion: 4` config --- src/commands/add.ts | 7 +++++-- src/utils/templates.ts | 22 ++++++++++++---------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/commands/add.ts b/src/commands/add.ts index f4a131142..4db346d2a 100644 --- a/src/commands/add.ts +++ b/src/commands/add.ts @@ -61,10 +61,13 @@ export default defineCommand({ const config = await kit.loadNuxtConfig({ cwd }) // Resolve template - const res = template({ name, args: ctx.args }) + const res = template({ name, args: ctx.args, nuxt: config }) + + // Change resolution root dir when applicable + const resolveFromRoot = config.future.compatibilityVersion === 4 && templateName === 'api' // Resolve full path to generated file - const path = resolve(config.srcDir, res.path) + const path = resolve(resolveFromRoot ? config.rootDir : config.srcDir, res.path) // Ensure not overriding user code if (!ctx.args.force && existsSync(path)) { diff --git a/src/utils/templates.ts b/src/utils/templates.ts index 339a3eefc..b9ec1600f 100644 --- a/src/utils/templates.ts +++ b/src/utils/templates.ts @@ -1,8 +1,10 @@ import { camelCase, upperFirst } from 'scule' +import type { NuxtOptions } from '@nuxt/schema' interface TemplateOptions { name: string args: Record + nuxt: NuxtOptions } interface Template { @@ -20,8 +22,8 @@ const httpMethods = [ 'trace', 'patch', ] -const api: Template = ({ name, args }) => ({ - path: `server/api/${name}${applySuffix(args, httpMethods, 'method')}.ts`, +const api: Template = ({ name, args, nuxt }) => ({ + path: `${nuxt.serverDir}/api/${name}${applySuffix(args, httpMethods, 'method')}.ts`, contents: ` export default defineEventHandler((event) => { return 'Hello ${name}' @@ -29,8 +31,8 @@ export default defineEventHandler((event) => { `, }) -const plugin: Template = ({ name, args }) => ({ - path: `plugins/${name}${applySuffix(args, ['client', 'server'], 'mode')}.ts`, +const plugin: Template = ({ name, args, nuxt }) => ({ + path: `${nuxt.dir.plugins}/${name}${applySuffix(args, ['client', 'server'], 'mode')}.ts`, contents: ` export default defineNuxtPlugin((nuxtApp) => {}) `, @@ -69,15 +71,15 @@ export const composable: Template = ({ name }) => { } } -const middleware: Template = ({ name, args }) => ({ - path: `middleware/${name}${applySuffix(args, ['global'])}.ts`, +const middleware: Template = ({ name, args, nuxt }) => ({ + path: `${nuxt.dir.middleware}/${name}${applySuffix(args, ['global'])}.ts`, contents: ` export default defineNuxtRouteMiddleware((to, from) => {}) `, }) -const layout: Template = ({ name }) => ({ - path: `layouts/${name}.vue`, +const layout: Template = ({ name, nuxt }) => ({ + path: `${nuxt.dir.layouts}/${name}.vue`, contents: ` @@ -92,8 +94,8 @@ const layout: Template = ({ name }) => ({ `, }) -const page: Template = ({ name }) => ({ - path: `pages/${name}.vue`, +const page: Template = ({ name, nuxt }) => ({ + path: `${nuxt.dir.pages}/${name}.vue`, contents: ` From 82eaf941591174d8d4014aafbaf6715548fa8e49 Mon Sep 17 00:00:00 2001 From: Bobbie Goede Date: Fri, 6 Dec 2024 17:43:52 +0100 Subject: [PATCH 2/5] refactor: resolve generated path in template function --- src/commands/add.ts | 16 +++++----------- src/utils/templates.ts | 31 ++++++++++++++++++------------- 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/src/commands/add.ts b/src/commands/add.ts index 4db346d2a..e1e5fc5e1 100644 --- a/src/commands/add.ts +++ b/src/commands/add.ts @@ -63,22 +63,16 @@ export default defineCommand({ // Resolve template const res = template({ name, args: ctx.args, nuxt: config }) - // Change resolution root dir when applicable - const resolveFromRoot = config.future.compatibilityVersion === 4 && templateName === 'api' - - // Resolve full path to generated file - const path = resolve(resolveFromRoot ? config.rootDir : config.srcDir, res.path) - // Ensure not overriding user code - if (!ctx.args.force && existsSync(path)) { + if (!ctx.args.force && existsSync(res.path)) { consola.error( - `File exists: ${path} . Use --force to override or use a different name.`, + `File exists: ${res.path} . Use --force to override or use a different name.`, ) process.exit(1) } // Ensure parent directory exists - const parentDir = dirname(path) + const parentDir = dirname(res.path) if (!existsSync(parentDir)) { consola.info('Creating directory', parentDir) if (templateName === 'page') { @@ -88,7 +82,7 @@ export default defineCommand({ } // Write file - await fsp.writeFile(path, res.contents.trim() + '\n') - consola.info(`🪄 Generated a new ${templateName} in ${path}`) + await fsp.writeFile(res.path, res.contents.trim() + '\n') + consola.info(`🪄 Generated a new ${templateName} in ${res.path}`) }, }) diff --git a/src/utils/templates.ts b/src/utils/templates.ts index b9ec1600f..eb49ecb89 100644 --- a/src/utils/templates.ts +++ b/src/utils/templates.ts @@ -1,3 +1,4 @@ +import { resolve } from 'pathe' import { camelCase, upperFirst } from 'scule' import type { NuxtOptions } from '@nuxt/schema' @@ -22,28 +23,32 @@ const httpMethods = [ 'trace', 'patch', ] -const api: Template = ({ name, args, nuxt }) => ({ - path: `${nuxt.serverDir}/api/${name}${applySuffix(args, httpMethods, 'method')}.ts`, - contents: ` +const api: Template = ({ name, args, nuxt }) => { + const pathRoot = nuxt.future.compatibilityVersion === 4 ? nuxt.rootDir : nuxt.srcDir + + return { + path: resolve(pathRoot, `${nuxt.serverDir}/api/${name}${applySuffix(args, httpMethods, 'method')}.ts`), + contents: ` export default defineEventHandler((event) => { return 'Hello ${name}' }) `, -}) + } +} const plugin: Template = ({ name, args, nuxt }) => ({ - path: `${nuxt.dir.plugins}/${name}${applySuffix(args, ['client', 'server'], 'mode')}.ts`, + path: resolve(nuxt.srcDir, `${nuxt.dir.plugins}/${name}${applySuffix(args, ['client', 'server'], 'mode')}.ts`), contents: ` export default defineNuxtPlugin((nuxtApp) => {}) `, }) -const component: Template = ({ name, args }) => ({ - path: `components/${name}${applySuffix( +const component: Template = ({ name, args, nuxt }) => ({ + path: resolve(nuxt.srcDir, `components/${name}${applySuffix( args, ['client', 'server'], 'mode', - )}.vue`, + )}.vue`), contents: ` @@ -57,12 +62,12 @@ const component: Template = ({ name, args }) => ({ `, }) -export const composable: Template = ({ name }) => { +export const composable: Template = ({ name, nuxt }) => { const nameWithoutUsePrefix = name.replace(/^use-?/, '') const nameWithUsePrefix = `use${upperFirst(camelCase(nameWithoutUsePrefix))}` return { - path: `composables/${name}.ts`, + path: resolve(nuxt.srcDir, `composables/${name}.ts`), contents: ` export const ${nameWithUsePrefix} = () => { return ref() @@ -72,14 +77,14 @@ export const composable: Template = ({ name }) => { } const middleware: Template = ({ name, args, nuxt }) => ({ - path: `${nuxt.dir.middleware}/${name}${applySuffix(args, ['global'])}.ts`, + path: resolve(nuxt.srcDir, `${nuxt.dir.middleware}/${name}${applySuffix(args, ['global'])}.ts`), contents: ` export default defineNuxtRouteMiddleware((to, from) => {}) `, }) const layout: Template = ({ name, nuxt }) => ({ - path: `${nuxt.dir.layouts}/${name}.vue`, + path: resolve(nuxt.srcDir, `${nuxt.dir.layouts}/${name}.vue`), contents: ` @@ -95,7 +100,7 @@ const layout: Template = ({ name, nuxt }) => ({ }) const page: Template = ({ name, nuxt }) => ({ - path: `${nuxt.dir.pages}/${name}.vue`, + path: resolve(nuxt.srcDir, `${nuxt.dir.pages}/${name}.vue`), contents: ` From bce62198e02991b01547a3171003e92c7354bf66 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Fri, 6 Dec 2024 21:48:57 +0000 Subject: [PATCH 3/5] refactor: always resolve from `srcDir` --- src/utils/templates.ts | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/utils/templates.ts b/src/utils/templates.ts index eb49ecb89..f913faf84 100644 --- a/src/utils/templates.ts +++ b/src/utils/templates.ts @@ -24,10 +24,8 @@ const httpMethods = [ 'patch', ] const api: Template = ({ name, args, nuxt }) => { - const pathRoot = nuxt.future.compatibilityVersion === 4 ? nuxt.rootDir : nuxt.srcDir - return { - path: resolve(pathRoot, `${nuxt.serverDir}/api/${name}${applySuffix(args, httpMethods, 'method')}.ts`), + path: resolve(nuxt.srcDir, nuxt.serverDir, `api/${name}${applySuffix(args, httpMethods, 'method')}.ts`), contents: ` export default defineEventHandler((event) => { return 'Hello ${name}' @@ -37,7 +35,7 @@ export default defineEventHandler((event) => { } const plugin: Template = ({ name, args, nuxt }) => ({ - path: resolve(nuxt.srcDir, `${nuxt.dir.plugins}/${name}${applySuffix(args, ['client', 'server'], 'mode')}.ts`), + path: resolve(nuxt.srcDir, nuxt.dir.plugins, `${name}${applySuffix(args, ['client', 'server'], 'mode')}.ts`), contents: ` export default defineNuxtPlugin((nuxtApp) => {}) `, @@ -77,14 +75,14 @@ export const composable: Template = ({ name, nuxt }) => { } const middleware: Template = ({ name, args, nuxt }) => ({ - path: resolve(nuxt.srcDir, `${nuxt.dir.middleware}/${name}${applySuffix(args, ['global'])}.ts`), + path: resolve(nuxt.srcDir, nuxt.dir.middleware, `${name}${applySuffix(args, ['global'])}.ts`), contents: ` export default defineNuxtRouteMiddleware((to, from) => {}) `, }) const layout: Template = ({ name, nuxt }) => ({ - path: resolve(nuxt.srcDir, `${nuxt.dir.layouts}/${name}.vue`), + path: resolve(nuxt.srcDir, nuxt.dir.layouts, `${name}.vue`), contents: ` @@ -100,7 +98,7 @@ const layout: Template = ({ name, nuxt }) => ({ }) const page: Template = ({ name, nuxt }) => ({ - path: resolve(nuxt.srcDir, `${nuxt.dir.pages}/${name}.vue`), + path: resolve(nuxt.srcDir, nuxt.dir.pages, `${name}.vue`), contents: ` From a580e43ee18e953b2f645f9c9afdd8c693f751a3 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Fri, 6 Dec 2024 21:51:52 +0000 Subject: [PATCH 4/5] refactor: rename to `nuxtOptions` --- src/commands/add.ts | 2 +- src/utils/templates.ts | 30 +++++++++++++++--------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/commands/add.ts b/src/commands/add.ts index e1e5fc5e1..b583829f2 100644 --- a/src/commands/add.ts +++ b/src/commands/add.ts @@ -61,7 +61,7 @@ export default defineCommand({ const config = await kit.loadNuxtConfig({ cwd }) // Resolve template - const res = template({ name, args: ctx.args, nuxt: config }) + const res = template({ name, args: ctx.args, nuxtOptions: config }) // Ensure not overriding user code if (!ctx.args.force && existsSync(res.path)) { diff --git a/src/utils/templates.ts b/src/utils/templates.ts index f913faf84..368b21f16 100644 --- a/src/utils/templates.ts +++ b/src/utils/templates.ts @@ -5,7 +5,7 @@ import type { NuxtOptions } from '@nuxt/schema' interface TemplateOptions { name: string args: Record - nuxt: NuxtOptions + nuxtOptions: NuxtOptions } interface Template { @@ -23,9 +23,9 @@ const httpMethods = [ 'trace', 'patch', ] -const api: Template = ({ name, args, nuxt }) => { +const api: Template = ({ name, args, nuxtOptions }) => { return { - path: resolve(nuxt.srcDir, nuxt.serverDir, `api/${name}${applySuffix(args, httpMethods, 'method')}.ts`), + path: resolve(nuxtOptions.srcDir, nuxtOptions.serverDir, `api/${name}${applySuffix(args, httpMethods, 'method')}.ts`), contents: ` export default defineEventHandler((event) => { return 'Hello ${name}' @@ -34,15 +34,15 @@ export default defineEventHandler((event) => { } } -const plugin: Template = ({ name, args, nuxt }) => ({ - path: resolve(nuxt.srcDir, nuxt.dir.plugins, `${name}${applySuffix(args, ['client', 'server'], 'mode')}.ts`), +const plugin: Template = ({ name, args, nuxtOptions }) => ({ + path: resolve(nuxtOptions.srcDir, nuxtOptions.dir.plugins, `${name}${applySuffix(args, ['client', 'server'], 'mode')}.ts`), contents: ` export default defineNuxtPlugin((nuxtApp) => {}) `, }) -const component: Template = ({ name, args, nuxt }) => ({ - path: resolve(nuxt.srcDir, `components/${name}${applySuffix( +const component: Template = ({ name, args, nuxtOptions }) => ({ + path: resolve(nuxtOptions.srcDir, `components/${name}${applySuffix( args, ['client', 'server'], 'mode', @@ -60,12 +60,12 @@ const component: Template = ({ name, args, nuxt }) => ({ `, }) -export const composable: Template = ({ name, nuxt }) => { +export const composable: Template = ({ name, nuxtOptions }) => { const nameWithoutUsePrefix = name.replace(/^use-?/, '') const nameWithUsePrefix = `use${upperFirst(camelCase(nameWithoutUsePrefix))}` return { - path: resolve(nuxt.srcDir, `composables/${name}.ts`), + path: resolve(nuxtOptions.srcDir, `composables/${name}.ts`), contents: ` export const ${nameWithUsePrefix} = () => { return ref() @@ -74,15 +74,15 @@ export const composable: Template = ({ name, nuxt }) => { } } -const middleware: Template = ({ name, args, nuxt }) => ({ - path: resolve(nuxt.srcDir, nuxt.dir.middleware, `${name}${applySuffix(args, ['global'])}.ts`), +const middleware: Template = ({ name, args, nuxtOptions }) => ({ + path: resolve(nuxtOptions.srcDir, nuxtOptions.dir.middleware, `${name}${applySuffix(args, ['global'])}.ts`), contents: ` export default defineNuxtRouteMiddleware((to, from) => {}) `, }) -const layout: Template = ({ name, nuxt }) => ({ - path: resolve(nuxt.srcDir, nuxt.dir.layouts, `${name}.vue`), +const layout: Template = ({ name, nuxtOptions }) => ({ + path: resolve(nuxtOptions.srcDir, nuxtOptions.dir.layouts, `${name}.vue`), contents: ` @@ -97,8 +97,8 @@ const layout: Template = ({ name, nuxt }) => ({ `, }) -const page: Template = ({ name, nuxt }) => ({ - path: resolve(nuxt.srcDir, nuxt.dir.pages, `${name}.vue`), +const page: Template = ({ name, nuxtOptions }) => ({ + path: resolve(nuxtOptions.srcDir, nuxtOptions.dir.pages, `${name}.vue`), contents: ` From e9de5efa6bc1e1c4e0bdff47f8fc9e00d7240ae8 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Fri, 6 Dec 2024 21:52:00 +0000 Subject: [PATCH 5/5] test: pass srcDir via `nuxtOptions` --- test/unit/templates.spec.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/unit/templates.spec.ts b/test/unit/templates.spec.ts index 54f6174f5..d95d9de78 100644 --- a/test/unit/templates.spec.ts +++ b/test/unit/templates.spec.ts @@ -1,10 +1,11 @@ import { describe, expect, it } from 'vitest' +import type { NuxtOptions } from '@nuxt/schema' import { composable } from '../../src/utils/templates' describe('templates', () => { it('composables', () => { for (const name of ['useSomeComposable', 'someComposable', 'use-some-composable', 'use-someComposable', 'some-composable']) { - expect(composable({ name, args: {} }).contents.trim().split('\n')[0]).toBe('export const useSomeComposable = () => {') + expect(composable({ name, args: {}, nuxtOptions: { srcDir: '/src' } as NuxtOptions }).contents.trim().split('\n')[0]).toBe('export const useSomeComposable = () => {') } }) })