diff --git a/apps/nuxt-e2e/tests/nuxt.test.ts b/apps/nuxt-e2e/tests/nuxt.test.ts index 6a6b4911..8b325342 100644 --- a/apps/nuxt-e2e/tests/nuxt.test.ts +++ b/apps/nuxt-e2e/tests/nuxt.test.ts @@ -8,9 +8,12 @@ import { } from '@nrwl/nx-plugin/testing'; describe('nuxt e2e', () => { + beforeAll(() => { + ensureNxProject('@nx-plus/nuxt', 'dist/libs/nuxt'); + }); + it('should generate app', async () => { const appName = uniq('app'); - ensureNxProject('@nx-plus/nuxt', 'dist/libs/nuxt'); await runNxCommandAsync(`generate @nx-plus/nuxt:app ${appName}`); const lintResult = await runNxCommandAsync(`lint ${appName}`); @@ -57,7 +60,6 @@ describe('nuxt e2e', () => { it('should report lint error in index.vue', async () => { const appName = uniq('app'); - ensureNxProject('@nx-plus/nuxt', 'dist/libs/nuxt'); await runNxCommandAsync(`generate @nx-plus/nuxt:app ${appName}`); updateFile( @@ -73,7 +75,6 @@ describe('nuxt e2e', () => { it('should generate static app', async () => { const appName = uniq('app'); - ensureNxProject('@nx-plus/nuxt', 'dist/libs/nuxt'); await runNxCommandAsync(`generate @nx-plus/nuxt:app ${appName}`); await runNxCommandAsync(`static ${appName}`); @@ -92,7 +93,6 @@ describe('nuxt e2e', () => { describe('--directory subdir', () => { it('should generate app', async () => { const appName = uniq('app'); - ensureNxProject('@nx-plus/nuxt', 'dist/libs/nuxt'); await runNxCommandAsync( `generate @nx-plus/nuxt:app ${appName} --directory subdir` ); @@ -123,7 +123,6 @@ describe('nuxt e2e', () => { it('should generate static app', async () => { const appName = uniq('app'); - ensureNxProject('@nx-plus/nuxt', 'dist/libs/nuxt'); await runNxCommandAsync( `generate @nx-plus/nuxt:app ${appName} --directory subdir` ); diff --git a/apps/vite-e2e/jest.config.js b/apps/vite-e2e/jest.config.js new file mode 100644 index 00000000..9d574a50 --- /dev/null +++ b/apps/vite-e2e/jest.config.js @@ -0,0 +1,14 @@ +module.exports = { + displayName: 'vite-e2e', + preset: '../../jest.preset.js', + globals: { + 'ts-jest': { + tsconfig: '/tsconfig.spec.json', + }, + }, + transform: { + '^.+\\.[tj]s$': 'ts-jest', + }, + moduleFileExtensions: ['ts', 'js', 'html'], + coverageDirectory: '../../coverage/apps/vite-e2e', +}; diff --git a/apps/vite-e2e/tests/vite.spec.ts b/apps/vite-e2e/tests/vite.spec.ts new file mode 100644 index 00000000..9a5834d1 --- /dev/null +++ b/apps/vite-e2e/tests/vite.spec.ts @@ -0,0 +1,58 @@ +import { tags } from '@angular-devkit/core'; +import { + checkFilesExist, + ensureNxProject, + runNxCommandAsync, + uniq, + updateFile, +} from '@nrwl/nx-plugin/testing'; +import { join } from 'path'; + +describe('vite e2e', () => { + it('should create vite app', async () => { + const appName = uniq('vite'); + ensureNxProject('@nx-plus/vite', 'dist/libs/vite'); + await runNxCommandAsync(`generate @nx-plus/vite:app ${appName}`); + + const lintResult = await runNxCommandAsync(`lint ${appName}`); + expect(lintResult.stdout).toContain('All files pass linting.'); + + const testResult = await runNxCommandAsync(`test ${appName}`); + expect(testResult.stderr).toContain(tags.stripIndent` + Test Suites: 1 passed, 1 total + Tests: 1 passed, 1 total + Snapshots: 0 total + `); + + disableHashing(appName); + const buildResult = await runNxCommandAsync(`build ${appName}`); + checkFilesExist( + `dist/apps/${appName}/index.html`, + `dist/apps/${appName}/assets/index.css`, + `dist/apps/${appName}/assets/index.js`, + `dist/apps/${appName}/assets/vendor.js` + ); + // Cannot disable hashing for assets + // see: https://github.com/vitejs/vite/issues/2944 + expect(buildResult.stdout).toContain(`dist/apps/${appName}/assets/logo`); + + const e2eResult = await runNxCommandAsync(`e2e ${appName}-e2e --headless`); + expect(e2eResult.stdout).toContain('All specs passed!'); + }, 100000); +}); + +function disableHashing(app: string, directory: string = '') { + updateFile(join('apps', app, directory, 'vite.config.ts'), (content) => + content.replace( + 'emptyOutDir: true,', + `emptyOutDir: true, + rollupOptions: { + output: { + entryFileNames: \`assets/[name].js\`, + chunkFileNames: \`assets/[name].js\`, + assetFileNames: \`assets/[name].[ext]\` + } + }` + ) + ); +} diff --git a/apps/vite-e2e/tsconfig.json b/apps/vite-e2e/tsconfig.json new file mode 100644 index 00000000..b9c9d953 --- /dev/null +++ b/apps/vite-e2e/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.base.json", + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/apps/vite-e2e/tsconfig.spec.json b/apps/vite-e2e/tsconfig.spec.json new file mode 100644 index 00000000..29efa430 --- /dev/null +++ b/apps/vite-e2e/tsconfig.spec.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "include": ["**/*.spec.ts", "**/*.d.ts"] +} diff --git a/apps/vue-e2e/tests/vue-2.test.ts b/apps/vue-e2e/tests/vue-2.test.ts index 89210af0..4f754f21 100644 --- a/apps/vue-e2e/tests/vue-2.test.ts +++ b/apps/vue-e2e/tests/vue-2.test.ts @@ -8,11 +8,14 @@ import { } from '@nrwl/nx-plugin/testing'; import { runNxProdCommandAsync, testGeneratedApp } from './utils'; -describe('vue 2 e2e', () => { +describe.skip('vue 2 e2e', () => { describe('app', () => { + beforeAll(() => { + ensureNxProject('@nx-plus/vue', 'dist/libs/vue'); + }); + it('should generate app', async () => { const appName = uniq('app'); - ensureNxProject('@nx-plus/vue', 'dist/libs/vue'); await runNxCommandAsync(`generate @nx-plus/vue:app ${appName}`); await testGeneratedApp(appName, { @@ -27,7 +30,6 @@ describe('vue 2 e2e', () => { describe('--routing', () => { it('should generate app with routing', async () => { const appName = uniq('app'); - ensureNxProject('@nx-plus/vue', 'dist/libs/vue'); await runNxCommandAsync( `generate @nx-plus/vue:app ${appName} --routing` ); @@ -52,7 +54,6 @@ describe('vue 2 e2e', () => { describe('--style', () => { it('should generate app with scss', async () => { const appName = uniq('app'); - ensureNxProject('@nx-plus/vue', 'dist/libs/vue'); await runNxCommandAsync( `generate @nx-plus/vue:app ${appName} --style scss` ); @@ -68,7 +69,6 @@ describe('vue 2 e2e', () => { it('should generate app with stylus', async () => { const appName = uniq('app'); - ensureNxProject('@nx-plus/vue', 'dist/libs/vue'); await runNxCommandAsync( `generate @nx-plus/vue:app ${appName} --style stylus` ); @@ -84,7 +84,6 @@ describe('vue 2 e2e', () => { it('should generate app with less', async () => { const appName = uniq('app'); - ensureNxProject('@nx-plus/vue', 'dist/libs/vue'); await runNxCommandAsync( `generate @nx-plus/vue:app ${appName} --style less` ); @@ -102,7 +101,6 @@ describe('vue 2 e2e', () => { describe('vuex', () => { it('should generate app and add vuex', async () => { const appName = uniq('app'); - ensureNxProject('@nx-plus/vue', 'dist/libs/vue'); await runNxCommandAsync(`generate @nx-plus/vue:app ${appName}`); await runNxCommandAsync(`generate @nx-plus/vue:vuex ${appName}`); @@ -118,7 +116,6 @@ describe('vue 2 e2e', () => { it('should generate app with routing and add vuex', async () => { const appName = uniq('app'); - ensureNxProject('@nx-plus/vue', 'dist/libs/vue'); await runNxCommandAsync(`generate @nx-plus/vue:app ${appName} --routing`); await runNxCommandAsync(`generate @nx-plus/vue:vuex ${appName}`); @@ -140,7 +137,6 @@ describe('vue 2 e2e', () => { it('should report lint error in App.vue', async () => { const appName = uniq('app'); - ensureNxProject('@nx-plus/vue', 'dist/libs/vue'); await runNxCommandAsync(`generate @nx-plus/vue:app ${appName}`); updateFile( @@ -157,7 +153,6 @@ describe('vue 2 e2e', () => { describe('--directory subdir', () => { it('should generate app', async () => { const appName = uniq('app'); - ensureNxProject('@nx-plus/vue', 'dist/libs/vue'); await runNxCommandAsync( `generate @nx-plus/vue:app ${appName} --directory subdir` ); @@ -177,9 +172,12 @@ describe('vue 2 e2e', () => { }); describe('library', () => { + beforeAll(() => { + ensureNxProject('@nx-plus/vue', 'dist/libs/vue'); + }); + it('should generate lib', async () => { const lib = uniq('lib'); - ensureNxProject('@nx-plus/vue', 'dist/libs/vue'); await runNxCommandAsync(`generate @nx-plus/vue:lib ${lib}`); const lintResult = await runNxCommandAsync(`lint ${lib}`); @@ -195,7 +193,6 @@ describe('vue 2 e2e', () => { it('should generate publishable lib', async () => { const lib = uniq('lib'); - ensureNxProject('@nx-plus/vue', 'dist/libs/vue'); await runNxCommandAsync(`generate @nx-plus/vue:lib ${lib} --publishable`); let buildResult = await runNxProdCommandAsync(`build ${lib}`); @@ -248,7 +245,6 @@ describe('vue 2 e2e', () => { describe('--directory subdir', () => { it('should generate publishable lib', async () => { const lib = uniq('lib'); - ensureNxProject('@nx-plus/vue', 'dist/libs/vue'); await runNxCommandAsync( `generate @nx-plus/vue:lib ${lib} --directory subdir --publishable` ); diff --git a/apps/vue-e2e/tests/vue-3.test.ts b/apps/vue-e2e/tests/vue-3.test.ts index 3a510677..157c07ed 100644 --- a/apps/vue-e2e/tests/vue-3.test.ts +++ b/apps/vue-e2e/tests/vue-3.test.ts @@ -10,9 +10,12 @@ import { runNxProdCommandAsync, testGeneratedApp } from './utils'; describe('vue 3 e2e', () => { describe('app', () => { + beforeAll(() => { + ensureNxProject('@nx-plus/vue', 'dist/libs/vue'); + }); + it('should generate app', async () => { const appName = uniq('app'); - ensureNxProject('@nx-plus/vue', 'dist/libs/vue'); await runNxCommandAsync( `generate @nx-plus/vue:app ${appName} --vueVersion 3` ); @@ -29,7 +32,6 @@ describe('vue 3 e2e', () => { describe('--routing', () => { it('should generate app with routing', async () => { const appName = uniq('app'); - ensureNxProject('@nx-plus/vue', 'dist/libs/vue'); await runNxCommandAsync( `generate @nx-plus/vue:app ${appName} --vueVersion 3 --routing` ); @@ -54,7 +56,6 @@ describe('vue 3 e2e', () => { describe('vuex', () => { it('should generate app and add vuex', async () => { const appName = uniq('app'); - ensureNxProject('@nx-plus/vue', 'dist/libs/vue'); await runNxCommandAsync( `generate @nx-plus/vue:app ${appName} --vueVersion 3` ); @@ -72,7 +73,6 @@ describe('vue 3 e2e', () => { it('should generate app with routing and add vuex', async () => { const appName = uniq('app'); - ensureNxProject('@nx-plus/vue', 'dist/libs/vue'); await runNxCommandAsync( `generate @nx-plus/vue:app ${appName} --vueVersion 3 --routing` ); @@ -96,7 +96,6 @@ describe('vue 3 e2e', () => { it('should report lint error in App.vue', async () => { const appName = uniq('app'); - ensureNxProject('@nx-plus/vue', 'dist/libs/vue'); await runNxCommandAsync( `generate @nx-plus/vue:app ${appName} --vueVersion 3` ); @@ -111,12 +110,30 @@ describe('vue 3 e2e', () => { }); expect(result.stderr).toContain('Lint errors found in the listed files.'); }, 300000); + + it('should generate component', async () => { + const appName = uniq('app'); + await runNxCommandAsync( + `generate @nx-plus/vue:app ${appName} --vueVersion 3` + ); + + await runNxCommandAsync( + `generate @nx-plus/vue:component my-component --project ${appName}` + ); + + expect(() => + checkFilesExist(`apps/${appName}/src/MyComponent.vue`) + ).not.toThrow(); + }, 300000); }); describe('library', () => { + beforeAll(() => { + ensureNxProject('@nx-plus/vue', 'dist/libs/vue'); + }); + it('should generate lib', async () => { const lib = uniq('lib'); - ensureNxProject('@nx-plus/vue', 'dist/libs/vue'); await runNxCommandAsync( `generate @nx-plus/vue:lib ${lib} --vueVersion 3` ); @@ -134,7 +151,6 @@ describe('vue 3 e2e', () => { it('should generate publishable lib', async () => { const lib = uniq('lib'); - ensureNxProject('@nx-plus/vue', 'dist/libs/vue'); await runNxCommandAsync( `generate @nx-plus/vue:lib ${lib} --vueVersion 3 --publishable` ); @@ -185,43 +201,20 @@ describe('vue 3 e2e', () => { ) ).toThrow(); }, 300000); - }); - - describe('component', () => { - describe('inside an app', () => { - it('should generate component', async () => { - const appName = uniq('app'); - ensureNxProject('@nx-plus/vue', 'dist/libs/vue'); - await runNxCommandAsync( - `generate @nx-plus/vue:app ${appName} --vueVersion 3` - ); - await runNxCommandAsync( - `generate @nx-plus/vue:component my-component --project ${appName}` - ); - - expect(() => - checkFilesExist(`apps/${appName}/src/MyComponent.vue`) - ).not.toThrow(); - }, 300000); - }); - - describe('inside a library', () => { - it('should generate component', async () => { - const libName = uniq('lib'); - ensureNxProject('@nx-plus/vue', 'dist/libs/vue'); - await runNxCommandAsync( - `generate @nx-plus/vue:lib ${libName} --vueVersion 3` - ); + it('should generate component', async () => { + const libName = uniq('lib'); + await runNxCommandAsync( + `generate @nx-plus/vue:lib ${libName} --vueVersion 3` + ); - await runNxCommandAsync( - `generate @nx-plus/vue:component my-component --project ${libName}` - ); + await runNxCommandAsync( + `generate @nx-plus/vue:component my-component --project ${libName}` + ); - expect(() => - checkFilesExist(`libs/${libName}/src/lib/MyComponent.vue`) - ).not.toThrow(); - }, 300000); - }); + expect(() => + checkFilesExist(`libs/${libName}/src/lib/MyComponent.vue`) + ).not.toThrow(); + }, 300000); }); }); diff --git a/libs/vite/.babelrc b/libs/vite/.babelrc new file mode 100644 index 00000000..cf7ddd99 --- /dev/null +++ b/libs/vite/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": [["@nrwl/web/babel", { "useBuiltIns": "usage" }]] +} diff --git a/libs/vite/.eslintrc.json b/libs/vite/.eslintrc.json new file mode 100644 index 00000000..c3d6e153 --- /dev/null +++ b/libs/vite/.eslintrc.json @@ -0,0 +1,21 @@ +{ + "extends": ["../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "parserOptions": { + "project": ["libs/vite/tsconfig.*?.json"] + }, + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/libs/vite/README.md b/libs/vite/README.md new file mode 100644 index 00000000..6ebda71a --- /dev/null +++ b/libs/vite/README.md @@ -0,0 +1,153 @@ +# Nx Plus Vite + +> First class support for [Vite](https://vitejs.dev/) in your [Nx](https://nx.dev/) workspace. + +
+ +
+ +## Contents + +- [Prerequisite](#prerequisite) +- [Getting Started](#getting-started) +- [Schematics (i.e. code generation)](#schematics-ie-code-generation) +- [Builders (i.e. task runners)](#builders-ie-task-runners) +- [Configuring Vite](#configuring-vite) +- [Updating Nx Plus Vite](#updating-nx-plus-vite) + +## Prerequisite + +### Nx Workspace + +If you have not already, [create an Nx workspace](https://github.com/nrwl/nx#creating-an-nx-workspace) with the following: + +``` +npx create-nx-workspace@^12.0.0 +``` + +### Peer Dependencies + +If you have not already, install peer dependencies with the following: + +``` +# npm +npm install @nrwl/cypress@^12.0.0 @nrwl/jest@^12.0.0 @nrwl/linter@^12.0.0 --save-dev + +# yarn +yarn add @nrwl/cypress@^12.0.0 @nrwl/jest@^12.0.0 @nrwl/linter@^12.0.0 --dev +``` + +## Getting Started + +### Install Plugin + +``` +# npm +npm install @nx-plus/vite --save-dev + +# yarn +yarn add @nx-plus/vite --dev +``` + +### Generate Your App + +``` +nx g @nx-plus/vite:app my-app +``` + +### Serve Your App + +``` +nx serve my-app +``` + +## Schematics (i.e. code generation) + +### Application + +`nx g @nx-plus/vite:app [options]` + +| Arguments | Description | +| --------- | --------------------- | +| `` | The name of your app. | + +| Options | Default | Description | +| ------------------ | --------- | ---------------------------------------------- | +| `--tags` | - | Tags to use for linting (comma-delimited). | +| `--directory` | `apps` | A directory where the project is placed. | +| `--unitTestRunner` | `jest` | Test runner to use for unit tests. | +| `--e2eTestRunner` | `cypress` | Test runner to use for end to end (e2e) tests. | +| `--skipFormat` | `false` | Skip formatting files. | + +## Builders (i.e. task runners) + +### Server + +`nx serve [options]` + +| Arguments | Description | +| --------- | --------------------- | +| `` | The name of your app. | + +| Options | Default | Description | +| -------------- | ------- | ------------------------------------------------------ | +| `--config` | - | Use specified config file. | +| `--root` | - | Use specified root directory. | +| `--base` | '/' | Public base path. | +| `--host` | - | Specify hostname. | +| `--port` | - | Specify port. | +| `--https` | - | Use TLS + HTTP/2. | +| `--open` | - | Open browser on startup. | +| `--cors` | - | Enable cors. | +| `--strictPort` | - | Exit if specified port is already in use. | +| `--mode` | - | Set env mode. | +| `--force` | - | Force the optimizer to ignore the cache and re-bundle. | + +### Build + +`nx build [options]` + +| Arguments | Description | +| --------- | --------------------- | +| `` | The name of your app. | + +| Options | Default | Description | +| --------------------- | ---------- | -------------------------------------------------------- | +| `--config` | - | Use specified config file. | +| `--root` | - | Use specified root directory. | +| `--base` | '/' | Public base path. | +| `--target` | 'modules' | Transpile target. | +| `--outDir` | - | Output directory. | +| `--assetsDir` | '\_assets' | Directory under outDir to place assets in. | +| `--assetsInlineLimit` | `4096` | Static asset base64 inline threshold in bytes. | +| `--ssr` | - | Build specified entry for server-side rendering. | +| `--sourcemap` | `false` | Output source maps for build. | +| `--minify` | 'esbuild' | Enable/disable minification, or specify minifier to use. | +| `--manifest` | - | Emit build manifest json. | +| `--ssrManifest` | - | Emit ssr manifest json. | +| `--emptyOutDir` | - | Force empty outDir when it's outside of root. | +| `--mode` | - | Set env mode. | +| `--force` | - | Force the optimizer to ignore the cache and re-bundle. | +| `--watch` | - | Rebuilds when modules have changed on disk. | + +### Configuring Vite + +A `vite.config.js` can be found at the root of your project. See the [Vite documentation](https://vitejs.dev/config/) for more details. + +### Linting + +`nx lint [options]` + +We use `@nrwl/linter` for linting, so the options are as documented [here](https://github.com/nrwl/nx/blob/master/docs/angular/api-linter/builders/eslint.md#eslint). + +### Unit Testing + +`nx test [options]` + +We use `@nrwl/jest` for unit testing, so the options are as documented [here](https://github.com/nrwl/nx/blob/master/docs/angular/api-jest/builders/jest.md#jest). + +### E2E Testing + +`nx e2e [options]` + +We use `@nrwl/cypress` for e2e testing, so the options are as documented [here](https://github.com/nrwl/nx/blob/master/docs/angular/api-cypress/builders/cypress.md#cypress). diff --git a/libs/vite/executors.json b/libs/vite/executors.json new file mode 100644 index 00000000..9473e60f --- /dev/null +++ b/libs/vite/executors.json @@ -0,0 +1,27 @@ +{ + "$schema": "http://json-schema.org/schema", + "executors": { + "build": { + "implementation": "./src/executors/build/executor", + "schema": "./src/executors/build/schema.json", + "description": "build executor" + }, + "server": { + "implementation": "./src/executors/server/executor", + "schema": "./src/executors/server/schema.json", + "description": "serve executor" + } + }, + "builders": { + "build": { + "implementation": "./src/executors/browser/compat", + "schema": "./src/executors/browser/schema.json", + "description": "browser builder" + }, + "server": { + "implementation": "./src/executors/server/compat", + "schema": "./src/executors/server/schema.json", + "description": "server builder" + } + } +} diff --git a/libs/vite/generators.json b/libs/vite/generators.json new file mode 100644 index 00000000..ccd2193a --- /dev/null +++ b/libs/vite/generators.json @@ -0,0 +1,21 @@ +{ + "$schema": "http://json-schema.org/schema", + "name": "vite", + "version": "0.0.1", + "generators": { + "application": { + "factory": "./src/generators/application/generator#applicationGenerator", + "schema": "./src/generators/application/schema.json", + "description": "application generator", + "aliases": ["app"] + } + }, + "schematics": { + "application": { + "factory": "./src/generators/application/generator#applicationSchematic", + "schema": "./src/generators/application/schema.json", + "description": "application schematic", + "aliases": ["app"] + } + } +} diff --git a/libs/vite/jest.config.js b/libs/vite/jest.config.js new file mode 100644 index 00000000..f160989f --- /dev/null +++ b/libs/vite/jest.config.js @@ -0,0 +1,14 @@ +module.exports = { + displayName: 'vite', + preset: '../../jest.preset.js', + globals: { + 'ts-jest': { + tsconfig: '/tsconfig.spec.json', + }, + }, + transform: { + '^.+\\.[tj]sx?$': 'ts-jest', + }, + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], + coverageDirectory: '../../coverage/libs/vite', +}; diff --git a/libs/vite/package.json b/libs/vite/package.json new file mode 100644 index 00000000..e41f9048 --- /dev/null +++ b/libs/vite/package.json @@ -0,0 +1,37 @@ +{ + "name": "@nx-plus/vite", + "version": "12.2.0", + "main": "src/index.js", + "generators": "./generators.json", + "executors": "./executors.json", + "description": "Vite plugin for Nx", + "repository": { + "type": "git", + "url": "git+https://github.com/ZachJW34/nx-plus.git" + }, + "keywords": [ + "nx", + "vite", + "documentation", + "website" + ], + "author": "Zachary Williams (https://github.com/ZachJW34/)", + "contributors": [ + "Bucky Maler (http://buckymaler.com/)" + ], + "license": "MIT", + "bugs": { + "url": "https://github.com/ZachJW34/nx-plus/issues" + }, + "homepage": "https://github.com/ZachJW34/nx-plus/tree/master/libs/vite#readme", + "peerDependencies": { + "@nrwl/cypress": "^12.0.0", + "@nrwl/jest": "^12.0.0", + "@nrwl/linter": "^12.0.0", + "@nrwl/workspace": "^12.0.0" + }, + "dependencies": { + "@nrwl/devkit": "^12.0.0", + "semver": "^7.3.2" + } +} diff --git a/libs/vite/patch-nx-dep-graph.js b/libs/vite/patch-nx-dep-graph.js new file mode 100644 index 00000000..c9d5d80c --- /dev/null +++ b/libs/vite/patch-nx-dep-graph.js @@ -0,0 +1,29 @@ +const fs = require('fs'); +const path = require('path'); + +/** + * Patch dep-graph builder function to support Vue files. + * @see https://github.com/nrwl/nx/issues/2960 + */ +function patchNxDepGraph() { + const filePath = path.join( + process.env.INIT_CWD || '', + 'node_modules/@nrwl/workspace/src/core/project-graph/build-dependencies/typescript-import-locator.js' + ); + try { + const fileContent = fs.readFileSync(filePath).toString('utf-8'); + const replacement = "extension !== '.ts' && extension !== '.vue'"; + if (fileContent.includes(replacement)) { + return; + } + fs.writeFileSync( + filePath, + fileContent.replace("extension !== '.ts'", replacement) + ); + console.log('Successfully patched Nx dep-graph for Vue support.'); + } catch (err) { + console.error('Failed to patch Nx dep-graph for Vue support.', err); + } +} + +patchNxDepGraph(); diff --git a/libs/vite/src/app-root.ts b/libs/vite/src/app-root.ts new file mode 100644 index 00000000..5f1f2ca5 --- /dev/null +++ b/libs/vite/src/app-root.ts @@ -0,0 +1,24 @@ +import * as fs from 'fs-extra'; +import * as path from 'path'; + +export const appRootPath = pathInner(__dirname); + +function pathInner(dir: string): string { + if (path.dirname(dir) === dir) return process.cwd(); + if ( + fileExists(path.join(dir, 'workspace.json')) || + fileExists(path.join(dir, 'angular.json')) + ) { + return dir; + } else { + return pathInner(path.dirname(dir)); + } +} + +function fileExists(filePath: string): boolean { + try { + return fs.statSync(filePath).isFile(); + } catch (err) { + return false; + } +} diff --git a/libs/vite/src/executors/build/compat.ts b/libs/vite/src/executors/build/compat.ts new file mode 100644 index 00000000..c78195e9 --- /dev/null +++ b/libs/vite/src/executors/build/compat.ts @@ -0,0 +1,4 @@ +import { convertNxExecutor } from '@nrwl/devkit'; +import { default as buildExecutor } from './executor'; + +export default convertNxExecutor(buildExecutor); diff --git a/libs/vite/src/executors/build/executor.ts b/libs/vite/src/executors/build/executor.ts new file mode 100644 index 00000000..4234d86a --- /dev/null +++ b/libs/vite/src/executors/build/executor.ts @@ -0,0 +1,29 @@ +import { build, BuildOptions } from 'vite'; +import { ViteBuildExecutorSchema } from './schema'; +import { cleanViteOptions } from '../utils'; + +export default async function* runExecutor(options: ViteBuildExecutorSchema) { + try { + await build({ + base: options.base, + mode: options.mode, + configFile: options.config, + logLevel: options.logLevel, + clearScreen: options.clearScreen, + build: cleanViteOptions(options) as BuildOptions, + }); + yield { + success: true, + }; + if (options.watch) { + // This Promise intentionally never resolves, leaving the process running + // eslint-disable-next-line @typescript-eslint/no-empty-function + await new Promise<{ success: boolean }>(() => {}); + } + } catch (err) { + return { + success: false, + error: err, + }; + } +} diff --git a/libs/vite/src/executors/build/schema.d.ts b/libs/vite/src/executors/build/schema.d.ts new file mode 100644 index 00000000..4c87a939 --- /dev/null +++ b/libs/vite/src/executors/build/schema.d.ts @@ -0,0 +1,23 @@ +import { LogLevel } from 'vite'; + +export interface ViteBuildExecutorSchema { + config: string; + root?: string; + base?: string; + logLevel?: LogLevel; + clearScreen?: boolean; + debug?: string | boolean; + filter?: string; + target?: string; + outDir?: string; + assetsDir?: string; + assetsInlineLimit?: number; + ssr?: string; + sourcemap?: boolean; + minify?: string | boolean; + manifest?: boolean; + ssrManifest?: boolean; + emptyOutDir?: boolean; + mode?: string; + watch?: boolean; +} diff --git a/libs/vite/src/executors/build/schema.json b/libs/vite/src/executors/build/schema.json new file mode 100644 index 00000000..b202bf2b --- /dev/null +++ b/libs/vite/src/executors/build/schema.json @@ -0,0 +1,77 @@ +{ + "$schema": "http://json-schema.org/schema", + "cli": "nx", + "title": "Build executor", + "description": "", + "type": "object", + "properties": { + "config": { + "type": "string", + "description": "use specified config file" + }, + "root": { + "type": "string", + "description": "use specified root directory" + }, + "base": { "type": "string", "description": "public base path" }, + "logLevel": { + "type": "string", + "description": "silent | error | warn | all" + }, + "clearScreen": { + "type": "boolean", + "description": "allow/disable clear screen when logging" + }, + "debug": { + "type": ["string", "boolean"], + "description": "show debug logs" + }, + "filter": { "type": "string", "description": "filter debug log" }, + "target": { + "type": "string", + "description": "transpile target" + }, + "outDir": { + "type": "string", + "description": "output directory" + }, + "assetsDir": { + "type": "string", + "description": "directory under outDir to place assets in" + }, + "assetsInlineLimit": { + "type": "number", + "description": "static asset base64 inline threshold in bytes" + }, + "ssr": { + "type": "string", + "description": "build specified entry for server-side rendering" + }, + "sourcemap": { + "type": "boolean", + "description": "output source maps for build" + }, + "minify": { + "type": ["string", "boolean"], + "description": "enable/disable minification, or specify minifier to use" + }, + "manifest": { + "type": "boolean", + "description": "emit build manifest json" + }, + "ssrManifest": { + "type": "boolean", + "description": "emit ssr manifest json" + }, + "emptyOutDir": { + "type": "boolean", + "description": "force empty outDir when it's outside of root" + }, + "mode": { "type": "string", "description": "set env mode" }, + "watch": { + "type": "boolean", + "description": "rebuilds when modules have changed on disk" + } + }, + "required": ["config"] +} diff --git a/libs/vite/src/executors/server/compat.ts b/libs/vite/src/executors/server/compat.ts new file mode 100644 index 00000000..bbac558f --- /dev/null +++ b/libs/vite/src/executors/server/compat.ts @@ -0,0 +1,4 @@ +import { convertNxExecutor } from '@nrwl/devkit'; +import { default as serverExecutor } from './executor'; + +export default convertNxExecutor(serverExecutor); diff --git a/libs/vite/src/executors/server/executor.ts b/libs/vite/src/executors/server/executor.ts new file mode 100644 index 00000000..e1b7f5f3 --- /dev/null +++ b/libs/vite/src/executors/server/executor.ts @@ -0,0 +1,30 @@ +import { createServer, ServerOptions } from 'vite'; +import { ViteServerExecutorSchema } from './schema'; +import { cleanViteOptions } from '../utils'; + +export default async function* runExecutor(options: ViteServerExecutorSchema) { + const server = await createServer({ + base: options.base, + mode: options.mode, + configFile: options.config, + logLevel: options.logLevel, + clearScreen: options.clearScreen, + server: cleanViteOptions(options) as ServerOptions, + }); + + if (!server.httpServer) { + throw new Error('HTTP server not available'); + } + + await server.listen(); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (server as any).printUrls(); + yield { + baseUrl: `http://localhost:${server.config.server.port}`, + success: true, + }; + // This Promise intentionally never resolves, leaving the process running + // eslint-disable-next-line @typescript-eslint/no-empty-function + await new Promise<{ success: boolean }>(() => {}); +} diff --git a/libs/vite/src/executors/server/schema.d.ts b/libs/vite/src/executors/server/schema.d.ts new file mode 100644 index 00000000..2359fad1 --- /dev/null +++ b/libs/vite/src/executors/server/schema.d.ts @@ -0,0 +1,17 @@ +export interface ViteServerExecutorSchema { + config: string; + root?: string; + base?: string; + logLevel?: LogLevel; + clearScreen?: boolean; + debug?: boolean; + filter?: string; + host?: string; + port?: number; + https?: boolean; + open?: string | boolean; + cors?: boolean; + strictPort?: boolean; + mode?: string; + force?: boolean; +} diff --git a/libs/vite/src/executors/server/schema.json b/libs/vite/src/executors/server/schema.json new file mode 100644 index 00000000..09e96f83 --- /dev/null +++ b/libs/vite/src/executors/server/schema.json @@ -0,0 +1,64 @@ +{ + "$schema": "http://json-schema.org/schema", + "cli": "nx", + "title": "Server executor", + "description": "", + "type": "object", + "properties": { + "config": { + "type": "string", + "description": "use specified config file" + }, + "root": { + "type": "string", + "description": "use specified root directory" + }, + "base": { "type": "string", "description": "public base path" }, + "logLevel": { + "type": "string", + "description": "silent | error | warn | all" + }, + "clearScreen": { + "type": "boolean", + "description": "allow/disable clear screen when logging" + }, + "debug": { + "type": "boolean", + "description": "show debug logs" + }, + "filter": { "type": "string", "description": "filter debug log" }, + "host": { + "type": "string", + "description": "specify hostname" + }, + "port": { + "type": "number", + "description": "specify port" + }, + "https": { + "type": "boolean", + "description": "use TLS + HTTP/2" + }, + "open": { + "type": "boolean", + "description": "open browser on startup" + }, + "cors": { + "type": "boolean", + "description": "enable cors" + }, + "strictPort": { + "type": "boolean", + "description": "exit if specified port is already in use" + }, + "mode": { + "type": "string", + "description": "set env mode" + }, + "force": { + "type": "boolean", + "description": "force the optimizer to ignore the cache and re-bundle" + } + }, + "required": ["config"] +} diff --git a/libs/vite/src/executors/utils.ts b/libs/vite/src/executors/utils.ts new file mode 100644 index 00000000..c87b26e7 --- /dev/null +++ b/libs/vite/src/executors/utils.ts @@ -0,0 +1,17 @@ +import { ViteBuildExecutorSchema } from './build/schema'; +import { ViteServerExecutorSchema } from './server/schema'; + +export function cleanViteOptions( + options: ViteServerExecutorSchema | ViteBuildExecutorSchema +) { + const ret = { ...options }; + delete ret.debug; + delete ret.filter; + delete ret.config; + delete ret.root; + delete ret.base; + delete ret.mode; + delete ret.logLevel; + delete ret.clearScreen; + return ret; +} diff --git a/libs/vite/src/generators/application/files/index.html.template b/libs/vite/src/generators/application/files/index.html.template new file mode 100644 index 00000000..11603f87 --- /dev/null +++ b/libs/vite/src/generators/application/files/index.html.template @@ -0,0 +1,13 @@ + + + + + + + Vite App + + +
+ + + diff --git a/libs/vite/src/generators/application/files/public/favicon.ico.template b/libs/vite/src/generators/application/files/public/favicon.ico.template new file mode 100644 index 00000000..df36fcfb Binary files /dev/null and b/libs/vite/src/generators/application/files/public/favicon.ico.template differ diff --git a/libs/vite/src/generators/application/files/src/App.vue.template b/libs/vite/src/generators/application/files/src/App.vue.template new file mode 100644 index 00000000..26d5731c --- /dev/null +++ b/libs/vite/src/generators/application/files/src/App.vue.template @@ -0,0 +1,27 @@ + + + + + \ No newline at end of file diff --git a/libs/vite/src/generators/application/files/src/assets/logo.png b/libs/vite/src/generators/application/files/src/assets/logo.png new file mode 100644 index 00000000..f3d2503f Binary files /dev/null and b/libs/vite/src/generators/application/files/src/assets/logo.png differ diff --git a/libs/vite/src/generators/application/files/src/components/HelloWorld.vue.template b/libs/vite/src/generators/application/files/src/components/HelloWorld.vue.template new file mode 100644 index 00000000..f51da827 --- /dev/null +++ b/libs/vite/src/generators/application/files/src/components/HelloWorld.vue.template @@ -0,0 +1,65 @@ + + + + + diff --git a/libs/vite/src/generators/application/files/src/main.ts.template b/libs/vite/src/generators/application/files/src/main.ts.template new file mode 100644 index 00000000..684d0421 --- /dev/null +++ b/libs/vite/src/generators/application/files/src/main.ts.template @@ -0,0 +1,4 @@ +import { createApp } from 'vue'; +import App from './App.vue'; + +createApp(App).mount('#app'); diff --git a/libs/vite/src/generators/application/files/src/shims-vue.d.ts.template b/libs/vite/src/generators/application/files/src/shims-vue.d.ts.template new file mode 100644 index 00000000..e5f68219 --- /dev/null +++ b/libs/vite/src/generators/application/files/src/shims-vue.d.ts.template @@ -0,0 +1,6 @@ +declare module '*.vue' { + import { DefineComponent } from 'vue'; + // eslint-disable-next-line + const component: DefineComponent<{}, {}, any>; + export default component; +} diff --git a/libs/vite/src/generators/application/files/tests/unit/example.spec.ts.template b/libs/vite/src/generators/application/files/tests/unit/example.spec.ts.template new file mode 100644 index 00000000..116c8b75 --- /dev/null +++ b/libs/vite/src/generators/application/files/tests/unit/example.spec.ts.template @@ -0,0 +1,10 @@ +import { shallowMount } from '@vue/test-utils'; +import HelloWorld from '../../src/components/HelloWorld.vue'; + +describe('HelloWorld.vue', () => { + it('renders props.msg when passed', () => { + const msg = 'new message'; + const wrapper = shallowMount(HelloWorld, { props: { msg }}); + expect(wrapper.text()).toMatch(msg); + }); +}); diff --git a/libs/vite/src/generators/application/files/tsconfig.app.json.template b/libs/vite/src/generators/application/files/tsconfig.app.json.template new file mode 100644 index 00000000..d1e71c48 --- /dev/null +++ b/libs/vite/src/generators/application/files/tsconfig.app.json.template @@ -0,0 +1,17 @@ +{ + "extends": "<%= offsetFromRoot %>tsconfig.base.json", + "compilerOptions": { + "target": "esnext", + "module": "esnext", + "moduleResolution": "node", + "strict": true, + "jsx": "preserve", + "sourceMap": true, + "resolveJsonModule": true, + "esModuleInterop": true, + "lib": ["esnext", "dom"], + "types": ["vite/client"] + },<% if (unitTestRunner === 'jest') { %> + "exclude": ["**/*.spec.ts", "**/*.spec.tsx"],<% } %> + "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"] +} diff --git a/libs/vite/src/generators/application/files/tsconfig.json.template b/libs/vite/src/generators/application/files/tsconfig.json.template new file mode 100644 index 00000000..1ee22e07 --- /dev/null +++ b/libs/vite/src/generators/application/files/tsconfig.json.template @@ -0,0 +1,10 @@ +{ + "extends": "<%= offsetFromRoot %>tsconfig.base.json", + "include": [], + "files": [], + "references": [ + { + "path": "./tsconfig.app.json" + } + ] +} diff --git a/libs/vite/src/generators/application/files/vite.config.ts.template b/libs/vite/src/generators/application/files/vite.config.ts.template new file mode 100644 index 00000000..fa125b80 --- /dev/null +++ b/libs/vite/src/generators/application/files/vite.config.ts.template @@ -0,0 +1,24 @@ +import { appRootPath } from '@nrwl/tao/src/utils/app-root'; +import vue from '@vitejs/plugin-vue'; +import { defineConfig } from 'vite'; +import { join } from 'path'; +import baseTsConfig from '<%= offsetFromRoot %>tsconfig.base.json'; + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [vue()], + root: __dirname, + build: { + outDir: '<%= offsetFromRoot %>dist/<%= projectRoot %>', + emptyOutDir: true, + }, + resolve: { + alias: Object.entries(baseTsConfig.compilerOptions.paths).reduce( + (acc, [key, paths]) => ({ + ...acc, + [key]: (paths as string[]).map((path) => join(appRootPath, path)), + }), + {} + ), + }, +}); diff --git a/libs/vite/src/generators/application/generator.spec.ts b/libs/vite/src/generators/application/generator.spec.ts new file mode 100644 index 00000000..81690d23 --- /dev/null +++ b/libs/vite/src/generators/application/generator.spec.ts @@ -0,0 +1,98 @@ +import { Tree, readProjectConfiguration, readJson } from '@nrwl/devkit'; +import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing'; +import { applicationGenerator } from './generator'; +import { ApplicationGeneratorSchema } from './schema'; + +describe('application generator', () => { + let appTree: Tree; + const options: ApplicationGeneratorSchema = { + name: 'my-app', + skipFormat: false, + unitTestRunner: 'jest', + e2eTestRunner: 'cypress', + }; + + beforeEach(() => { + appTree = createTreeWithEmptyWorkspace(); + }); + + it('should update workspace.json', async () => { + await applicationGenerator(appTree, options); + + const config = readProjectConfiguration(appTree, 'my-app'); + + expect(config.root).toBe('apps/my-app'); + expect(config.sourceRoot).toBe('apps/my-app/src'); + expect(config.targets.build.executor).toBe('@nx-plus/vite:build'); + expect(config.targets.build.options).toEqual({ + config: 'apps/my-app/vite.config.ts', + }); + expect(config.targets.serve.executor).toBe('@nx-plus/vite:server'); + expect(config.targets.serve.options).toEqual({ + config: 'apps/my-app/vite.config.ts', + }); + + expect(config.targets.lint.executor).toBe('@nrwl/linter:eslint'); + expect(config.targets.test.executor).toBe('@nrwl/jest:jest'); + + const e2eConfig = readProjectConfiguration(appTree, 'my-app-e2e'); + expect(e2eConfig).toBeDefined(); + }); + + it('should generate files', async () => { + await applicationGenerator(appTree, options); + + [ + 'apps/my-app/index.html', + 'apps/my-app/tsconfig.json', + 'apps/my-app/tsconfig.app.json', + 'apps/my-app/vite.config.ts', + 'apps/my-app/public/favicon.ico', + 'apps/my-app/src/assets/logo.png', + 'apps/my-app/src/components/HelloWorld.vue', + 'apps/my-app/src/App.vue', + 'apps/my-app/src/main.ts', + 'apps/my-app/src/shims-vue.d.ts', + 'apps/my-app/tests/unit/example.spec.ts', + ].forEach((path) => expect(appTree.exists(path)).toBeTruthy()); + }); + + it('should add postinstall script', async () => { + await applicationGenerator(appTree, options); + + expect(readJson(appTree, 'package.json').scripts.postinstall).toBe( + 'node node_modules/@nx-plus/vite/patch-nx-dep-graph.js' + ); + }); + + describe('--unitTestRunner none', () => { + it('should not generate test configuration', async () => { + await applicationGenerator(appTree, { + ...options, + unitTestRunner: 'none', + }); + const config = readProjectConfiguration(appTree, 'my-app'); + + expect(config.targets.test).toBeUndefined(); + + [ + 'apps/my-app/tsconfig.spec.json', + 'apps/my-app/jest.config.js', + 'apps/my-app/tests/unit/example.spec.ts', + ].forEach((path) => expect(appTree.exists(path)).toBeFalsy()); + + const tsconfigAppJson = readJson( + appTree, + 'apps/my-app/tsconfig.app.json' + ); + expect(tsconfigAppJson.exclude).toBeUndefined(); + + expect(appTree.read('apps/my-app/.eslintrc.js').toString()).not.toContain( + 'overrides:' + ); + + const tsConfigJson = readJson(appTree, 'apps/my-app/tsconfig.json'); + expect(tsConfigJson.references[1]).toBeUndefined(); + }); + }); +}); diff --git a/libs/vite/src/generators/application/generator.ts b/libs/vite/src/generators/application/generator.ts new file mode 100644 index 00000000..effbe3f1 --- /dev/null +++ b/libs/vite/src/generators/application/generator.ts @@ -0,0 +1,301 @@ +import { + addProjectConfiguration, + formatFiles, + generateFiles, + getWorkspaceLayout, + names, + offsetFromRoot, + Tree, + addDependenciesToPackageJson, + updateJson, + convertNxGenerator, + logger, +} from '@nrwl/devkit'; +import { runTasksInSerial } from '@nrwl/workspace/src/utilities/run-tasks-in-serial'; +import * as path from 'path'; +import { checkPeerDeps } from '../../utils'; +import { ApplicationGeneratorSchema } from './schema'; + +interface NormalizedSchema extends ApplicationGeneratorSchema { + projectName: string; + projectRoot: string; + projectDirectory: string; + parsedTags: string[]; +} + +function normalizeOptions( + host: Tree, + options: ApplicationGeneratorSchema +): NormalizedSchema { + const name = names(options.name).fileName; + const projectDirectory = options.directory + ? `${names(options.directory).fileName}/${name}` + : name; + const projectName = projectDirectory.replace(new RegExp('/', 'g'), '-'); + const projectRoot = `${getWorkspaceLayout(host).appsDir}/${projectDirectory}`; + const parsedTags = options.tags + ? options.tags.split(',').map((s) => s.trim()) + : []; + + return { + ...options, + name, + projectName, + projectRoot, + projectDirectory, + parsedTags, + }; +} + +function addFiles(host: Tree, options: NormalizedSchema) { + const templateOptions = { + ...options, + ...names(options.name), + offsetFromRoot: offsetFromRoot(options.projectRoot), + template: '', + }; + generateFiles( + host, + path.join(__dirname, 'files'), + options.projectRoot, + templateOptions + ); + if (options.unitTestRunner === 'none') { + const { path } = host + .listChanges() + .find(({ path }) => path.includes('example.spec.ts')); + host.delete(path); + } +} + +function getEslintConfig(options: NormalizedSchema) { + const eslintConfig = { + extends: [ + `${offsetFromRoot(options.projectRoot)}.eslintrc.json`, + `plugin:vue/vue3-essential`, + '@vue/typescript/recommended', + 'prettier', + ], + rules: {}, + ignorePatterns: ['!**/*'], + env: { + node: true, + }, + } as any; + + if (options.unitTestRunner === 'jest') { + eslintConfig.overrides = [ + { + files: ['**/*.spec.{j,t}s?(x)'], + env: { + jest: true, + }, + }, + ]; + } + + return eslintConfig; +} + +async function addEsLint(tree: Tree, options: NormalizedSchema) { + const { lintProjectGenerator, Linter } = await import('@nrwl/linter'); + const lintTask = await lintProjectGenerator(tree, { + linter: Linter.EsLint, + project: options.projectName, + eslintFilePatterns: [`${options.projectRoot}/**/*.{ts,tsx,vue}`], + skipFormat: true, + }); + + updateJson(tree, `${options.projectRoot}/.eslintrc.json`, (json) => { + json.extends.unshift(json.extends.pop()); + return json; + }); + + const content = JSON.stringify(getEslintConfig(options)); + const configPath = `${options.projectRoot}/.eslintrc.json`; + const newConfigPath = configPath.slice(0, -2); + tree.rename(configPath, newConfigPath); + tree.write(newConfigPath, `module.exports = ${content};`); + + const installTask = addDependenciesToPackageJson( + tree, + {}, + { + '@vue/eslint-config-prettier': '6.0.0', + '@vue/eslint-config-typescript': '^5.0.2', + 'eslint-plugin-prettier': '^3.1.3', + 'eslint-plugin-vue': '^7.0.0-0', + } + ); + + return [lintTask, installTask]; +} + +async function addCypress(tree: Tree, options: NormalizedSchema) { + const { cypressInitGenerator, cypressProjectGenerator } = await import( + '@nrwl/cypress' + ); + const { Linter } = await import('@nrwl/linter'); + const cypressInitTask = await cypressInitGenerator(tree); + const cypressTask = await cypressProjectGenerator(tree, { + project: options.projectName, + name: options.name + '-e2e', + directory: options.directory, + linter: Linter.EsLint, + js: false, + }); + + const appSpecPath = options.projectRoot + '-e2e/src/integration/app.spec.ts'; + tree.write( + appSpecPath, + tree + .read(appSpecPath) + .toString('utf-8') + .replace( + `Welcome to ${options.projectName}!`, + 'Hello Vue 3 + TypeScript + Vite' + ) + ); + + return [cypressInitTask, cypressTask]; +} + +async function addJest(tree: Tree, options: NormalizedSchema) { + const { jestProjectGenerator, jestInitGenerator } = await import( + '@nrwl/jest' + ); + const jestInitTask = await jestInitGenerator(tree, { babelJest: false }); + const jestTask = await jestProjectGenerator(tree, { + project: options.projectName, + setupFile: 'none', + skipSerializers: true, + supportTsx: true, + testEnvironment: 'jsdom', + babelJest: false, + }); + updateJson(tree, `${options.projectRoot}/tsconfig.spec.json`, (json) => { + json.include = json.include.filter((pattern) => !/\.jsx?$/.test(pattern)); + json.compilerOptions = { + ...json.compilerOptions, + jsx: 'preserve', + esModuleInterop: true, + allowSyntheticDefaultImports: true, + }; + return json; + }); + const content = `module.exports = { + displayName: '${options.projectName}', + preset: '${offsetFromRoot(options.projectRoot)}jest.preset.js', + transform: { + '^.+\\.vue$': 'vue3-jest', + '.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': + 'jest-transform-stub', + '^.+\\.tsx?$': 'ts-jest', + }, + moduleFileExtensions: ["ts", "tsx", "vue", "js", "json"], + coverageDirectory: '${offsetFromRoot(options.projectRoot)}coverage/${ + options.projectRoot + }', + snapshotSerializers: ['jest-serializer-vue'], + globals: { + 'ts-jest': { + tsconfig: '${options.projectRoot}/tsconfig.spec.json', + }, + 'vue-jest': { + tsConfig: '${options.projectRoot}/tsconfig.spec.json', + } + }, +}; +`; + tree.write(`${options.projectRoot}/jest.config.js`, content); + + const installTask = addDependenciesToPackageJson( + tree, + {}, + { + '@vue/test-utils': '^2.0.0-0', + 'jest-serializer-vue': '^2.0.2', + 'jest-transform-stub': '^2.0.0', + 'vue3-jest': '^27.0.0-alpha.1', + } + ); + return [jestInitTask, jestTask, installTask]; +} + +function addPostInstall(host: Tree) { + return updateJson(host, 'package.json', (json) => { + const vuePostInstall = + 'node node_modules/@nx-plus/vite/patch-nx-dep-graph.js'; + const { postinstall } = json.scripts || {}; + if (postinstall) { + if (postinstall !== vuePostInstall) { + logger.warn( + "We couldn't add our postinstall script. Without it Nx's dependency graph won't support Vue files. For more information see https://github.com/ZachJW34/nx-plus/tree/master/libs/vite#nx-dependency-graph-support" + ); + } + return json; + } + json.scripts = { ...json.scripts, postinstall: vuePostInstall }; + return json; + }); +} + +export async function applicationGenerator( + host: Tree, + options: ApplicationGeneratorSchema +) { + checkPeerDeps(host, options); + const normalizedOptions = normalizeOptions(host, options); + addProjectConfiguration(host, normalizedOptions.projectName, { + root: normalizedOptions.projectRoot, + projectType: 'application', + sourceRoot: `${normalizedOptions.projectRoot}/src`, + targets: { + build: { + executor: '@nx-plus/vite:build', + options: { + config: `${normalizedOptions.projectRoot}/vite.config.ts`, + }, + }, + serve: { + executor: '@nx-plus/vite:server', + options: { + config: `${normalizedOptions.projectRoot}/vite.config.ts`, + }, + }, + }, + tags: normalizedOptions.parsedTags, + }); + addFiles(host, normalizedOptions); + const lintTasks = await addEsLint(host, normalizedOptions); + const cypressTasks = + options.e2eTestRunner === 'cypress' + ? await addCypress(host, normalizedOptions) + : []; + const jestTasks = + options.unitTestRunner === 'jest' + ? await addJest(host, normalizedOptions) + : []; + const installTask = addDependenciesToPackageJson( + host, + { vue: '^3.0.5' }, + { + '@vitejs/plugin-vue': '^2.0.0', + typescript: '^4.4.4', + vite: '^2.7.1', + } + ); + addPostInstall(host); + if (!normalizedOptions.skipFormat) { + await formatFiles(host); + } + + return runTasksInSerial( + ...lintTasks, + ...cypressTasks, + ...jestTasks, + installTask + ); +} + +export const applicationSchematic = convertNxGenerator(applicationGenerator); diff --git a/libs/vite/src/generators/application/schema.d.ts b/libs/vite/src/generators/application/schema.d.ts new file mode 100644 index 00000000..7e108a8e --- /dev/null +++ b/libs/vite/src/generators/application/schema.d.ts @@ -0,0 +1,8 @@ +export interface ApplicationGeneratorSchema { + name: string; + tags?: string; + directory?: string; + skipFormat: boolean; + unitTestRunner: 'jest' | 'none'; + e2eTestRunner: 'cypress' | 'none'; +} diff --git a/libs/vite/src/generators/application/schema.json b/libs/vite/src/generators/application/schema.json new file mode 100644 index 00000000..2c6f13b4 --- /dev/null +++ b/libs/vite/src/generators/application/schema.json @@ -0,0 +1,46 @@ +{ + "$schema": "http://json-schema.org/schema", + "cli": "nx", + "id": "Application", + "title": "", + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "", + "$default": { + "$source": "argv", + "index": 0 + }, + "x-prompt": "What name would you like to use?" + }, + "tags": { + "type": "string", + "description": "Add tags to the project (used for linting)", + "alias": "t" + }, + "directory": { + "type": "string", + "description": "A directory where the project is placed", + "alias": "d" + }, + "skipFormat": { + "type": "boolean", + "description": "Skip formatting files", + "default": false + }, + "unitTestRunner": { + "type": "string", + "enum": ["jest", "none"], + "description": "Test runner to use for unit tests", + "default": "jest" + }, + "e2eTestRunner": { + "type": "string", + "enum": ["cypress", "none"], + "description": "Test runner to use for end to end (e2e) tests", + "default": "cypress" + } + }, + "required": ["name"] +} diff --git a/libs/vite/src/index.ts b/libs/vite/src/index.ts new file mode 100644 index 00000000..e69de29b diff --git a/libs/vite/src/utils.ts b/libs/vite/src/utils.ts new file mode 100644 index 00000000..0b30eaa8 --- /dev/null +++ b/libs/vite/src/utils.ts @@ -0,0 +1,80 @@ +import { logger, Tree } from '@nrwl/devkit'; +import * as path from 'path'; +import * as semver from 'semver'; +import { appRootPath } from './app-root'; + +// eslint-disable-next-line @typescript-eslint/no-var-requires +const Module = require('module'); + +export function loadModule(request, context, force = false) { + try { + return createRequire(path.resolve(context, 'package.json'))(request); + } catch (e) { + const resolvedPath = require.resolve(request, { paths: [context] }); + if (resolvedPath) { + if (force) { + clearRequireCache(resolvedPath); + } + return require(resolvedPath); + } + } +} + +// https://github.com/benmosher/eslint-plugin-import/pull/1591 +// https://github.com/benmosher/eslint-plugin-import/pull/1602 +// Polyfill Node's `Module.createRequireFromPath` if not present (added in Node v10.12.0) +// Use `Module.createRequire` if available (added in Node v12.2.0) +const createRequire = + Module.createRequire || + Module.createRequireFromPath || + function (filename) { + const mod = new Module(filename, null); + mod.filename = filename; + mod.paths = Module._nodeModulePaths(path.dirname(filename)); + + mod._compile(`module.exports = require;`, filename); + + return mod.exports; + }; + +function clearRequireCache(id, map = new Map()) { + const module = require.cache[id]; + if (module) { + map.set(id, true); + // Clear children modules + module.children.forEach((child) => { + if (!map.get(child.id)) clearRequireCache(child.id, map); + }); + delete require.cache[id]; + } +} + +export function checkPeerDeps(tree: Tree, options): void { + const expectedVersion = '^12.0.0'; + const unmetPeerDeps = [ + ...(options.e2eTestRunner === 'cypress' ? ['@nrwl/cypress'] : []), + ...(options.unitTestRunner === 'jest' ? ['@nrwl/jest'] : []), + '@nrwl/linter', + '@nrwl/workspace', + ].filter((dep) => { + try { + const { version } = loadModule(`${dep}/package.json`, appRootPath, true); + return !semver.satisfies(version, expectedVersion); + } catch (err) { + return true; + } + }); + + if (unmetPeerDeps.length) { + logger.warn(` +You have the following unmet peer dependencies: + +${unmetPeerDeps + .map((dep) => `${dep}@${expectedVersion}\n`) + .join() + .split(',') + .join('')} +@nx-plus/vite may not work as expected. + `); + } +} diff --git a/libs/vite/tsconfig.json b/libs/vite/tsconfig.json new file mode 100644 index 00000000..62ebbd94 --- /dev/null +++ b/libs/vite/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../../tsconfig.base.json", + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/libs/vite/tsconfig.lib.json b/libs/vite/tsconfig.lib.json new file mode 100644 index 00000000..037d796f --- /dev/null +++ b/libs/vite/tsconfig.lib.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "commonjs", + "outDir": "../../dist/out-tsc", + "declaration": true, + "types": ["node"] + }, + "exclude": ["**/*.spec.ts"], + "include": ["**/*.ts"] +} diff --git a/libs/vite/tsconfig.spec.json b/libs/vite/tsconfig.spec.json new file mode 100644 index 00000000..559410b9 --- /dev/null +++ b/libs/vite/tsconfig.spec.json @@ -0,0 +1,15 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "include": [ + "**/*.spec.ts", + "**/*.spec.tsx", + "**/*.spec.js", + "**/*.spec.jsx", + "**/*.d.ts" + ] +} diff --git a/libs/vue/nx-plus-vue.png b/libs/vue/nx-plus-vue.png deleted file mode 100644 index 1350f1a5..00000000 Binary files a/libs/vue/nx-plus-vue.png and /dev/null differ diff --git a/package.json b/package.json index 8f368b85..34186eb5 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,7 @@ "@nrwl/devkit": "12.6.5", "@nrwl/eslint-plugin-nx": "12.6.5", "@nrwl/jest": "12.6.5", + "@nrwl/node": "12.6.5", "@nrwl/nx-plugin": "12.6.5", "@nrwl/tao": "12.6.5", "@nrwl/workspace": "12.6.5", @@ -71,7 +72,9 @@ "semver": "^7.3.2", "ts-jest": "27.0.3", "ts-node": "9.1.1", + "tslib": "^2.0.0", "tslint": "6.1.3", - "typescript": "4.3.5" + "typescript": "4.3.5", + "vite": "^2.7.1" } } diff --git a/tsconfig.base.json b/tsconfig.base.json index 9b02d6b2..55c09897 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -18,7 +18,8 @@ "paths": { "@nx-plus/docusaurus": ["libs/docusaurus/src/index.ts"], "@nx-plus/nuxt": ["libs/nuxt/src/index.ts"], - "@nx-plus/vue": ["libs/vue/src/index.ts"] + "@nx-plus/vue": ["libs/vue/src/index.ts"], + "@nx-plus/vite": ["libs/vite/src/index.ts"] } }, "exclude": ["node_modules", "tmp"] diff --git a/workspace.json b/workspace.json index ed2240d3..cbf9c1c5 100644 --- a/workspace.json +++ b/workspace.json @@ -236,6 +236,79 @@ } } } + }, + "vite": { + "root": "libs/vite", + "sourceRoot": "libs/vite/src", + "projectType": "library", + "architect": { + "lint": { + "builder": "@nrwl/linter:eslint", + "options": { + "lintFilePatterns": ["libs/vite/**/*.ts"] + } + }, + "test": { + "builder": "@nrwl/jest:jest", + "outputs": ["coverage/libs/vite"], + "options": { + "jestConfig": "libs/vite/jest.config.js", + "passWithNoTests": true + } + }, + "build": { + "builder": "@nrwl/node:package", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/libs/vite", + "tsConfig": "libs/vite/tsconfig.lib.json", + "packageJson": "libs/vite/package.json", + "main": "libs/vite/src/index.ts", + "assets": [ + "libs/vite/*.md", + { + "input": "./libs/vite/src", + "glob": "**/!(*.ts)", + "output": "./src" + }, + { + "input": "./libs/vite/src", + "glob": "**/*.d.ts", + "output": "./src" + }, + { + "input": "./libs/vite", + "glob": "generators.json", + "output": "." + }, + { + "input": "./libs/vite", + "glob": "executors.json", + "output": "." + }, + { + "input": "./libs/vite", + "glob": "patch-nx-dep-graph.js", + "output": "." + } + ] + } + } + } + }, + "vite-e2e": { + "projectType": "application", + "root": "apps/vite-e2e", + "sourceRoot": "apps/vite-e2e/src", + "architect": { + "e2e": { + "builder": "@nrwl/nx-plugin:e2e", + "options": { + "target": "vite:build", + "jestConfig": "apps/vite-e2e/jest.config.js" + } + } + } } }, "cli": { diff --git a/yarn.lock b/yarn.lock index 6a587d0d..366d13c2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8085,6 +8085,114 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" +esbuild-android-arm64@0.13.15: + version "0.13.15" + resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.13.15.tgz#3fc3ff0bab76fe35dd237476b5d2b32bb20a3d44" + integrity sha512-m602nft/XXeO8YQPUDVoHfjyRVPdPgjyyXOxZ44MK/agewFFkPa8tUo6lAzSWh5Ui5PB4KR9UIFTSBKh/RrCmg== + +esbuild-darwin-64@0.13.15: + version "0.13.15" + resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.13.15.tgz#8e9169c16baf444eacec60d09b24d11b255a8e72" + integrity sha512-ihOQRGs2yyp7t5bArCwnvn2Atr6X4axqPpEdCFPVp7iUj4cVSdisgvEKdNR7yH3JDjW6aQDw40iQFoTqejqxvQ== + +esbuild-darwin-arm64@0.13.15: + version "0.13.15" + resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.13.15.tgz#1b07f893b632114f805e188ddfca41b2b778229a" + integrity sha512-i1FZssTVxUqNlJ6cBTj5YQj4imWy3m49RZRnHhLpefFIh0To05ow9DTrXROTE1urGTQCloFUXTX8QfGJy1P8dQ== + +esbuild-freebsd-64@0.13.15: + version "0.13.15" + resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.13.15.tgz#0b8b7eca1690c8ec94c75680c38c07269c1f4a85" + integrity sha512-G3dLBXUI6lC6Z09/x+WtXBXbOYQZ0E8TDBqvn7aMaOCzryJs8LyVXKY4CPnHFXZAbSwkCbqiPuSQ1+HhrNk7EA== + +esbuild-freebsd-arm64@0.13.15: + version "0.13.15" + resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.13.15.tgz#2e1a6c696bfdcd20a99578b76350b41db1934e52" + integrity sha512-KJx0fzEDf1uhNOZQStV4ujg30WlnwqUASaGSFPhznLM/bbheu9HhqZ6mJJZM32lkyfGJikw0jg7v3S0oAvtvQQ== + +esbuild-linux-32@0.13.15: + version "0.13.15" + resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.13.15.tgz#6fd39f36fc66dd45b6b5f515728c7bbebc342a69" + integrity sha512-ZvTBPk0YWCLMCXiFmD5EUtB30zIPvC5Itxz0mdTu/xZBbbHJftQgLWY49wEPSn2T/TxahYCRDWun5smRa0Tu+g== + +esbuild-linux-64@0.13.15: + version "0.13.15" + resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.13.15.tgz#9cb8e4bcd7574e67946e4ee5f1f1e12386bb6dd3" + integrity sha512-eCKzkNSLywNeQTRBxJRQ0jxRCl2YWdMB3+PkWFo2BBQYC5mISLIVIjThNtn6HUNqua1pnvgP5xX0nHbZbPj5oA== + +esbuild-linux-arm64@0.13.15: + version "0.13.15" + resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.13.15.tgz#3891aa3704ec579a1b92d2a586122e5b6a2bfba1" + integrity sha512-bYpuUlN6qYU9slzr/ltyLTR9YTBS7qUDymO8SV7kjeNext61OdmqFAzuVZom+OLW1HPHseBfJ/JfdSlx8oTUoA== + +esbuild-linux-arm@0.13.15: + version "0.13.15" + resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.13.15.tgz#8a00e99e6a0c6c9a6b7f334841364d8a2b4aecfe" + integrity sha512-wUHttDi/ol0tD8ZgUMDH8Ef7IbDX+/UsWJOXaAyTdkT7Yy9ZBqPg8bgB/Dn3CZ9SBpNieozrPRHm0BGww7W/jA== + +esbuild-linux-mips64le@0.13.15: + version "0.13.15" + resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.13.15.tgz#36b07cc47c3d21e48db3bb1f4d9ef8f46aead4f7" + integrity sha512-KlVjIG828uFPyJkO/8gKwy9RbXhCEUeFsCGOJBepUlpa7G8/SeZgncUEz/tOOUJTcWMTmFMtdd3GElGyAtbSWg== + +esbuild-linux-ppc64le@0.13.15: + version "0.13.15" + resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.13.15.tgz#f7e6bba40b9a11eb9dcae5b01550ea04670edad2" + integrity sha512-h6gYF+OsaqEuBjeesTBtUPw0bmiDu7eAeuc2OEH9S6mV9/jPhPdhOWzdeshb0BskRZxPhxPOjqZ+/OqLcxQwEQ== + +esbuild-netbsd-64@0.13.15: + version "0.13.15" + resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.13.15.tgz#a2fedc549c2b629d580a732d840712b08d440038" + integrity sha512-3+yE9emwoevLMyvu+iR3rsa+Xwhie7ZEHMGDQ6dkqP/ndFzRHkobHUKTe+NCApSqG5ce2z4rFu+NX/UHnxlh3w== + +esbuild-openbsd-64@0.13.15: + version "0.13.15" + resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.13.15.tgz#b22c0e5806d3a1fbf0325872037f885306b05cd7" + integrity sha512-wTfvtwYJYAFL1fSs8yHIdf5GEE4NkbtbXtjLWjM3Cw8mmQKqsg8kTiqJ9NJQe5NX/5Qlo7Xd9r1yKMMkHllp5g== + +esbuild-sunos-64@0.13.15: + version "0.13.15" + resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.13.15.tgz#d0b6454a88375ee8d3964daeff55c85c91c7cef4" + integrity sha512-lbivT9Bx3t1iWWrSnGyBP9ODriEvWDRiweAs69vI+miJoeKwHWOComSRukttbuzjZ8r1q0mQJ8Z7yUsDJ3hKdw== + +esbuild-windows-32@0.13.15: + version "0.13.15" + resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.13.15.tgz#c96d0b9bbb52f3303322582ef8e4847c5ad375a7" + integrity sha512-fDMEf2g3SsJ599MBr50cY5ve5lP1wyVwTe6aLJsM01KtxyKkB4UT+fc5MXQFn3RLrAIAZOG+tHC+yXObpSn7Nw== + +esbuild-windows-64@0.13.15: + version "0.13.15" + resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.13.15.tgz#1f79cb9b1e1bb02fb25cd414cb90d4ea2892c294" + integrity sha512-9aMsPRGDWCd3bGjUIKG/ZOJPKsiztlxl/Q3C1XDswO6eNX/Jtwu4M+jb6YDH9hRSUflQWX0XKAfWzgy5Wk54JQ== + +esbuild-windows-arm64@0.13.15: + version "0.13.15" + resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.13.15.tgz#482173070810df22a752c686509c370c3be3b3c3" + integrity sha512-zzvyCVVpbwQQATaf3IG8mu1IwGEiDxKkYUdA4FpoCHi1KtPa13jeScYDjlW0Qh+ebWzpKfR2ZwvqAQkSWNcKjA== + +esbuild@^0.13.12: + version "0.13.15" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.13.15.tgz#db56a88166ee373f87dbb2d8798ff449e0450cdf" + integrity sha512-raCxt02HBKv8RJxE8vkTSCXGIyKHdEdGfUmiYb8wnabnaEmHzyW7DCHb5tEN0xU8ryqg5xw54mcwnYkC4x3AIw== + optionalDependencies: + esbuild-android-arm64 "0.13.15" + esbuild-darwin-64 "0.13.15" + esbuild-darwin-arm64 "0.13.15" + esbuild-freebsd-64 "0.13.15" + esbuild-freebsd-arm64 "0.13.15" + esbuild-linux-32 "0.13.15" + esbuild-linux-64 "0.13.15" + esbuild-linux-arm "0.13.15" + esbuild-linux-arm64 "0.13.15" + esbuild-linux-mips64le "0.13.15" + esbuild-linux-ppc64le "0.13.15" + esbuild-netbsd-64 "0.13.15" + esbuild-openbsd-64 "0.13.15" + esbuild-sunos-64 "0.13.15" + esbuild-windows-32 "0.13.15" + esbuild-windows-64 "0.13.15" + esbuild-windows-arm64 "0.13.15" + escalade@^3.0.1: version "3.0.2" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.0.2.tgz#6a580d70edb87880f22b4c91d0d56078df6962c4" @@ -12083,6 +12191,11 @@ nan@^2.12.1: integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== nanoid@^3.1.23: + version "3.1.23" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.23.tgz#f744086ce7c2bc47ee0a8472574d5c78e4183a81" + integrity sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw== + +nanoid@^3.1.30: version "3.1.30" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.30.tgz#63f93cc548d2a113dc5dfbc63bfa09e2b9b64362" integrity sha512-zJpuPDwOv8D2zq2WRoMe1HsfZthVewpel9CAvTfc/2mBD1uUT/agc5f7GHGWXlYkFvi1mVxe4IjvP2HNrop7nQ== @@ -13756,6 +13869,15 @@ postcss@^7.0.32: source-map "^0.6.1" supports-color "^6.1.0" +postcss@^8.3.11: + version "8.4.4" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.4.tgz#d53d4ec6a75fd62557a66bb41978bf47ff0c2869" + integrity sha512-joU6fBsN6EIer28Lj6GDFoC/5yOZzLCfn0zHAn/MYXI7aPt4m4hK5KC5ovEZXy+lnCjmYIbQWngvju2ddyEr8Q== + dependencies: + nanoid "^3.1.30" + picocolors "^1.0.0" + source-map-js "^1.0.1" + prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" @@ -14587,6 +14709,13 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^3.0.0" inherits "^2.0.1" +rollup@^2.59.0: + version "2.61.1" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.61.1.tgz#1a5491f84543cf9e4caf6c61222d9a3f8f2ba454" + integrity sha512-BbTXlEvB8d+XFbK/7E5doIcRtxWPRiqr0eb5vQ0+2paMM04Ye4PZY5nHOQef2ix24l/L0SpLd5hwcH15QHPdvA== + optionalDependencies: + fsevents "~2.3.2" + run-async@^2.2.0, run-async@^2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.0.tgz#e59054a5b86876cfae07f431d18cbaddc594f1e8" @@ -15112,6 +15241,11 @@ source-list-map@^2.0.0: resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== +source-map-js@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.1.tgz#a1741c131e3c77d048252adfa24e23b908670caf" + integrity sha512-4+TN2b3tqOCd/kaGRJ/sTYA0tR0mdXx26ipdolxcwtJVqEnqNYvlCAt1q3ypy4QMlYus+Zh34RNtYLoq2oQ4IA== + source-map-resolve@^0.5.0: version "0.5.3" resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" @@ -16501,6 +16635,18 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" +vite@^2.7.1: + version "2.7.1" + resolved "https://registry.yarnpkg.com/vite/-/vite-2.7.1.tgz#be50ad13214290ecbebbe5ad389ed423cb5f137e" + integrity sha512-TDXXhcu5lyQ6uosK4ZWaOyB4VzOiizk0biitRzDzaEtgSUi8rVYPc4k1xgOjLSf0OuceDJmojFKXHOX9DB1WuQ== + dependencies: + esbuild "^0.13.12" + postcss "^8.3.11" + resolve "^1.20.0" + rollup "^2.59.0" + optionalDependencies: + fsevents "~2.3.2" + vm-browserify@^1.0.1: version "1.1.2" resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0"