From 2f444d2cb8fa45e87d214db8754a8fbe8adc2ecf Mon Sep 17 00:00:00 2001 From: Katerina Skroumpelou Date: Mon, 9 Oct 2023 18:14:13 +0300 Subject: [PATCH] feat(nuxt): component and page generators --- packages/nuxt/generators.json | 13 +++ .../__snapshots__/application.spec.ts.snap | 2 +- .../src/generators/application/application.ts | 2 +- .../generators/component/component.spec.ts | 27 ++++++ .../src/generators/component/component.ts | 23 +++++ .../nuxt/src/generators/component/schema.d.ts | 13 +++ .../nuxt/src/generators/component/schema.json | 91 +++++++++++++++++++ .../nuxt/src/generators/page/page.spec.ts | 57 ++++++++++++ packages/nuxt/src/generators/page/page.ts | 36 ++++++++ packages/nuxt/src/generators/page/schema.d.ts | 9 ++ packages/nuxt/src/generators/page/schema.json | 62 +++++++++++++ 11 files changed, 333 insertions(+), 2 deletions(-) create mode 100644 packages/nuxt/src/generators/component/component.spec.ts create mode 100644 packages/nuxt/src/generators/component/component.ts create mode 100644 packages/nuxt/src/generators/component/schema.d.ts create mode 100644 packages/nuxt/src/generators/component/schema.json create mode 100644 packages/nuxt/src/generators/page/page.spec.ts create mode 100644 packages/nuxt/src/generators/page/page.ts create mode 100644 packages/nuxt/src/generators/page/schema.d.ts create mode 100644 packages/nuxt/src/generators/page/schema.json diff --git a/packages/nuxt/generators.json b/packages/nuxt/generators.json index 2c7bb409a3d4e4..39c6a24c846077 100644 --- a/packages/nuxt/generators.json +++ b/packages/nuxt/generators.json @@ -14,6 +14,19 @@ "schema": "./src/generators/application/schema.json", "aliases": ["app"], "description": "Create a Nuxt application." + }, + "component": { + "factory": "./src/generators/component/component", + "schema": "./src/generators/component/schema.json", + "aliases": ["c"], + "x-type": "component", + "description": "Create a Vue component for your Nuxt application." + }, + "page": { + "factory": "./src/generators/page/page", + "schema": "./src/generators/page/schema.json", + "x-type": "page", + "description": "Create a new page for your Nuxt application." } } } diff --git a/packages/nuxt/src/generators/application/__snapshots__/application.spec.ts.snap b/packages/nuxt/src/generators/application/__snapshots__/application.spec.ts.snap index 7319218499f77b..8f3a7dbe55cf61 100644 --- a/packages/nuxt/src/generators/application/__snapshots__/application.spec.ts.snap +++ b/packages/nuxt/src/generators/application/__snapshots__/application.spec.ts.snap @@ -15,7 +15,7 @@ exports[`app generated files content - as-provided should configure tsconfig and "name": "my-app", "$schema": "../node_modules/nx/schemas/project-schema.json", "projectType": "application", - "sourceRoot": "my-app/src", + "sourceRoot": "my-app", "targets": { "serve": { "executor": "nx:run-commands", diff --git a/packages/nuxt/src/generators/application/application.ts b/packages/nuxt/src/generators/application/application.ts index 383386ddb3cf53..725b991469e977 100644 --- a/packages/nuxt/src/generators/application/application.ts +++ b/packages/nuxt/src/generators/application/application.ts @@ -37,7 +37,7 @@ export async function applicationGenerator(tree: Tree, schema: Schema) { addProjectConfiguration(tree, options.name, { root: options.appProjectRoot, projectType: 'application', - sourceRoot: `${options.appProjectRoot}/src`, + sourceRoot: `${options.appProjectRoot}`, targets: {}, }); diff --git a/packages/nuxt/src/generators/component/component.spec.ts b/packages/nuxt/src/generators/component/component.spec.ts new file mode 100644 index 00000000000000..323dd9e6aa9474 --- /dev/null +++ b/packages/nuxt/src/generators/component/component.spec.ts @@ -0,0 +1,27 @@ +import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; +import { Tree } from '@nx/devkit'; +import { applicationGenerator } from '../application/application'; +import { componentGenerator } from './component'; + +describe('app', () => { + let tree: Tree; + const name = 'my-app'; + + describe('generated files content - as-provided', () => { + beforeAll(async () => { + tree = createTreeWithEmptyWorkspace(); + await applicationGenerator(tree, { + name, + projectNameAndRootFormat: 'as-provided', + }); + }); + it('should create a new vue component in the correct location', async () => { + await componentGenerator(tree, { + name: 'hello', + project: name, + }); + + expect(tree.exists('my-app/components/hello/hello.vue')).toBeTruthy(); + }); + }); +}); diff --git a/packages/nuxt/src/generators/component/component.ts b/packages/nuxt/src/generators/component/component.ts new file mode 100644 index 00000000000000..e5710103d42405 --- /dev/null +++ b/packages/nuxt/src/generators/component/component.ts @@ -0,0 +1,23 @@ +import { formatFiles, runTasksInSerial, Tree } from '@nx/devkit'; +import { componentGenerator as vueComponentGenerator } from '@nx/vue'; +import { Schema } from './schema'; + +/* + * This generator is basically the Vue one, but for Nuxt we + * are just adjusting some options + */ +export async function componentGenerator(host: Tree, options: Schema) { + const componentGenerator = await vueComponentGenerator(host, { + ...options, + routing: false, + skipFormat: true, + }); + + if (!options.skipFormat) { + await formatFiles(host); + } + + return runTasksInSerial(componentGenerator); +} + +export default componentGenerator; diff --git a/packages/nuxt/src/generators/component/schema.d.ts b/packages/nuxt/src/generators/component/schema.d.ts new file mode 100644 index 00000000000000..29587473dfa0b7 --- /dev/null +++ b/packages/nuxt/src/generators/component/schema.d.ts @@ -0,0 +1,13 @@ +export interface Schema { + project: string; + name: string; + skipTests?: boolean; + flat?: boolean; + pascalCaseFiles?: boolean; + pascalCaseDirectory?: boolean; + fileName?: string; + inSourceTests?: boolean; + skipFormat?: boolean; + directory?: string; + unitTestRunner?: 'jest' | 'vitest' | 'none'; +} diff --git a/packages/nuxt/src/generators/component/schema.json b/packages/nuxt/src/generators/component/schema.json new file mode 100644 index 00000000000000..f2fc52c8991ee8 --- /dev/null +++ b/packages/nuxt/src/generators/component/schema.json @@ -0,0 +1,91 @@ +{ + "$schema": "http://json-schema.org/schema", + "cli": "nx", + "$id": "NxNuxtComponent", + "title": "Create a Nuxt Component", + "description": "Create a Nuxt Component for Nx.", + "type": "object", + "examples": [ + { + "command": "nx g component my-component --project=myapp", + "description": "Generate a component in the `myapp` application" + }, + { + "command": "nx g component my-component --project=myapp --classComponent", + "description": "Generate a class component in the `myapp` application" + } + ], + "properties": { + "project": { + "type": "string", + "description": "The name of the project.", + "alias": "p", + "$default": { + "$source": "projectName" + }, + "x-prompt": "What is the name of the project for this component?", + "x-priority": "important" + }, + "name": { + "type": "string", + "description": "The name of the component.", + "$default": { + "$source": "argv", + "index": 0 + }, + "x-prompt": "What name would you like to use for the component?", + "x-priority": "important" + }, + "skipTests": { + "type": "boolean", + "description": "When true, does not create `spec.ts` test files for the new component.", + "default": false, + "x-priority": "internal" + }, + "flat": { + "type": "boolean", + "description": "Create component at the source root rather than its own directory.", + "default": false + }, + "directory": { + "type": "string", + "description": "Create the component under this directory (can be nested).", + "alias": "dir", + "x-priority": "important" + }, + "pascalCaseFiles": { + "type": "boolean", + "description": "Use pascal case component file name (e.g. `App.vue`).", + "alias": "P", + "default": false + }, + "pascalCaseDirectory": { + "type": "boolean", + "description": "Use pascal case directory name (e.g. `App/App.vue`).", + "alias": "R", + "default": false + }, + "fileName": { + "type": "string", + "description": "Create a component with this file name." + }, + "inSourceTests": { + "type": "boolean", + "default": false, + "description": "When using Vitest, separate spec files will not be generated and instead will be included within the source files. Read more on the Vitest docs site: https://vitest.dev/guide/in-source.html" + }, + "skipFormat": { + "description": "Skip formatting files.", + "type": "boolean", + "default": false, + "x-priority": "internal" + }, + "unitTestRunner": { + "type": "string", + "enum": ["vitest", "jest", "none"], + "description": "Test runner to use for unit tests.", + "x-prompt": "What unit test runner should be used?" + } + }, + "required": ["name", "project"] +} diff --git a/packages/nuxt/src/generators/page/page.spec.ts b/packages/nuxt/src/generators/page/page.spec.ts new file mode 100644 index 00000000000000..da2606bc339fa7 --- /dev/null +++ b/packages/nuxt/src/generators/page/page.spec.ts @@ -0,0 +1,57 @@ +import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; +import { Tree } from '@nx/devkit'; +import { applicationGenerator } from '../application/application'; +import { getDirectory, pageGenerator } from './page'; + +describe('app', () => { + let tree: Tree; + const name = 'my-app'; + + describe('getDirectory', () => { + it('should return "pages" if no directory is provided', () => { + expect(getDirectory(undefined)).toEqual('pages'); + }); + + it('should return the directory unchanged if it already starts with "pages/"', () => { + expect(getDirectory('pages/someDir')).toEqual('pages/someDir'); + }); + + it('should append "/pages" to the directory if it does not start with "pages/" 2', () => { + expect(getDirectory('someDir/')).toEqual('someDir/pages'); + }); + + it('should append "/pages" to the directory if it does not start with "pages/"', () => { + expect(getDirectory('someDir')).toEqual('someDir/pages'); + }); + + it('should work with an empty string', () => { + expect(getDirectory('')).toEqual('pages'); + }); + }); + describe('generated files content - as-provided', () => { + beforeAll(async () => { + tree = createTreeWithEmptyWorkspace(); + await applicationGenerator(tree, { + name, + projectNameAndRootFormat: 'as-provided', + }); + }); + it('should create a new page in the correct location', async () => { + await pageGenerator(tree, { + name: 'about', + project: name, + }); + + expect(tree.exists('my-app/pages/About.vue')).toBeTruthy(); + }); + + it('should create a new page in the correct location for nested directory', async () => { + await pageGenerator(tree, { + name: 'about', + project: name, + }); + + expect(tree.exists('my-app/pages/About.vue')).toBeTruthy(); + }); + }); +}); diff --git a/packages/nuxt/src/generators/page/page.ts b/packages/nuxt/src/generators/page/page.ts new file mode 100644 index 00000000000000..1c5e495ae26f03 --- /dev/null +++ b/packages/nuxt/src/generators/page/page.ts @@ -0,0 +1,36 @@ +import { + formatFiles, + joinPathFragments, + runTasksInSerial, + Tree, +} from '@nx/devkit'; +import { componentGenerator } from '../component/component'; +import { Schema } from './schema'; + +export async function pageGenerator(host: Tree, options: Schema) { + const pageGenerator = await componentGenerator(host, { + ...options, + directory: getDirectory(options.directory), + skipTests: true, + flat: true, + pascalCaseFiles: options.pascalCaseFiles ?? true, + pascalCaseDirectory: options.pascalCaseDirectory ?? true, + skipFormat: true, + }); + + if (!options.skipFormat) { + await formatFiles(host); + } + + return runTasksInSerial(pageGenerator); +} + +export function getDirectory(directory: string) { + return directory?.length > 0 + ? directory.startsWith('pages/') + ? directory + : joinPathFragments(directory + '/pages') + : 'pages'; +} + +export default pageGenerator; diff --git a/packages/nuxt/src/generators/page/schema.d.ts b/packages/nuxt/src/generators/page/schema.d.ts new file mode 100644 index 00000000000000..228d53dc340aea --- /dev/null +++ b/packages/nuxt/src/generators/page/schema.d.ts @@ -0,0 +1,9 @@ +export interface Schema { + project: string; + name: string; + pascalCaseFiles?: boolean; + pascalCaseDirectory?: boolean; + directory?: string; + fileName?: string; + skipFormat?: boolean; +} diff --git a/packages/nuxt/src/generators/page/schema.json b/packages/nuxt/src/generators/page/schema.json new file mode 100644 index 00000000000000..e8b058951c9389 --- /dev/null +++ b/packages/nuxt/src/generators/page/schema.json @@ -0,0 +1,62 @@ +{ + "$schema": "http://json-schema.org/schema", + "cli": "nx", + "$id": "NxNuxtPage", + "title": "Create a Nuxt page", + "description": "Create a Nuxt page for Nx.", + "type": "object", + "examples": [ + { + "command": "nx g page new-page --project=myapp", + "description": "Generate a page in the `myapp` application" + } + ], + "properties": { + "project": { + "type": "string", + "description": "The name of the project.", + "alias": "p", + "$default": { + "$source": "projectName" + }, + "x-prompt": "What is the name of the project for this page?", + "x-priority": "important" + }, + "name": { + "type": "string", + "description": "The name of the page.", + "$default": { + "$source": "argv", + "index": 0 + }, + "x-prompt": "What name would you like to use for the page?", + "x-priority": "important" + }, + "directory": { + "type": "string", + "description": "Create the page under this directory - all nested directories will be created under the pages directory.", + "alias": "dir" + }, + "pascalCaseFiles": { + "type": "boolean", + "description": "Use pascal case component file name (e.g. `App.vue`).", + "alias": "P" + }, + "pascalCaseDirectory": { + "type": "boolean", + "description": "Use pascal case directory name (e.g. `App/App.vue`).", + "alias": "R" + }, + "fileName": { + "type": "string", + "description": "Create a component with this file name." + }, + "skipFormat": { + "description": "Skip formatting files.", + "type": "boolean", + "default": false, + "x-priority": "internal" + } + }, + "required": ["name", "project"] +}