Skip to content

Commit

Permalink
feat(vue): init and lib generators
Browse files Browse the repository at this point in the history
  • Loading branch information
mandarini committed Sep 6, 2023
1 parent 5610f2a commit ca05078
Show file tree
Hide file tree
Showing 42 changed files with 2,264 additions and 1 deletion.
14 changes: 14 additions & 0 deletions docs/map.json
Original file line number Diff line number Diff line change
Expand Up @@ -2069,6 +2069,20 @@
}
]
},
{
"name": "vue",
"id": "vue",
"description": "Vue package.",
"itemList": [
{
"id": "overview",
"path": "/packages/vue",
"name": "Overview of the Nx Vue Plugin",
"description": "The Nx Plugin for Vue contains generators for managing Vue applications and libraries within an Nx workspace. This page also explains how to configure Vue on your Nx workspace.",
"file": "shared/packages/vue/vue-plugin"
}
]
},
{
"name": "webpack",
"id": "webpack",
Expand Down
10 changes: 10 additions & 0 deletions docs/packages.json
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,16 @@
"generators": ["init", "configuration", "vitest"]
}
},
{
"name": "vue",
"packageName": "vue",
"description": "The Vue plugin for Nx contains executors and generators for managing Vue applications and libraries within an Nx workspace. It provides:\n\n\n- Integration with libraries such as Jest, Cypress, and Storybook.\n\n- Generators for applications, libraries, components, hooks, and more.\n\n- Library build support for publishing packages to npm or other registries.\n\n- Utilities for automatic workspace refactoring.",
"path": "generated/packages/vite.json",
"schemas": {
"executors": [],
"generators": ["init", "library", "application", "component"]
}
},
{
"name": "web",
"packageName": "web",
Expand Down
64 changes: 64 additions & 0 deletions docs/shared/packages/vue/vue-plugin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
---
title: Overview of the Nx Vue Plugin
description: The Nx Plugin for Vue contains generators for managing Vue applications and libraries within an Nx workspace. This page also explains how to configure Vue on your Nx workspace.
---

{% callout type="caution" title="`@nx/vue` is not available yet" %}
The `@nx/vue` plugin is not available yet. It will be released soon.
{% /callout %}

The Nx plugin for [Vue](https://vuejs.org/).

## Setting up a new Nx workspace with Vue

You can create a new workspace that uses Vue with one of the following commands:

- Generate a new monorepo with a Vue app set up with Vue

```shell
npx create-nx-workspace@latest --preset=vue
```

## Add Vue to an existing workspace

There is a number of ways to use Vue in your existing workspace.

### Install the `@nx/vue` plugin

{% tabs %}
{% tab label="npm" %}

```shell
npm install -D @nx/vue
```

{% /tab %}
{% tab label="yarn" %}

```shell
yarn add -D @nx/vue
```

{% /tab %}
{% tab label="pnpm" %}

```shell
pnpm install -D @nx/vue
```

{% /tab %}
{% /tabs %}

### Generate a new project using Vue

To generate a Vue application, run the following:

```bash
nx g @nx/vue:app my-app
```

To generate a Vue library, run the following:

```bash
nx g @nx/vue:lib my-lib
```
Empty file added packages/vue/docs/.gitkeep
Empty file.
18 changes: 17 additions & 1 deletion packages/vue/generators.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
{
"name": "Nx Vue",
"version": "0.1",
"generators": {}
"generators": {
"init": {
"factory": "./src/generators/init/init#vueInitSchematic",
"schema": "./src/generators/init/schema.json",
"description": "Initialize the `@nrwl/vue` plugin.",
"aliases": ["ng-add"],
"hidden": true
},

"library": {
"factory": "./src/generators/library/library",
"schema": "./src/generators/library/schema.json",
"aliases": ["lib"],
"x-type": "library",
"description": "Create a Vue library."
}
}
}
5 changes: 5 additions & 0 deletions packages/vue/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export * from './src/utils/versions';
export { libraryGenerator } from './src/generators/library/library';
export { componentGenerator } from './src/generators/component/component';
export { type InitSchema } from './src/generators/init/schema';
export { vueInitGenerator } from './src/generators/init/init';
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`component --export should add to index.ts barrel 1`] = `
"export * from './lib/hello/hello';
"
`;

exports[`component should generate files 1`] = `
"<script setup lang="ts">
defineProps<{}>();
</script>
<template>
<div>
<p>Welcome to Hello!</p>
</div>
</template>
<style scoped>
div {
color: pink;
}
</style>
"
`;

exports[`component should generate files 2`] = `
"import { describe, it, expect } from 'vitest';
import { mount } from '@vue/test-utils';
import Hello from '../hello';
describe('Hello', () => {
it('renders properly', () => {
const wrapper = mount(Hello, { props: {} });
expect(wrapper.text()).toContain('Welcome to Hello');
});
});
"
`;
180 changes: 180 additions & 0 deletions packages/vue/src/generators/component/component.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
import { installedCypressVersion } from '@nx/cypress/src/utils/cypress-version';
import { logger, readJson, Tree } from '@nx/devkit';
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
import { componentGenerator } from './component';
import { createLib } from '../../utils/testing-generators';

// need to mock cypress otherwise it'll use the nx installed version from package.json
// which is v9 while we are testing for the new v10 version
jest.mock('@nx/cypress/src/utils/cypress-version');

describe('component', () => {
let appTree: Tree;
let projectName: string;
let mockedInstalledCypressVersion: jest.Mock<
ReturnType<typeof installedCypressVersion>
> = installedCypressVersion as never;

beforeEach(async () => {
mockedInstalledCypressVersion.mockReturnValue(10);
projectName = 'my-lib';
appTree = createTreeWithEmptyWorkspace();
// await createApp(appTree, 'my-app');
await createLib(appTree, projectName);
jest.spyOn(logger, 'warn').mockImplementation(() => {});
jest.spyOn(logger, 'debug').mockImplementation(() => {});
});

afterEach(() => {
jest.restoreAllMocks();
});

it('should generate files', async () => {
await componentGenerator(appTree, {
name: 'hello',
project: projectName,
});

expect(appTree.exists('my-lib/src/lib/hello/hello.vue')).toBeTruthy();
expect(
appTree.exists('my-lib/src/lib/hello/__tests__/hello.spec.ts')
).toBeTruthy();

expect(
appTree.read('my-lib/src/lib/hello/hello.vue', 'utf-8')
).toMatchSnapshot();
expect(
appTree.read('my-lib/src/lib/hello/__tests__/hello.spec.ts', 'utf-8')
).toMatchSnapshot();
});

// we don't have app generator yet
xit('should generate files for an app', async () => {
await componentGenerator(appTree, {
name: 'hello',
project: 'my-app',
});

expect(appTree.exists('my-app/src/app/hello/hello.tsx')).toBeTruthy();
expect(appTree.exists('my-app/src/app/hello/hello.spec.ts')).toBeTruthy();
expect(
appTree.exists('my-app/src/app/hello/hello.module.css')
).toBeTruthy();
});

describe('--export', () => {
it('should add to index.ts barrel', async () => {
await componentGenerator(appTree, {
name: 'hello',
project: projectName,
export: true,
});
expect(appTree.read('my-lib/src/index.ts', 'utf-8')).toMatchSnapshot();
});

// no app generator yet
xit('should not export from an app', async () => {
await componentGenerator(appTree, {
name: 'hello',
project: 'my-app',
export: true,
});

expect(appTree.read('my-app/src/index.ts', 'utf-8')).toMatchSnapshot();
});
});

describe('--pascalCaseFiles', () => {
it('should generate component files with upper case names', async () => {
await componentGenerator(appTree, {
name: 'hello',
project: projectName,
pascalCaseFiles: true,
});
expect(appTree.exists('my-lib/src/lib/hello/Hello.vue')).toBeTruthy();
expect(
appTree.exists('my-lib/src/lib/hello/__tests__/Hello.spec.ts')
).toBeTruthy();
});
});

describe('--pascalCaseDirectory', () => {
it('should generate component files with pascal case directories', async () => {
await componentGenerator(appTree, {
name: 'hello-world',
project: projectName,
pascalCaseFiles: true,
pascalCaseDirectory: true,
});
expect(
appTree.exists('my-lib/src/lib/HelloWorld/HelloWorld.vue')
).toBeTruthy();
expect(
appTree.exists('my-lib/src/lib/HelloWorld/__tests__/HelloWorld.spec.ts')
).toBeTruthy();
});
});

// TODO: figure out routing
xdescribe('--routing', () => {
it('should add routes to the component', async () => {
await componentGenerator(appTree, {
name: 'hello',
project: projectName,
routing: true,
});

const content = appTree.read('my-lib/src/lib/hello/hello.tsx').toString();
expect(content).toContain('react-router-dom');
expect(content).toMatch(/<Route\s*path="\/"/);
expect(content).toMatch(/<Link\s*to="\/"/);

const packageJSON = readJson(appTree, 'package.json');
expect(packageJSON.dependencies['react-router-dom']).toBeDefined();
});
});

describe('--directory', () => {
it('should create component under the directory', async () => {
await componentGenerator(appTree, {
name: 'hello',
project: projectName,
directory: 'components',
});

expect(appTree.exists('/my-lib/src/components/hello/hello.vue'));
});

it('should create with nested directories', async () => {
await componentGenerator(appTree, {
name: 'helloWorld',
project: projectName,
directory: 'lib/foo',
});

expect(appTree.exists('/my-lib/src/lib/foo/hello-world/hello-world.vue'));
});
});

describe('--flat', () => {
it('should create in project directory rather than in its own folder', async () => {
await componentGenerator(appTree, {
name: 'hello',
project: projectName,
flat: true,
});

expect(appTree.exists('/my-lib/src/lib/hello.vue'));
});
it('should work with custom directory path', async () => {
await componentGenerator(appTree, {
name: 'hello',
project: projectName,
flat: true,
directory: 'components',
});

expect(appTree.exists('/my-lib/src/components/hello.vue'));
});
});
});
Loading

0 comments on commit ca05078

Please sign in to comment.