Skip to content

Commit

Permalink
feat(nuxt): component and page generators
Browse files Browse the repository at this point in the history
  • Loading branch information
mandarini committed Oct 10, 2023
1 parent 8a67eb2 commit 28f6c1f
Show file tree
Hide file tree
Showing 28 changed files with 767 additions and 195 deletions.
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@
"@ngrx/router-store": "~16.0.0",
"@ngrx/store": "~16.0.0",
"@nguniversal/builders": "~16.2.0",
"@nuxt/kit": "^3.7.4",
"@nuxt/schema": "^3.7.4",
"@nx/angular": "16.10.0-beta.1",
"@nx/cypress": "16.10.0-beta.1",
"@nx/devkit": "16.10.0-beta.1",
Expand Down Expand Up @@ -156,6 +158,7 @@
"cytoscape-popper": "^2.0.0",
"cz-git": "^1.4.0",
"czg": "^1.4.0",
"defu": "^6.1.2",
"detect-port": "^1.5.1",
"dotenv": "~16.3.1",
"dotenv-expand": "^10.0.0",
Expand Down
13 changes: 13 additions & 0 deletions packages/nuxt/generators.json
Original file line number Diff line number Diff line change
Expand Up @@ -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."
}
}
}
5 changes: 4 additions & 1 deletion packages/nuxt/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,17 @@
"migrations": "./migrations.json"
},
"dependencies": {
"defu": "^6.1.2",
"fs-extra": "^11.1.0",
"tslib": "^2.3.0",
"@nx/devkit": "file:../devkit",
"@nx/js": "file:../js",
"@nx/linter": "file:../linter",
"@nx/vue": "file:../vue",
"@nx/cypress": "file:../cypress",
"@nx/playwright": "file:../playwright"
"@nx/playwright": "file:../playwright",
"@nuxt/kit": "^3.7.4",
"@nuxt/schema": "^3.7.4"
},
"publishConfig": {
"access": "public"
Expand Down
12 changes: 11 additions & 1 deletion packages/nuxt/plugins/with-nuxt.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
import { join, resolve } from 'path';
import { workspaceRoot } from '@nx/devkit';
import { existsSync } from 'fs-extra';
import { defineNuxtModule } from '@nuxt/kit';
import { defu } from 'defu';
import { NuxtModule } from '@nuxt/schema';

export const NxNuxtModule: NuxtModule = defineNuxtModule({
meta: { name: '@nx/nuxt/module', configKey: 'nx' },
setup(_options, nuxt) {
nuxt.options.alias = defu(nuxt.options.alias, nxTsPaths());
},
});

/**
* read the compilerOptions.paths option from a tsconfig and return as aliases for Nuxt
**/
export function nxTsPaths() {
function nxTsPaths() {
const tsConfigPath = getTsConfig(join(workspaceRoot, 'tsconfig.base.json'));
const tsPaths = require(tsConfigPath)?.compilerOptions?.paths as Record<
string,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,33 @@ exports[`app generated files content - as-provided should add nuxt entries in .g
.cache"
`;

exports[`app generated files content - as-provided should configure eslint correctly 1`] = `
"{
"extends": ["@nuxt/eslint-config", "../.eslintrc.json"],
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx", "*.vue"],
"rules": {}
}
]
}
"
`;

exports[`app generated files content - as-provided should configure nuxt correctly 1`] = `
"import { NxNuxtModule } from '@nx/nuxt';
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
modules: [NxNuxtModule],
srcDir: 'src',
devtools: { enabled: true },
css: ['~/assets/css/styles.css'],
});
"
`;

exports[`app generated files content - as-provided should configure tsconfig and project.json correctly 1`] = `
"{
"name": "my-app",
Expand Down Expand Up @@ -86,16 +113,16 @@ exports[`app generated files content - as-provided should create all new files i
".prettierignore",
"my-app/project.json",
"my-app/.npmrc",
"my-app/app.vue",
"my-app/assets/css/styles.css",
"my-app/components/NxWelcome.vue",
"my-app/nuxt.config.ts",
"my-app/pages/About.vue",
"my-app/pages/index.vue",
"my-app/public/.gitkeep",
"my-app/public/favicon.ico",
"my-app/server/api/greet.ts",
"my-app/server/tsconfig.json",
"my-app/src/app.vue",
"my-app/src/assets/css/styles.css",
"my-app/src/components/NxWelcome.vue",
"my-app/src/pages/about.vue",
"my-app/src/pages/index.vue",
"my-app/src/public/.gitkeep",
"my-app/src/public/favicon.ico",
"my-app/src/server/api/greet.ts",
"my-app/src/server/tsconfig.json",
"my-app/tsconfig.json",
".gitignore",
".eslintrc.json",
Expand Down
8 changes: 8 additions & 0 deletions packages/nuxt/src/generators/application/application.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ describe('app', () => {
expect(tree.read('.gitignore', 'utf-8')).toMatchSnapshot();
});

it('should configure nuxt correctly', () => {
expect(tree.read('my-app/nuxt.config.ts', 'utf-8')).toMatchSnapshot();
});

it('should configure eslint correctly', () => {
expect(tree.read('my-app/.eslintrc.json', 'utf-8')).toMatchSnapshot();
});

it('should configure tsconfig and project.json correctly', () => {
expect(tree.read('my-app/project.json', 'utf-8')).toMatchSnapshot();
expect(tree.read('my-app/tsconfig.json', 'utf-8')).toMatchSnapshot();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,9 @@
import { nxTsPaths } from '@nx/nuxt';
import { NxNuxtModule } from '@nx/nuxt';

// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
/**
* Nuxt recommends setting custom alias from a tsconfig here,
* instead of in tsconfig since it will override the auto generated tsconfig.
* all aliases added here will be added to the auto generated tsconfig.
* Other projects generated with Nx will be added to the root level tsconfig.base.json
* which might want to be used in this project.
*
* https://nuxt.com/docs/guide/directory-structure/tsconfig
**/
alias: nxTsPaths(),
modules: [NxNuxtModule],
srcDir: 'src',
devtools: { enabled: true },
css: ['~/assets/css/styles.css'],
});

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const route = useRoute();
<NuxtLink to="/about">About</NuxtLink>
</nav>
</header>
<router-view></router-view>
<nuxt-page></nuxt-page>
</main>
</template>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<template>
<NxWelcome title="<%= title %>" />
</template>



27 changes: 27 additions & 0 deletions packages/nuxt/src/generators/component/component.spec.ts
Original file line number Diff line number Diff line change
@@ -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/src/components/hello/hello.vue')).toBeTruthy();
});
});
});
23 changes: 23 additions & 0 deletions packages/nuxt/src/generators/component/component.ts
Original file line number Diff line number Diff line change
@@ -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;
13 changes: 13 additions & 0 deletions packages/nuxt/src/generators/component/schema.d.ts
Original file line number Diff line number Diff line change
@@ -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';
}
91 changes: 91 additions & 0 deletions packages/nuxt/src/generators/component/schema.json
Original file line number Diff line number Diff line change
@@ -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"]
}
Loading

0 comments on commit 28f6c1f

Please sign in to comment.