From c1e3cbfe2f64744a2f923d582d93617882043d15 Mon Sep 17 00:00:00 2001 From: Lucas Vieira Date: Tue, 1 Mar 2022 19:24:13 -0300 Subject: [PATCH] Add @nxlv/python initial version --- lucasvieira/.editorconfig => .editorconfig | 0 lucasvieira/.eslintrc.json => .eslintrc.json | 0 lucasvieira/.gitignore => .gitignore | 0 .../.prettierignore => .prettierignore | 0 lucasvieira/.prettierrc => .prettierrc | 0 .../.vscode => .vscode}/extensions.json | 0 README.md | 9 + .../e2e => e2e}/nx-python-e2e/jest.config.js | 0 .../e2e => e2e}/nx-python-e2e/project.json | 0 e2e/nx-python-e2e/tests/nx-python.spec.ts | 86 +++ .../e2e => e2e}/nx-python-e2e/tsconfig.json | 0 .../nx-python-e2e/tsconfig.spec.json | 0 lucasvieira/jest.config.js => jest.config.js | 0 lucasvieira/jest.preset.js => jest.preset.js | 0 lucasvieira/README.md | 94 --- .../e2e/nx-python-e2e/tests/nx-python.spec.ts | 44 -- lucasvieira/packages/nx-python/README.md | 7 - lucasvieira/packages/nx-python/executors.json | 10 - .../packages/nx-python/generators.json | 12 - lucasvieira/packages/nx-python/package.json | 7 - .../src/executors/build/executor.spec.ts | 11 - .../nx-python/src/executors/build/executor.ts | 8 - .../nx-python/src/executors/build/schema.d.ts | 1 - .../nx-python/src/executors/build/schema.json | 9 - .../nx-python/files/src/index.ts__template__ | 1 - .../generators/nx-python/generator.spec.ts | 20 - .../src/generators/nx-python/generator.ts | 73 -- .../src/generators/nx-python/schema.d.ts | 5 - .../src/generators/nx-python/schema.json | 29 - lucasvieira/packages/nx-python/src/index.ts | 0 lucasvieira/nx.json => nx.json | 0 .../package-lock.json => package-lock.json | 470 +++++++++++- lucasvieira/package.json => package.json | 10 +- .../packages => packages}/nx-python/.babelrc | 0 .../nx-python/.eslintrc.json | 0 packages/nx-python/README.md | 168 ++++ packages/nx-python/executors.json | 40 + packages/nx-python/generators.json | 19 + .../nx-python/jest.config.js | 0 packages/nx-python/package.json | 11 + .../nx-python/project.json | 0 .../src/dependency/update-dependency.ts | 37 + .../src/executors/add/executor.spec.ts | 425 +++++++++++ .../nx-python/src/executors/add/executor.ts | 49 ++ .../nx-python/src/executors/add/schema.d.ts | 5 + .../nx-python/src/executors/add/schema.json | 28 + .../src/executors/build/executor.spec.ts | 463 +++++++++++ .../nx-python/src/executors/build/executor.ts | 205 +++++ .../nx-python/src/executors/build/schema.d.ts | 6 + .../nx-python/src/executors/build/schema.json | 32 + .../src/executors/flake8/executor.spec.ts | 114 +++ .../src/executors/flake8/executor.ts | 52 ++ .../src/executors/flake8/schema.d.ts | 4 + .../src/executors/flake8/schema.json | 19 + .../src/executors/install/executor.spec.ts | 129 ++++ .../src/executors/install/executor.ts | 50 ++ .../src/executors/install/schema.d.ts | 7 + .../src/executors/install/schema.json | 33 + .../src/executors/remove/executor.spec.ts | 322 ++++++++ .../src/executors/remove/executor.ts | 59 ++ .../src/executors/remove/schema.d.ts | 5 + .../src/executors/remove/schema.json | 24 + .../src/executors/tox/executor.spec.ts | 168 ++++ .../nx-python/src/executors/tox/executor.ts | 65 ++ .../nx-python/src/executors/tox/schema.d.ts | 4 + .../nx-python/src/executors/tox/schema.json | 19 + .../src/executors/update/executor.spec.ts | 467 ++++++++++++ .../src/executors/update/executor.ts | 90 +++ .../src/executors/update/schema.d.ts | 5 + .../src/executors/update/schema.json | 23 + .../src/executors/utils/logger.spec.ts | 44 ++ .../nx-python/src/executors/utils/logger.ts | 23 + .../nx-python/src/executors/utils/poetry.ts | 49 ++ .../__snapshots__/generator.spec.ts.snap | 716 ++++++++++++++++++ .../src/generators/project/files/CHANGELOG.md | 8 + .../src/generators/project/files/README.md | 7 + .../project/files/__dot__flake8.template | 11 + .../project/files/__moduleName__/index.py | 3 + .../src/generators/project/files/poetry.toml | 2 + .../generators/project/files/pyproject.toml | 37 + .../project/files/tests/test_index.py | 5 + .../src/generators/project/files/tox.ini | 10 + .../src/generators/project/generator.spec.ts | 129 ++++ .../src/generators/project/generator.ts | 180 +++++ .../src/generators/project/schema.d.ts | 13 + .../src/generators/project/schema.json | 81 ++ .../src/graph/dependency-graph.spec.ts | 115 +++ .../nx-python/src/graph/dependency-graph.ts | 112 +++ packages/nx-python/src/index.spec.ts | 8 + packages/nx-python/src/index.ts | 1 + .../src/utils/mocks/child_process.mock.ts | 7 + .../nx-python/src/utils/mocks/uuid.mock.ts | 7 + .../nx-python/tsconfig.json | 0 .../nx-python/tsconfig.lib.json | 3 +- .../nx-python/tsconfig.spec.json | 1 + .../tools => tools}/generators/.gitkeep | 0 .../tools => tools}/tsconfig.tools.json | 0 .../tsconfig.base.json => tsconfig.base.json | 2 +- lucasvieira/workspace.json => workspace.json | 0 99 files changed, 5278 insertions(+), 349 deletions(-) rename lucasvieira/.editorconfig => .editorconfig (100%) rename lucasvieira/.eslintrc.json => .eslintrc.json (100%) rename lucasvieira/.gitignore => .gitignore (100%) rename lucasvieira/.prettierignore => .prettierignore (100%) rename lucasvieira/.prettierrc => .prettierrc (100%) rename {lucasvieira/.vscode => .vscode}/extensions.json (100%) create mode 100644 README.md rename {lucasvieira/e2e => e2e}/nx-python-e2e/jest.config.js (100%) rename {lucasvieira/e2e => e2e}/nx-python-e2e/project.json (100%) create mode 100644 e2e/nx-python-e2e/tests/nx-python.spec.ts rename {lucasvieira/e2e => e2e}/nx-python-e2e/tsconfig.json (100%) rename {lucasvieira/e2e => e2e}/nx-python-e2e/tsconfig.spec.json (100%) rename lucasvieira/jest.config.js => jest.config.js (100%) rename lucasvieira/jest.preset.js => jest.preset.js (100%) delete mode 100644 lucasvieira/README.md delete mode 100644 lucasvieira/e2e/nx-python-e2e/tests/nx-python.spec.ts delete mode 100644 lucasvieira/packages/nx-python/README.md delete mode 100644 lucasvieira/packages/nx-python/executors.json delete mode 100644 lucasvieira/packages/nx-python/generators.json delete mode 100644 lucasvieira/packages/nx-python/package.json delete mode 100644 lucasvieira/packages/nx-python/src/executors/build/executor.spec.ts delete mode 100644 lucasvieira/packages/nx-python/src/executors/build/executor.ts delete mode 100644 lucasvieira/packages/nx-python/src/executors/build/schema.d.ts delete mode 100644 lucasvieira/packages/nx-python/src/executors/build/schema.json delete mode 100644 lucasvieira/packages/nx-python/src/generators/nx-python/files/src/index.ts__template__ delete mode 100644 lucasvieira/packages/nx-python/src/generators/nx-python/generator.spec.ts delete mode 100644 lucasvieira/packages/nx-python/src/generators/nx-python/generator.ts delete mode 100644 lucasvieira/packages/nx-python/src/generators/nx-python/schema.d.ts delete mode 100644 lucasvieira/packages/nx-python/src/generators/nx-python/schema.json delete mode 100644 lucasvieira/packages/nx-python/src/index.ts rename lucasvieira/nx.json => nx.json (100%) rename lucasvieira/package-lock.json => package-lock.json (93%) rename lucasvieira/package.json => package.json (82%) rename {lucasvieira/packages => packages}/nx-python/.babelrc (100%) rename {lucasvieira/packages => packages}/nx-python/.eslintrc.json (100%) create mode 100644 packages/nx-python/README.md create mode 100644 packages/nx-python/executors.json create mode 100644 packages/nx-python/generators.json rename {lucasvieira/packages => packages}/nx-python/jest.config.js (100%) create mode 100644 packages/nx-python/package.json rename {lucasvieira/packages => packages}/nx-python/project.json (100%) create mode 100644 packages/nx-python/src/dependency/update-dependency.ts create mode 100644 packages/nx-python/src/executors/add/executor.spec.ts create mode 100644 packages/nx-python/src/executors/add/executor.ts create mode 100644 packages/nx-python/src/executors/add/schema.d.ts create mode 100644 packages/nx-python/src/executors/add/schema.json create mode 100644 packages/nx-python/src/executors/build/executor.spec.ts create mode 100644 packages/nx-python/src/executors/build/executor.ts create mode 100644 packages/nx-python/src/executors/build/schema.d.ts create mode 100644 packages/nx-python/src/executors/build/schema.json create mode 100644 packages/nx-python/src/executors/flake8/executor.spec.ts create mode 100644 packages/nx-python/src/executors/flake8/executor.ts create mode 100644 packages/nx-python/src/executors/flake8/schema.d.ts create mode 100644 packages/nx-python/src/executors/flake8/schema.json create mode 100644 packages/nx-python/src/executors/install/executor.spec.ts create mode 100644 packages/nx-python/src/executors/install/executor.ts create mode 100644 packages/nx-python/src/executors/install/schema.d.ts create mode 100644 packages/nx-python/src/executors/install/schema.json create mode 100644 packages/nx-python/src/executors/remove/executor.spec.ts create mode 100644 packages/nx-python/src/executors/remove/executor.ts create mode 100644 packages/nx-python/src/executors/remove/schema.d.ts create mode 100644 packages/nx-python/src/executors/remove/schema.json create mode 100644 packages/nx-python/src/executors/tox/executor.spec.ts create mode 100644 packages/nx-python/src/executors/tox/executor.ts create mode 100644 packages/nx-python/src/executors/tox/schema.d.ts create mode 100644 packages/nx-python/src/executors/tox/schema.json create mode 100644 packages/nx-python/src/executors/update/executor.spec.ts create mode 100644 packages/nx-python/src/executors/update/executor.ts create mode 100644 packages/nx-python/src/executors/update/schema.d.ts create mode 100644 packages/nx-python/src/executors/update/schema.json create mode 100644 packages/nx-python/src/executors/utils/logger.spec.ts create mode 100644 packages/nx-python/src/executors/utils/logger.ts create mode 100644 packages/nx-python/src/executors/utils/poetry.ts create mode 100644 packages/nx-python/src/generators/project/__snapshots__/generator.spec.ts.snap create mode 100644 packages/nx-python/src/generators/project/files/CHANGELOG.md create mode 100644 packages/nx-python/src/generators/project/files/README.md create mode 100644 packages/nx-python/src/generators/project/files/__dot__flake8.template create mode 100644 packages/nx-python/src/generators/project/files/__moduleName__/index.py create mode 100644 packages/nx-python/src/generators/project/files/poetry.toml create mode 100644 packages/nx-python/src/generators/project/files/pyproject.toml create mode 100644 packages/nx-python/src/generators/project/files/tests/test_index.py create mode 100644 packages/nx-python/src/generators/project/files/tox.ini create mode 100644 packages/nx-python/src/generators/project/generator.spec.ts create mode 100644 packages/nx-python/src/generators/project/generator.ts create mode 100644 packages/nx-python/src/generators/project/schema.d.ts create mode 100644 packages/nx-python/src/generators/project/schema.json create mode 100644 packages/nx-python/src/graph/dependency-graph.spec.ts create mode 100644 packages/nx-python/src/graph/dependency-graph.ts create mode 100644 packages/nx-python/src/index.spec.ts create mode 100644 packages/nx-python/src/index.ts create mode 100644 packages/nx-python/src/utils/mocks/child_process.mock.ts create mode 100644 packages/nx-python/src/utils/mocks/uuid.mock.ts rename {lucasvieira/packages => packages}/nx-python/tsconfig.json (100%) rename {lucasvieira/packages => packages}/nx-python/tsconfig.lib.json (68%) rename {lucasvieira/packages => packages}/nx-python/tsconfig.spec.json (92%) rename {lucasvieira/tools => tools}/generators/.gitkeep (100%) rename {lucasvieira/tools => tools}/tsconfig.tools.json (100%) rename lucasvieira/tsconfig.base.json => tsconfig.base.json (87%) rename lucasvieira/workspace.json => workspace.json (100%) diff --git a/lucasvieira/.editorconfig b/.editorconfig similarity index 100% rename from lucasvieira/.editorconfig rename to .editorconfig diff --git a/lucasvieira/.eslintrc.json b/.eslintrc.json similarity index 100% rename from lucasvieira/.eslintrc.json rename to .eslintrc.json diff --git a/lucasvieira/.gitignore b/.gitignore similarity index 100% rename from lucasvieira/.gitignore rename to .gitignore diff --git a/lucasvieira/.prettierignore b/.prettierignore similarity index 100% rename from lucasvieira/.prettierignore rename to .prettierignore diff --git a/lucasvieira/.prettierrc b/.prettierrc similarity index 100% rename from lucasvieira/.prettierrc rename to .prettierrc diff --git a/lucasvieira/.vscode/extensions.json b/.vscode/extensions.json similarity index 100% rename from lucasvieira/.vscode/extensions.json rename to .vscode/extensions.json diff --git a/README.md b/README.md new file mode 100644 index 0000000..88a3d4a --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +# @nxlv/nx-plugins + +## What is Nx + +🔎 Extensible Dev Tools for Monorepos. + +## Plugins available + +- [@nxlv/nx-plugins](packages/nx-python/README.md) diff --git a/lucasvieira/e2e/nx-python-e2e/jest.config.js b/e2e/nx-python-e2e/jest.config.js similarity index 100% rename from lucasvieira/e2e/nx-python-e2e/jest.config.js rename to e2e/nx-python-e2e/jest.config.js diff --git a/lucasvieira/e2e/nx-python-e2e/project.json b/e2e/nx-python-e2e/project.json similarity index 100% rename from lucasvieira/e2e/nx-python-e2e/project.json rename to e2e/nx-python-e2e/project.json diff --git a/e2e/nx-python-e2e/tests/nx-python.spec.ts b/e2e/nx-python-e2e/tests/nx-python.spec.ts new file mode 100644 index 0000000..add1d75 --- /dev/null +++ b/e2e/nx-python-e2e/tests/nx-python.spec.ts @@ -0,0 +1,86 @@ +import { + ensureNxProject, + readJson, + runNxCommandAsync, + updateFile, + checkFilesExist, +} from '@nrwl/nx-plugin/testing'; +describe('nx-python e2e', () => { + it('should create nx-python project', async (done) => { + const app1 = 'app1'; + const lib1 = 'lib1'; + ensureNxProject('@nxlv/python', 'dist/packages/nx-python'); + + const nxJson = readJson('nx.json'); + nxJson.plugins = ['@nxlv/nx-python']; + + updateFile('nx.json', JSON.stringify(nxJson, null, 4)); + + await runNxCommandAsync( + `generate @nxlv/python:project ${app1} --type "application"` + ); + + await runNxCommandAsync( + `generate @nxlv/python:project ${lib1} --type "library"` + ); + + await runNxCommandAsync(`run ${app1}:add --name ${lib1} --local`); + + await runNxCommandAsync(`run ${lib1}:add --name pendulum`); + + await runNxCommandAsync(`run ${app1}:lint`); + + await runNxCommandAsync(`run ${app1}:build`); + + expect(() => + checkFilesExist( + `apps/${app1}/dist/${app1.replace('-', '_')}-1.0.0-py3-none-any.whl`, + `apps/${app1}/dist/${app1}-1.0.0.tar.gz` + ) + ).not.toThrow(); + + done(); + }, 3000000); + + it('should create nx-python project with 3 levels', async (done) => { + const app1 = 'app1'; + const lib1 = 'lib1'; + const lib2 = 'lib2'; + + ensureNxProject('@nxlv/python', 'dist/packages/nx-python'); + + const nxJson = readJson('nx.json'); + nxJson.plugins = ['@nxlv/python']; + + updateFile('nx.json', JSON.stringify(nxJson, null, 4)); + + await runNxCommandAsync( + `generate @nxlv/python:project ${app1} --type "application"` + ); + + await runNxCommandAsync( + `generate @nxlv/python:project ${lib1} --type "library"` + ); + + await runNxCommandAsync( + `generate @nxlv/python:project ${lib2} --type "library"` + ); + + await runNxCommandAsync(`run ${lib1}:add --name ${lib2} --local`); + + await runNxCommandAsync(`run ${app1}:add --name ${lib1} --local`); + + await runNxCommandAsync(`run ${lib2}:add --name numpy`); + + await runNxCommandAsync(`run ${app1}:build`); + + expect(() => + checkFilesExist( + `apps/${app1}/dist/${app1.replace('-', '_')}-1.0.0-py3-none-any.whl`, + `apps/${app1}/dist/${app1}-1.0.0.tar.gz` + ) + ).not.toThrow(); + + done(); + }, 3000000); +}); diff --git a/lucasvieira/e2e/nx-python-e2e/tsconfig.json b/e2e/nx-python-e2e/tsconfig.json similarity index 100% rename from lucasvieira/e2e/nx-python-e2e/tsconfig.json rename to e2e/nx-python-e2e/tsconfig.json diff --git a/lucasvieira/e2e/nx-python-e2e/tsconfig.spec.json b/e2e/nx-python-e2e/tsconfig.spec.json similarity index 100% rename from lucasvieira/e2e/nx-python-e2e/tsconfig.spec.json rename to e2e/nx-python-e2e/tsconfig.spec.json diff --git a/lucasvieira/jest.config.js b/jest.config.js similarity index 100% rename from lucasvieira/jest.config.js rename to jest.config.js diff --git a/lucasvieira/jest.preset.js b/jest.preset.js similarity index 100% rename from lucasvieira/jest.preset.js rename to jest.preset.js diff --git a/lucasvieira/README.md b/lucasvieira/README.md deleted file mode 100644 index 921e0e7..0000000 --- a/lucasvieira/README.md +++ /dev/null @@ -1,94 +0,0 @@ - - -# Lucasvieira - -This project was generated using [Nx](https://nx.dev). - -

- -🔎 **Smart, Fast and Extensible Build System** - -## Adding capabilities to your workspace - -Nx supports many plugins which add capabilities for developing different types of applications and different tools. - -These capabilities include generating applications, libraries, etc as well as the devtools to test, and build projects as well. - -Below are our core plugins: - -- [React](https://reactjs.org) - - `npm install --save-dev @nrwl/react` -- Web (no framework frontends) - - `npm install --save-dev @nrwl/web` -- [Angular](https://angular.io) - - `npm install --save-dev @nrwl/angular` -- [Nest](https://nestjs.com) - - `npm install --save-dev @nrwl/nest` -- [Express](https://expressjs.com) - - `npm install --save-dev @nrwl/express` -- [Node](https://nodejs.org) - - `npm install --save-dev @nrwl/node` - -There are also many [community plugins](https://nx.dev/community) you could add. - -## Generate an application - -Run `nx g @nrwl/react:app my-app` to generate an application. - -> You can use any of the plugins above to generate applications as well. - -When using Nx, you can create multiple applications and libraries in the same workspace. - -## Generate a library - -Run `nx g @nrwl/react:lib my-lib` to generate a library. - -> You can also use any of the plugins above to generate libraries as well. - -Libraries are shareable across libraries and applications. They can be imported from `@lucasvieira/mylib`. - -## Development server - -Run `nx serve my-app` for a dev server. Navigate to http://localhost:4200/. The app will automatically reload if you change any of the source files. - -## Code scaffolding - -Run `nx g @nrwl/react:component my-component --project=my-app` to generate a new component. - -## Build - -Run `nx build my-app` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build. - -## Running unit tests - -Run `nx test my-app` to execute the unit tests via [Jest](https://jestjs.io). - -Run `nx affected:test` to execute the unit tests affected by a change. - -## Running end-to-end tests - -Run `nx e2e my-app` to execute the end-to-end tests via [Cypress](https://www.cypress.io). - -Run `nx affected:e2e` to execute the end-to-end tests affected by a change. - -## Understand your workspace - -Run `nx graph` to see a diagram of the dependencies of your projects. - -## Further help - -Visit the [Nx Documentation](https://nx.dev) to learn more. - - - -## ☁ Nx Cloud - -### Distributed Computation Caching & Distributed Task Execution - -

- -Nx Cloud pairs with Nx in order to enable you to build and test code more rapidly, by up to 10 times. Even teams that are new to Nx can connect to Nx Cloud and start saving time instantly. - -Teams using Nx gain the advantage of building full-stack applications with their preferred framework alongside Nx’s advanced code generation and project dependency graph, plus a unified experience for both frontend and backend developers. - -Visit [Nx Cloud](https://nx.app/) to learn more. diff --git a/lucasvieira/e2e/nx-python-e2e/tests/nx-python.spec.ts b/lucasvieira/e2e/nx-python-e2e/tests/nx-python.spec.ts deleted file mode 100644 index 0180669..0000000 --- a/lucasvieira/e2e/nx-python-e2e/tests/nx-python.spec.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { - checkFilesExist, - ensureNxProject, - readJson, - runNxCommandAsync, - uniq, -} from '@nrwl/nx-plugin/testing'; -describe('nx-python e2e', () => { - it('should create nx-python', async () => { - const plugin = uniq('nx-python'); - ensureNxProject('@lucasvieira/nx-python', 'dist/packages/nx-python'); - await runNxCommandAsync( - `generate @lucasvieira/nx-python:nx-python ${plugin}` - ); - - const result = await runNxCommandAsync(`build ${plugin}`); - expect(result.stdout).toContain('Executor ran'); - }, 120000); - - describe('--directory', () => { - it('should create src in the specified directory', async () => { - const plugin = uniq('nx-python'); - ensureNxProject('@lucasvieira/nx-python', 'dist/packages/nx-python'); - await runNxCommandAsync( - `generate @lucasvieira/nx-python:nx-python ${plugin} --directory subdir` - ); - expect(() => - checkFilesExist(`libs/subdir/${plugin}/src/index.ts`) - ).not.toThrow(); - }, 120000); - }); - - describe('--tags', () => { - it('should add tags to the project', async () => { - const plugin = uniq('nx-python'); - ensureNxProject('@lucasvieira/nx-python', 'dist/packages/nx-python'); - await runNxCommandAsync( - `generate @lucasvieira/nx-python:nx-python ${plugin} --tags e2etag,e2ePackage` - ); - const project = readJson(`libs/${plugin}/project.json`); - expect(project.tags).toEqual(['e2etag', 'e2ePackage']); - }, 120000); - }); -}); diff --git a/lucasvieira/packages/nx-python/README.md b/lucasvieira/packages/nx-python/README.md deleted file mode 100644 index 8b63837..0000000 --- a/lucasvieira/packages/nx-python/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# nx-python - -This library was generated with [Nx](https://nx.dev). - -## Running unit tests - -Run `nx test nx-python` to execute the unit tests via [Jest](https://jestjs.io). diff --git a/lucasvieira/packages/nx-python/executors.json b/lucasvieira/packages/nx-python/executors.json deleted file mode 100644 index b92c873..0000000 --- a/lucasvieira/packages/nx-python/executors.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "$schema": "http://json-schema.org/schema", - "executors": { - "build": { - "implementation": "./src/executors/build/executor", - "schema": "./src/executors/build/schema.json", - "description": "build executor" - } - } -} diff --git a/lucasvieira/packages/nx-python/generators.json b/lucasvieira/packages/nx-python/generators.json deleted file mode 100644 index fcc7403..0000000 --- a/lucasvieira/packages/nx-python/generators.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "$schema": "http://json-schema.org/schema", - "name": "nx-python", - "version": "0.0.1", - "generators": { - "nx-python": { - "factory": "./src/generators/nx-python/generator", - "schema": "./src/generators/nx-python/schema.json", - "description": "nx-python generator" - } - } -} diff --git a/lucasvieira/packages/nx-python/package.json b/lucasvieira/packages/nx-python/package.json deleted file mode 100644 index f045d84..0000000 --- a/lucasvieira/packages/nx-python/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "@lucasvieira/nx-python", - "version": "0.0.1", - "main": "src/index.js", - "generators": "./generators.json", - "executors": "./executors.json" -} diff --git a/lucasvieira/packages/nx-python/src/executors/build/executor.spec.ts b/lucasvieira/packages/nx-python/src/executors/build/executor.spec.ts deleted file mode 100644 index 32de13c..0000000 --- a/lucasvieira/packages/nx-python/src/executors/build/executor.spec.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { BuildExecutorSchema } from './schema'; -import executor from './executor'; - -const options: BuildExecutorSchema = {}; - -describe('Build Executor', () => { - it('can run', async () => { - const output = await executor(options); - expect(output.success).toBe(true); - }); -}); diff --git a/lucasvieira/packages/nx-python/src/executors/build/executor.ts b/lucasvieira/packages/nx-python/src/executors/build/executor.ts deleted file mode 100644 index 39569a7..0000000 --- a/lucasvieira/packages/nx-python/src/executors/build/executor.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { BuildExecutorSchema } from './schema'; - -export default async function runExecutor(options: BuildExecutorSchema) { - console.log('Executor ran for Build', options); - return { - success: true, - }; -} diff --git a/lucasvieira/packages/nx-python/src/executors/build/schema.d.ts b/lucasvieira/packages/nx-python/src/executors/build/schema.d.ts deleted file mode 100644 index f8247ab..0000000 --- a/lucasvieira/packages/nx-python/src/executors/build/schema.d.ts +++ /dev/null @@ -1 +0,0 @@ -export interface BuildExecutorSchema {} // eslint-disable-line diff --git a/lucasvieira/packages/nx-python/src/executors/build/schema.json b/lucasvieira/packages/nx-python/src/executors/build/schema.json deleted file mode 100644 index 54d8d22..0000000 --- a/lucasvieira/packages/nx-python/src/executors/build/schema.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "$schema": "http://json-schema.org/schema", - "cli": "nx", - "title": "Build executor", - "description": "", - "type": "object", - "properties": {}, - "required": [] -} diff --git a/lucasvieira/packages/nx-python/src/generators/nx-python/files/src/index.ts__template__ b/lucasvieira/packages/nx-python/src/generators/nx-python/files/src/index.ts__template__ deleted file mode 100644 index dde3cb6..0000000 --- a/lucasvieira/packages/nx-python/src/generators/nx-python/files/src/index.ts__template__ +++ /dev/null @@ -1 +0,0 @@ -const variable = "<%= projectName %>"; \ No newline at end of file diff --git a/lucasvieira/packages/nx-python/src/generators/nx-python/generator.spec.ts b/lucasvieira/packages/nx-python/src/generators/nx-python/generator.spec.ts deleted file mode 100644 index b030122..0000000 --- a/lucasvieira/packages/nx-python/src/generators/nx-python/generator.spec.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing'; -import { Tree, readProjectConfiguration } from '@nrwl/devkit'; - -import generator from './generator'; -import { NxPythonGeneratorSchema } from './schema'; - -describe('nx-python generator', () => { - let appTree: Tree; - const options: NxPythonGeneratorSchema = { name: 'test' }; - - beforeEach(() => { - appTree = createTreeWithEmptyWorkspace(); - }); - - it('should run successfully', async () => { - await generator(appTree, options); - const config = readProjectConfiguration(appTree, 'test'); - expect(config).toBeDefined(); - }); -}); diff --git a/lucasvieira/packages/nx-python/src/generators/nx-python/generator.ts b/lucasvieira/packages/nx-python/src/generators/nx-python/generator.ts deleted file mode 100644 index 030c788..0000000 --- a/lucasvieira/packages/nx-python/src/generators/nx-python/generator.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { - addProjectConfiguration, - formatFiles, - generateFiles, - getWorkspaceLayout, - names, - offsetFromRoot, - Tree, -} from '@nrwl/devkit'; -import * as path from 'path'; -import { NxPythonGeneratorSchema } from './schema'; - -interface NormalizedSchema extends NxPythonGeneratorSchema { - projectName: string; - projectRoot: string; - projectDirectory: string; - parsedTags: string[]; -} - -function normalizeOptions( - tree: Tree, - options: NxPythonGeneratorSchema -): 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(tree).libsDir}/${projectDirectory}`; - const parsedTags = options.tags - ? options.tags.split(',').map((s) => s.trim()) - : []; - - return { - ...options, - projectName, - projectRoot, - projectDirectory, - parsedTags, - }; -} - -function addFiles(tree: Tree, options: NormalizedSchema) { - const templateOptions = { - ...options, - ...names(options.name), - offsetFromRoot: offsetFromRoot(options.projectRoot), - template: '', - }; - generateFiles( - tree, - path.join(__dirname, 'files'), - options.projectRoot, - templateOptions - ); -} - -export default async function (tree: Tree, options: NxPythonGeneratorSchema) { - const normalizedOptions = normalizeOptions(tree, options); - addProjectConfiguration(tree, normalizedOptions.projectName, { - root: normalizedOptions.projectRoot, - projectType: 'library', - sourceRoot: `${normalizedOptions.projectRoot}/src`, - targets: { - build: { - executor: '@lucasvieira/nx-python:build', - }, - }, - tags: normalizedOptions.parsedTags, - }); - addFiles(tree, normalizedOptions); - await formatFiles(tree); -} diff --git a/lucasvieira/packages/nx-python/src/generators/nx-python/schema.d.ts b/lucasvieira/packages/nx-python/src/generators/nx-python/schema.d.ts deleted file mode 100644 index 7240d82..0000000 --- a/lucasvieira/packages/nx-python/src/generators/nx-python/schema.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -export interface NxPythonGeneratorSchema { - name: string; - tags?: string; - directory?: string; -} diff --git a/lucasvieira/packages/nx-python/src/generators/nx-python/schema.json b/lucasvieira/packages/nx-python/src/generators/nx-python/schema.json deleted file mode 100644 index 4392b25..0000000 --- a/lucasvieira/packages/nx-python/src/generators/nx-python/schema.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "$schema": "http://json-schema.org/schema", - "cli": "nx", - "$id": "NxPython", - "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" - } - }, - "required": ["name"] -} diff --git a/lucasvieira/packages/nx-python/src/index.ts b/lucasvieira/packages/nx-python/src/index.ts deleted file mode 100644 index e69de29..0000000 diff --git a/lucasvieira/nx.json b/nx.json similarity index 100% rename from lucasvieira/nx.json rename to nx.json diff --git a/lucasvieira/package-lock.json b/package-lock.json similarity index 93% rename from lucasvieira/package-lock.json rename to package-lock.json index a186e0e..64fcecf 100644 --- a/lucasvieira/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { - "name": "lucasvieira", - "version": "0.0.0", + "name": "@lucasvieira/nx-plugins", + "version": "1.0.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -511,6 +511,11 @@ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, + "@iarna/toml": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", + "integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==" + }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -544,6 +549,16 @@ "slash": "^3.0.0" }, "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, "jest-util": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", @@ -641,6 +656,16 @@ "collect-v8-coverage": "^1.0.0" } }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, "istanbul-lib-instrument": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz", @@ -764,6 +789,18 @@ "ci-info": "^3.2.0", "graceful-fs": "^4.2.9", "picomatch": "^2.2.3" + }, + "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } } } @@ -809,6 +846,18 @@ "string-length": "^4.0.1", "terminal-link": "^2.0.0", "v8-to-istanbul": "^8.0.0" + }, + "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } }, "@jest/source-map": { @@ -883,6 +932,16 @@ "write-file-atomic": "^3.0.0" }, "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, "jest-util": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", @@ -910,6 +969,18 @@ "@types/node": "*", "@types/yargs": "^16.0.0", "chalk": "^4.0.0" + }, + "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } }, "@jridgewell/resolve-uri": { @@ -971,6 +1042,18 @@ "enquirer": "~2.3.6", "v8-compile-cache": "2.3.0", "yargs-parser": "20.0.0" + }, + "dependencies": { + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } }, "@nrwl/devkit": { @@ -1026,6 +1109,16 @@ "integrity": "sha512-iwKiHvV8p48/82+eJRCy/WcnAZBOFr2ZJ5VLtRuV+sz0mkWjoimnLZ8SEshoru9PVoKF7hfG7AMqKaVOUjSJFg==", "dev": true, "optional": true + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } } } }, @@ -1046,6 +1139,18 @@ "resolve.exports": "1.1.0", "rxjs": "^6.5.4", "tslib": "^2.3.0" + }, + "dependencies": { + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } }, "@nrwl/linter": { @@ -1093,6 +1198,16 @@ "webpack-node-externals": "^3.0.0" }, "dependencies": { + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, "glob": { "version": "7.1.4", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", @@ -1155,6 +1270,16 @@ "yargs-parser": "20.0.0" }, "dependencies": { + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, "nx": { "version": "13.8.3", "resolved": "https://registry.npmjs.org/nx/-/nx-13.8.3.tgz", @@ -1199,6 +1324,16 @@ "yargs-parser": "20.0.0" }, "dependencies": { + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, "glob": { "version": "7.1.4", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", @@ -1283,6 +1418,18 @@ "pirates": "^4.0.4", "tslib": "^2.3.1", "typescript": "^4.5.3" + }, + "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } }, "@swc-node/sourcemap-support": { @@ -1959,7 +2106,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "requires": { "color-convert": "^2.0.1" } @@ -2027,6 +2173,18 @@ "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "slash": "^3.0.0" + }, + "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } }, "babel-plugin-istanbul": { @@ -2198,10 +2356,9 @@ "dev": true }, "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -2300,7 +2457,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "requires": { "color-name": "~1.1.4" } @@ -2308,8 +2464,7 @@ "color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "combined-stream": { "version": "1.0.8", @@ -2681,6 +2836,16 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, "escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -3044,6 +3209,16 @@ "tapable": "^1.0.0" }, "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, "schema-utils": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", @@ -3229,8 +3404,7 @@ "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, "html-encoding-sniffer": { "version": "2.0.1", @@ -3642,6 +3816,18 @@ "jest-validate": "^27.5.1", "prompts": "^2.0.1", "yargs": "^16.2.0" + }, + "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } }, "jest-config": { @@ -3674,6 +3860,18 @@ "pretty-format": "^27.5.1", "slash": "^3.0.0", "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } }, "jest-resolve": { @@ -3692,6 +3890,18 @@ "resolve": "^1.20.0", "resolve.exports": "^1.1.0", "slash": "^3.0.0" + }, + "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } }, "jest-util": { @@ -3706,6 +3916,18 @@ "ci-info": "^3.2.0", "graceful-fs": "^4.2.9", "picomatch": "^2.2.3" + }, + "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } }, "wrap-ansi": { @@ -3798,6 +4020,16 @@ "collect-v8-coverage": "^1.0.0" } }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, "jest-util": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", @@ -3841,6 +4073,18 @@ "jest-validate": "^27.2.2", "micromatch": "^4.0.4", "pretty-format": "^27.2.2" + }, + "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } }, "jest-diff": { @@ -3853,6 +4097,18 @@ "diff-sequences": "^27.5.1", "jest-get-type": "^27.5.1", "pretty-format": "^27.5.1" + }, + "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } }, "jest-docblock": { @@ -3877,6 +4133,16 @@ "pretty-format": "^27.5.1" }, "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, "jest-util": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", @@ -3920,6 +4186,18 @@ "ci-info": "^3.2.0", "graceful-fs": "^4.2.9", "picomatch": "^2.2.3" + }, + "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } } } @@ -3950,6 +4228,18 @@ "ci-info": "^3.2.0", "graceful-fs": "^4.2.9", "picomatch": "^2.2.3" + }, + "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } } } @@ -3993,6 +4283,18 @@ "ci-info": "^3.2.0", "graceful-fs": "^4.2.9", "picomatch": "^2.2.3" + }, + "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } } } @@ -4034,6 +4336,16 @@ "collect-v8-coverage": "^1.0.0" } }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, "jest-util": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", @@ -4070,6 +4382,18 @@ "jest-diff": "^27.5.1", "jest-get-type": "^27.5.1", "pretty-format": "^27.5.1" + }, + "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } }, "jest-message-util": { @@ -4087,6 +4411,18 @@ "pretty-format": "^27.5.1", "slash": "^3.0.0", "stack-utils": "^2.0.3" + }, + "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } }, "jest-mock": { @@ -4127,6 +4463,18 @@ "jest-validate": "^27.2.2", "resolve": "^1.20.0", "slash": "^3.0.0" + }, + "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } }, "jest-resolve-dependencies": { @@ -4181,6 +4529,16 @@ "collect-v8-coverage": "^1.0.0" } }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, "jest-resolve": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.5.1.tgz", @@ -4257,6 +4615,16 @@ "collect-v8-coverage": "^1.0.0" } }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, "jest-resolve": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.5.1.tgz", @@ -4331,6 +4699,16 @@ "semver": "^7.3.2" }, "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, "jest-util": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", @@ -4359,6 +4737,18 @@ "graceful-fs": "^4.2.4", "is-ci": "^3.0.0", "picomatch": "^2.2.3" + }, + "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } }, "jest-validate": { @@ -4380,6 +4770,16 @@ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } } } }, @@ -4410,6 +4810,16 @@ "collect-v8-coverage": "^1.0.0" } }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, "jest-util": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", @@ -4732,6 +5142,12 @@ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", "dev": true }, + "mock-fs": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-5.1.2.tgz", + "integrity": "sha512-YkjQkdLulFrz0vD4BfNQdQRVmgycXTV7ykuHMlyv+C8WCHazpkiQRDthwa02kSyo8wKnY9wRptHfQLgmf0eR+A==", + "dev": true + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -5325,7 +5741,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, "requires": { "has-flag": "^4.0.0" } @@ -5514,6 +5929,18 @@ "enhanced-resolve": "^5.0.0", "micromatch": "^4.0.0", "semver": "^7.3.4" + }, + "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } }, "ts-node": { @@ -5568,6 +5995,18 @@ "chalk": "^4.1.0", "enhanced-resolve": "^5.7.0", "tsconfig-paths": "^3.9.0" + }, + "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } }, "tslib": { @@ -5643,6 +6082,11 @@ "punycode": "^2.1.0" } }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + }, "v8-compile-cache": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", diff --git a/lucasvieira/package.json b/package.json similarity index 82% rename from lucasvieira/package.json rename to package.json index 4927060..6d79545 100644 --- a/lucasvieira/package.json +++ b/package.json @@ -1,6 +1,6 @@ { - "name": "lucasvieira", - "version": "0.0.0", + "name": "@nxlv/nx-plugins", + "version": "1.0.0", "license": "MIT", "scripts": { "start": "nx serve", @@ -9,7 +9,10 @@ }, "private": true, "dependencies": { - "tslib": "^2.0.0" + "@iarna/toml": "^2.2.5", + "chalk": "^4.1.1", + "tslib": "^2.0.0", + "uuid": "^8.3.2" }, "devDependencies": { "@nrwl/cli": "13.8.3", @@ -28,6 +31,7 @@ "eslint": "~8.7.0", "eslint-config-prettier": "8.1.0", "jest": "27.2.3", + "mock-fs": "^5.1.2", "prettier": "^2.5.1", "ts-jest": "27.0.5", "tslib": "^2.0.0", diff --git a/lucasvieira/packages/nx-python/.babelrc b/packages/nx-python/.babelrc similarity index 100% rename from lucasvieira/packages/nx-python/.babelrc rename to packages/nx-python/.babelrc diff --git a/lucasvieira/packages/nx-python/.eslintrc.json b/packages/nx-python/.eslintrc.json similarity index 100% rename from lucasvieira/packages/nx-python/.eslintrc.json rename to packages/nx-python/.eslintrc.json diff --git a/packages/nx-python/README.md b/packages/nx-python/README.md new file mode 100644 index 0000000..44c99d1 --- /dev/null +++ b/packages/nx-python/README.md @@ -0,0 +1,168 @@ +# @nxlv/python + +This library was generated with [Nx](https://nx.dev). + +## What is Nx + +🔎 Extensible Dev Tools for Monorepos. + +## What is @nxlv/python + +🔎 An Nx Custom Plugin to generate Python projects using Poetry, Tox and a custom dependency tree plugin + +[Full Nx Monorepo Tools](https://domgen.atlassian.net/wiki/spaces/OPS/pages/1861386252/NX+Monorepo+DevOps+Configuration) + +## Getting Started + +### Add to an existing Nx Workspace + +Install the npm dependency + +```shell +npm install @nxlv/python --save-dev +``` + +### Usage + +#### Add a new Python Project + +```shell +nx generate @nxlv/python:project myproject +``` + +#### Options + +| Option | Type | Description | Required | Default | +| ------------------- | :-------: | -------------------------------------------------- | -------------------------------------- | ------------- | +| `--type` | `string` | Project type `application` or `library` | `true` | `application` | +| `--description` | `string` | Project description | `false` | | +| `--directory` | `string` | A directory where the project is placed | `false` | | +| `--packageName` | `string` | Package name | `true` | | +| `--publishable` | `boolean` | Speficies if the project is publishable or not | `false` | `true` | +| `--customSource` | `boolean` | Speficies if the project uses custom PyPi registry | `false` | `false` | +| `--sourceName` | `string` | Custom PyPi registry name | only if the `--customSource` is `true` | | +| `--sourceUrl` | `string` | Custom PyPi registry url | only if the `--customSource` is `true` | | +| `--sourceSecondary` | `boolean` | Custom PyPi registry secondary flag | only if the `--customSource` is `true` | `true` | +| `--tags` | `string` | Add tags to the project | `false` | | + +#### Add a new dependency to a project + +```shell +nx run {project}:add --name {projectName} --local +``` + +#### Add an external dependency to the project + +To add a new dependency to the project use the `nx run {project}:add` command detailed below. This ensures that any dependent projects are updated. + +```shell +nx run {project}:add --name {dependencyName} +``` + +### Executors + +#### add + +The `@nxlv/python:add` executor handles `poetry add` command to provide a level of abstraction and control in the monorepo projects. + +##### Features + +- Add new external dependencies +- Add local dependencies + +> Both features updates the local workspace dependency tree to keep the lock/venv updated. + +##### Options + +| Option | Type | Description | Required | Default | +| --------- | :-------: | ------------------------------------------------------------- | ---------------------------------------------------- | ------- | +| `--name` | `string` | Dependency name (if local dependency use the Nx project name) | `true` | | +| `--args` | `string` | Custom args to be used in the `poetry add` command | `false` | | +| `--local` | `boolean` | Specifies if the dependency is local | `false` (only if the `--name` is a local dependency) | | + +#### update + +The `@nxlv/python:update` executor handles `poetry update` command to provide a level of abstraction and control in the monorepo projects. + +##### Features + +- Update external dependencies +- Update local dependencies + +> Both features updates the local workspace dependency tree to keep the lock/venv updated. + +##### Options + +| Option | Type | Description | Required | Default | +| --------- | :-------: | ------------------------------------------------------------- | ---------------------------------------------------- | ------- | +| `--name` | `string` | Dependency name (if local dependency use the Nx project name) | `false` | | +| `--args` | `string` | Custom args to be used in the `poetry update` command | `false` | | +| `--local` | `boolean` | Specifies if the dependency is local | `false` (only if the `--name` is a local dependency) | | + +#### remove + +The `@nxlv/python:remove` executor handles `poetry remove` command to provide a level of abstraction and control in the monorepo projects. + +##### Features + +- Remove external dependencies +- Remove local dependencies + +> Both features updates the local workspace dependency tree to keep the lock/venv updated. + +##### Options + +| Option | Type | Description | Required | Default | +| --------- | :-------: | ------------------------------------------------------------- | ---------------------------------------------------- | ------- | +| `--name` | `string` | Dependency name (if local dependency use the Nx project name) | `true` | | +| `--args` | `string` | Custom args to be used in the `poetry remove` command | `false` | | +| `--local` | `boolean` | Specifies if the dependency is local | `false` (only if the `--name` is a local dependency) | | + +#### build + +The `@nxlv/python:build` command handles the `sdist` and `wheel` build generation. When the project has local dependencies the executor copies the package/dependencies recursively. + +##### Options + +| Option | Type | Description | Required | Default | +| ------------------- | :-------: | ---------------------------------------- | -------- | ---------------------------- | +| `--silent` | `boolean` | Hide output text | `false` | `false` | +| `--outputPath` | `string` | Output path for the python tar/whl files | `true` | | +| `--keepBuildFolder` | `boolean` | Keep build folder | `false` | `false` | +| `--ignorePaths` | `array` | Ignore folder/files on build process | `false` | `[".venv", ".tox", "tests"]` | + +#### flake8 + +The `@nxlv/python:flake8` handles the `flake8` linting tasks and reporting generator. + +##### Options + +| Option | Type | Description | Required | Default | +| -------------- | :-------: | ----------------------- | -------- | ------- | +| `--silent` | `boolean` | Hide output text | `false` | `false` | +| `--outputFile` | `string` | Output pylint file path | `true` | | + +#### install + +The `@nxlv/python:install` handles the `poetry install` command for a project. + +#### Options + +| Option | Type | Description | Required | Default | +| ------------ | :-------: | ---------------------------------------------------- | -------- | ------- | +| `--silent` | `boolean` | Hide output text | `false` | `false` | +| `--args` | `string` | Custom arguments (e.g `--group dev`) | `false` | | +| `--cacheDir` | `string` | Custom poetry install cache directory | `false` | | +| `--verbose` | `boolean` | Use verbose mode in the install `poetry install -vv` | `false` | `false` | +| `--debug` | `boolean` | Use debug mode in the install `poetry install -vvv` | `false` | `false` | + +#### tox + +The `@nxlv/python:tox` handles tox executions for a project. + +#### Options + +| Option | Type | Description | Required | Default | +| ---------- | :-------: | -------------------------------- | -------- | ------- | +| `--silent` | `boolean` | Hide output text | `false` | `false` | +| `--args` | `string` | Custom arguments (e.g `-e py38`) | `false` | | diff --git a/packages/nx-python/executors.json b/packages/nx-python/executors.json new file mode 100644 index 0000000..7c4b026 --- /dev/null +++ b/packages/nx-python/executors.json @@ -0,0 +1,40 @@ +{ + "$schema": "http://json-schema.org/schema", + "executors": { + "build": { + "implementation": "./src/executors/build/executor", + "schema": "./src/executors/build/schema.json", + "description": "build executor" + }, + "add": { + "implementation": "./src/executors/add/executor", + "schema": "./src/executors/add/schema.json", + "description": "Add a dependency to the project" + }, + "flake8": { + "implementation": "./src/executors/flake8/executor", + "schema": "./src/executors/flake8/schema.json", + "description": "Flake8 Linting Executor" + }, + "tox": { + "implementation": "./src/executors/tox/executor", + "schema": "./src/executors/tox/schema.json", + "description": "Nx Python Tox Executor" + }, + "install": { + "implementation": "./src/executors/install/executor", + "schema": "./src/executors/install/schema.json", + "description": "Nx Python Poetry Install" + }, + "update": { + "implementation": "./src/executors/update/executor", + "schema": "./src/executors/update/schema.json", + "description": "Poetry Update Wrapper Executor" + }, + "remove": { + "implementation": "./src/executors/remove/executor", + "schema": "./src/executors/remove/schema.json", + "description": "Poetry Remove Wrapper Executor" + } + } +} diff --git a/packages/nx-python/generators.json b/packages/nx-python/generators.json new file mode 100644 index 0000000..16c7785 --- /dev/null +++ b/packages/nx-python/generators.json @@ -0,0 +1,19 @@ +{ + "$schema": "http://json-schema.org/schema", + "name": "nx-python", + "version": "0.0.1", + "generators": { + "project": { + "factory": "./src/generators/project/generator", + "schema": "./src/generators/project/schema.json", + "description": "python project generator" + } + }, + "schematics": { + "project": { + "factory": "./src/generators/project/generator#schematic", + "schema": "./src/generators/project/schema.json", + "description": "python project generator" + } + } +} diff --git a/lucasvieira/packages/nx-python/jest.config.js b/packages/nx-python/jest.config.js similarity index 100% rename from lucasvieira/packages/nx-python/jest.config.js rename to packages/nx-python/jest.config.js diff --git a/packages/nx-python/package.json b/packages/nx-python/package.json new file mode 100644 index 0000000..e593970 --- /dev/null +++ b/packages/nx-python/package.json @@ -0,0 +1,11 @@ +{ + "name": "@nxlv/python", + "version": "0.0.6", + "description": "Custom NX Plugin to support the Python language", + "main": "src/index.js", + "schematics": "./generators.json", + "executors": "./executors.json", + "dependencies": { + "@angular-devkit/schematics": "^13.2.5" + } +} diff --git a/lucasvieira/packages/nx-python/project.json b/packages/nx-python/project.json similarity index 100% rename from lucasvieira/packages/nx-python/project.json rename to packages/nx-python/project.json diff --git a/packages/nx-python/src/dependency/update-dependency.ts b/packages/nx-python/src/dependency/update-dependency.ts new file mode 100644 index 0000000..014fc99 --- /dev/null +++ b/packages/nx-python/src/dependency/update-dependency.ts @@ -0,0 +1,37 @@ +import { ExecutorContext, WorkspaceJsonConfiguration } from '@nrwl/devkit'; +import chalk from 'chalk'; +import { getDependents } from '../graph/dependency-graph'; +import { + getProjectTomlPath, + parseToml, + updateProject, +} from '../executors/utils/poetry'; + +export function updateDependencyTree(context: ExecutorContext) { + const projectConfig = context.workspace.projects[context.projectName]; + const projectToml = getProjectTomlPath(projectConfig); + const { + tool: { + poetry: { name }, + }, + } = parseToml(projectToml); + + updateDependents(context.workspace, context.projectName, name); +} + +export function updateDependents( + workspace: WorkspaceJsonConfiguration, + projectName: string, + modifiedProject: string +) { + const deps = getDependents(projectName, workspace); + + for (const dep of deps) { + console.log(chalk`\nUpdating project {bold ${dep}}`); + const depConfig = workspace.projects[dep]; + + updateProject(modifiedProject, depConfig.root); + + updateDependents(workspace, dep, modifiedProject); + } +} diff --git a/packages/nx-python/src/executors/add/executor.spec.ts b/packages/nx-python/src/executors/add/executor.spec.ts new file mode 100644 index 0000000..49eb764 --- /dev/null +++ b/packages/nx-python/src/executors/add/executor.spec.ts @@ -0,0 +1,425 @@ +import { execSyncMock } from '../../utils/mocks/child_process.mock'; +import executor from './executor'; +import fsMock from 'mock-fs'; +import chalk from 'chalk'; +import { parseToml } from '../utils/poetry'; +import { ExecutorContext } from '@nrwl/devkit'; + +describe('Add Executor', () => { + beforeAll(() => { + console.log(chalk`init chalk`); + }); + + afterEach(() => { + fsMock.restore(); + jest.resetAllMocks(); + }); + + it('run add target and should add the dependency to the project', async () => { + fsMock({ + 'apps/app/pyproject.toml': `[tool.poetry] +name = "app" +version = "1.0.0" + [[tool.poetry.packages]] + include = "app" + + [tool.poetry.dependencies] + python = "^3.8" + click = "click" +`, + }); + + const options = { + name: 'numpy', + local: false, + }; + + const context: ExecutorContext = { + cwd: '', + root: '', + isVerbose: false, + projectName: 'app', + workspace: { + version: 2, + npmScope: '@lucasvieira', + projects: { + app: { + root: 'apps/app', + targets: {}, + }, + }, + }, + }; + + const output = await executor(options, context); + expect(execSyncMock).toHaveBeenCalledWith('poetry add numpy ', { + cwd: 'apps/app', + stdio: 'inherit', + }); + expect(output.success).toBe(true); + }); + + it('run add target and should not add the dependency to the project because the project does not exist', async () => { + fsMock({ + 'apps/app/pyproject.toml': `[tool.poetry] +name = "app" +version = "1.0.0" + [[tool.poetry.packages]] + include = "app" + + [tool.poetry.dependencies] + python = "^3.8" + click = "click" +`, + }); + + const options = { + local: true, + name: 'lib1', + }; + + const context: ExecutorContext = { + cwd: '', + root: '', + isVerbose: false, + projectName: 'app', + workspace: { + version: 2, + npmScope: '@lucasvieira', + projects: { + app: { + root: 'apps/app', + targets: {}, + }, + }, + }, + }; + + const output = await executor(options, context); + expect(execSyncMock).not.toHaveBeenCalled(); + expect(output.success).toBe(false); + }); + + it('run add target and should throw an exception', async () => { + fsMock({ + 'apps/app/pyproject.toml': `[tool.poetry] +name = "app" +version = "1.0.0" + [[tool.poetry.packages]] + include = "app" + + [tool.poetry.dependencies] + python = "^3.8" + click = "click" +`, + }); + + execSyncMock.mockImplementation(() => { + throw new Error('fake error'); + }); + + const options = { + name: 'numpy', + local: false, + }; + + const context: ExecutorContext = { + cwd: '', + root: '', + isVerbose: false, + projectName: 'app', + workspace: { + version: 2, + npmScope: '@lucasvieira', + projects: { + app: { + root: 'apps/app', + targets: {}, + }, + }, + }, + }; + + const output = await executor(options, context); + expect(execSyncMock).toHaveBeenCalledWith('poetry add numpy ', { + cwd: 'apps/app', + stdio: 'inherit', + }); + expect(output.success).toBe(false); + }); + + it('run add target and should update all the dependency tree', async () => { + fsMock({ + 'apps/app/pyproject.toml': `[tool.poetry] +name = "app" +version = "1.0.0" + [[tool.poetry.packages]] + include = "app" + + [tool.poetry.dependencies] + python = "^3.8" + click = "click" + lib1 = { path = "../../libs/lib1" } +`, + + 'apps/app1/pyproject.toml': `[tool.poetry] +name = "app1" +version = "1.0.0" + [[tool.poetry.packages]] + include = "app" + + [tool.poetry.dependencies] + python = "^3.8" + click = "click" + lib1 = { path = "../../libs/lib1" } +`, + + 'libs/lib1/pyproject.toml': `[tool.poetry] + name = "lib1" + version = "1.0.0" + [[tool.poetry.packages]] + include = "app" + + [tool.poetry.dependencies] + python = "^3.8" + shared1 = { path = "../../libs/shared1" }`, + + 'libs/shared1/pyproject.toml': `[tool.poetry] + name = "lib1" + version = "1.0.0" + [[tool.poetry.packages]] + include = "app" + + [tool.poetry.dependencies] + python = "^3.8"`, + }); + + const options = { + name: 'numpy', + local: false, + }; + + const context: ExecutorContext = { + cwd: '', + root: '', + isVerbose: false, + projectName: 'shared1', + workspace: { + version: 2, + npmScope: '@lucasvieira', + projects: { + app: { + root: 'apps/app', + targets: {}, + }, + app1: { + root: 'apps/app1', + targets: {}, + }, + app3: { + root: 'apps/app3', + targets: {}, + }, + lib1: { + root: 'libs/lib1', + targets: {}, + }, + shared1: { + root: 'libs/shared1', + targets: {}, + }, + }, + }, + }; + + const output = await executor(options, context); + expect(execSyncMock).toHaveBeenCalledTimes(4); + expect(execSyncMock).toHaveBeenNthCalledWith(1, 'poetry add numpy ', { + cwd: 'libs/shared1', + stdio: 'inherit', + }); + expect(execSyncMock).toHaveBeenNthCalledWith(2, 'poetry update lib1', { + cwd: 'libs/lib1', + stdio: 'inherit', + }); + expect(execSyncMock).toHaveBeenNthCalledWith(3, 'poetry update lib1', { + cwd: 'apps/app', + stdio: 'inherit', + }); + expect(execSyncMock).toHaveBeenNthCalledWith(4, 'poetry update lib1', { + cwd: 'apps/app1', + stdio: 'inherit', + }); + expect(output.success).toBe(true); + }); + + it('run add target with local dependency', async () => { + fsMock({ + 'apps/app/pyproject.toml': `[tool.poetry] +name = "app" +version = "1.0.0" + [[tool.poetry.packages]] + include = "app" + + [tool.poetry.dependencies] + python = "^3.8" + click = "click"`, + + 'libs/lib1/pyproject.toml': `[tool.poetry] +name = "lib1" +version = "1.0.0" + [[tool.poetry.packages]] + include = "app" + + [tool.poetry.dependencies] + python = "^3.8"`, + }); + + const options = { + name: 'lib1', + local: true, + }; + + const context: ExecutorContext = { + cwd: '', + root: '', + isVerbose: false, + projectName: 'app', + workspace: { + version: 2, + npmScope: '@lucasvieira', + projects: { + app: { + root: 'apps/app', + targets: {}, + }, + lib1: { + root: 'libs/lib1', + targets: {}, + }, + }, + }, + }; + + const output = await executor(options, context); + expect(execSyncMock).toHaveBeenCalledTimes(1); + expect(execSyncMock).toHaveBeenNthCalledWith(1, 'poetry update lib1', { + cwd: 'apps/app', + stdio: 'inherit', + }); + expect(output.success).toBe(true); + }); + + it('run add target with local dependency with project name and package name different', async () => { + fsMock({ + 'apps/app/pyproject.toml': `[tool.poetry] +name = "dgx-devops-app" +version = "1.0.0" + [[tool.poetry.packages]] + include = "app" + + [tool.poetry.dependencies] + python = "^3.8" + click = "click"`, + + 'libs/lib1/pyproject.toml': `[tool.poetry] +name = "dgx-devops-lib1" +version = "1.0.0" + [[tool.poetry.packages]] + include = "app" + + [tool.poetry.dependencies] + python = "^3.8"`, + }); + + const options = { + name: 'lib1', + local: true, + }; + + const context: ExecutorContext = { + cwd: '', + root: '', + isVerbose: false, + projectName: 'app', + workspace: { + version: 2, + npmScope: '@lucasvieira', + projects: { + app: { + root: 'apps/app', + targets: {}, + }, + lib1: { + root: 'libs/lib1', + targets: {}, + }, + }, + }, + }; + + const output = await executor(options, context); + expect(execSyncMock).toHaveBeenCalledTimes(1); + expect(execSyncMock).toHaveBeenNthCalledWith(1, 'poetry update dgx-devops-lib1', { + cwd: 'apps/app', + stdio: 'inherit', + }); + expect(output.success).toBe(true); + + const { + tool: { + poetry: { dependencies }, + }, + } = parseToml('apps/app/pyproject.toml'); + + expect(dependencies['dgx-devops-lib1']).toStrictEqual({ + path: '../../libs/lib1', + develop: true, + }); + }); + + it('run add target and should add the dependency using custom args', async () => { + fsMock({ + 'apps/app/pyproject.toml': `[tool.poetry] +name = "app" +version = "1.0.0" + [[tool.poetry.packages]] + include = "app" + + [tool.poetry.dependencies] + python = "^3.8" + click = "click" +`, + }); + + const options = { + name: 'numpy', + local: false, + args: "--group dev" + }; + + const context: ExecutorContext = { + cwd: '', + root: '', + isVerbose: false, + projectName: 'app', + workspace: { + version: 2, + npmScope: '@lucasvieira', + projects: { + app: { + root: 'apps/app', + targets: {}, + }, + }, + }, + }; + + const output = await executor(options, context); + expect(execSyncMock).toHaveBeenCalledWith('poetry add numpy --group dev', { + cwd: 'apps/app', + stdio: 'inherit', + }); + expect(output.success).toBe(true); + }); +}); diff --git a/packages/nx-python/src/executors/add/executor.ts b/packages/nx-python/src/executors/add/executor.ts new file mode 100644 index 0000000..d25c669 --- /dev/null +++ b/packages/nx-python/src/executors/add/executor.ts @@ -0,0 +1,49 @@ +import { ExecutorContext } from '@nrwl/devkit'; +import { execSync } from 'child_process'; +import { AddExecutorSchema } from './schema'; +import chalk from 'chalk'; +import { updateDependencyTree } from '../../dependency/update-dependency'; +import { updateLocalProject } from '../update/executor'; + +export default async function runExecutor( + options: AddExecutorSchema, + context: ExecutorContext +) { + try { + const projectConfig = context.workspace.projects[context.projectName]; + + if (options.local) { + console.log( + chalk`\n {bold Adding {bgBlue ${options.name} } workspace dependency...}\n` + ); + updateLocalProject(context, options.name, projectConfig); + } else { + console.log( + chalk`\n {bold Adding {bgBlue ${options.name} } dependency...}\n` + ); + const installCommand = `poetry add ${options.name} ${options.args ?? ''}`; + console.log( + chalk`{bold Running command}: ${installCommand} at {bold ${projectConfig.root}} folder\n` + ); + execSync(installCommand, { + cwd: projectConfig.root, + stdio: 'inherit', + }); + } + + updateDependencyTree(context); + + console.log( + chalk`\n {green.bold '${options.name}'} {green dependency has been successfully added to the project}\n` + ); + + return { + success: true, + }; + } catch (error) { + console.log(chalk`\n {bgRed.bold ERROR } ${error.message}\n`); + return { + success: false, + }; + } +} diff --git a/packages/nx-python/src/executors/add/schema.d.ts b/packages/nx-python/src/executors/add/schema.d.ts new file mode 100644 index 0000000..3d23aab --- /dev/null +++ b/packages/nx-python/src/executors/add/schema.d.ts @@ -0,0 +1,5 @@ +export interface AddExecutorSchema { + name: string; + local: boolean; + args?: string; +} diff --git a/packages/nx-python/src/executors/add/schema.json b/packages/nx-python/src/executors/add/schema.json new file mode 100644 index 0000000..a634aa7 --- /dev/null +++ b/packages/nx-python/src/executors/add/schema.json @@ -0,0 +1,28 @@ +{ + "$schema": "http://json-schema.org/schema", + "cli": "nx", + "title": "Add executor", + "description": "", + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Package name", + "$default": { + "$source": "argv", + "index": 0 + }, + "x-prompt": "What is the Dependency name?" + }, + "args": { + "type": "string", + "description": "Install additional arguments" + }, + "local": { + "type": "boolean", + "description": "Local dependency", + "default": false + } + }, + "required": ["name"] +} diff --git a/packages/nx-python/src/executors/build/executor.spec.ts b/packages/nx-python/src/executors/build/executor.spec.ts new file mode 100644 index 0000000..c963398 --- /dev/null +++ b/packages/nx-python/src/executors/build/executor.spec.ts @@ -0,0 +1,463 @@ +import { BuildExecutorSchema } from './schema'; +import { execSyncMock } from '../../utils/mocks/child_process.mock'; +import { uuidMock } from '../../utils/mocks/uuid.mock'; +import executor from './executor'; +import fsMock from 'mock-fs'; +import { existsSync, readFileSync, mkdirsSync, writeFileSync } from 'fs-extra'; +import { parse } from '@iarna/toml'; +import { join } from 'path'; +import { tmpdir } from 'os'; +import chalk from "chalk"; + +describe('Build Executor', () => { + + let buildPath = null + + beforeAll(() => { + console.log(chalk`init chalk`) + }) + + beforeEach(() => { + uuidMock.mockReturnValue('abc') + buildPath = join(tmpdir(), 'nx-python', 'build', 'abc') + + execSyncMock.mockImplementation((command, opts) => { + mkdirsSync(join(opts.cwd, 'dist')); + writeFileSync(join(opts.cwd, 'dist', 'app.fake'), 'fake data'); + }); + }) + + afterEach(() => { + fsMock.restore(); + jest.resetAllMocks(); + }); + + it('should build python project with dependencies and keep the build folder', async () => { + fsMock({ + 'apps/app/.venv/pyvenv.cfg': 'fake', + 'apps/app/app/index.py': 'print("Hello from app")', + 'apps/app/pyproject.toml': `[tool.poetry] +name = "app" +version = "1.0.0" + [[tool.poetry.packages]] + include = "app" + + [tool.poetry.dependencies] + python = "^3.8" + dep1 = { path = "../../libs/dep1" }`, + 'apps/app/poetry.lock': `[[package]] +name = "click" +version = "7.1.2" +description = "Composable command line interface toolkit" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.source] +type = "legacy" + +[[package]] +name = "numpy" +version = "1.21.0" +description = "NumPy is the fundamental package for array computing with Python." +category = "main" +optional = false +python-versions = ">=3.7" + +[package.source] +type = "legacy" + +[[package]] +name = "python-dateutil" +version = "2.8.1" +description = "Extensions to the standard Python datetime module" +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" + +[package.source] +type = "legacy" + +[[package]] +name = "dep1" +version = "1.0.0" +description = "Dep 1" +category = "main" +optional = false + python-versions = "^3.8" +develop = false + +[package.dependencies] +numpy = "1.21.0" +pandas = "1.2.5" + +[package.source] +type = "directory" +url = "../../libs/dep1"`, + + 'libs/dep1/dep1/index.py': 'print("Hello from dep1")', + 'libs/dep1/pyproject.toml': `[tool.poetry] +name = "dep1" +version = "1.0.0" + [[tool.poetry.packages]] + include = "dep1" + + [tool.poetry.dependencies] + python = "^3.8"`, + + 'libs/dep2/dep2/index.py': 'print("Hello from dep2")', + 'libs/dep2/pyproject.toml': `[tool.poetry] +name = "dep2" +version = "1.0.0" + [[tool.poetry.packages]] + include = "dep2" + + [tool.poetry.dependencies] + python = "^3.8"`, + }); + + const options: BuildExecutorSchema = { + ignorePaths: ['.venv', '.tox', 'tests/'], + silent: false, + outputPath: 'dist/apps/app', + keepBuildFolder: true, + }; + + const output = await executor(options, { + cwd: '', + root: '', + isVerbose: false, + projectName: 'app', + workspace: { + version: 2, + npmScope: '@lucasvieira', + projects: { + app: { + root: 'apps/app', + targets: {}, + }, + dep1: { + root: 'libs/dep1', + targets: {}, + }, + dep2: { + root: 'libs/dep2', + targets: {}, + }, + }, + }, + }); + + expect(existsSync(buildPath)).toBeTruthy(); + expect(existsSync(`${buildPath}/app`)).toBeTruthy(); + expect(existsSync(`${buildPath}/dep1`)).toBeTruthy(); + expect(existsSync(`${buildPath}/dist/app.fake`)).toBeTruthy(); + expect(execSyncMock).toHaveBeenCalledWith('poetry build', { + cwd: buildPath, + stdio: 'inherit' + }); + + const projectTomlData = parse( + readFileSync(`${buildPath}/pyproject.toml`).toString('utf-8') + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ) as any; + + expect(projectTomlData.tool.poetry.packages).toStrictEqual([ + { + include: 'app', + }, + { + include: 'dep1', + }, + ]); + + expect(projectTomlData.tool.poetry.dependencies).toStrictEqual({ + python: '^3.8', + click: '7.1.2', + numpy: '1.21.0', + 'python-dateutil': '2.8.1', + }); + + expect(output.success).toBe(true); + }); + + it('should not build python project when the poetry.lock not exist', async () => { + fsMock({ + 'apps/app/app/index.py': 'print("Hello from app")', + 'apps/app/pyproject.toml': `[tool.poetry] +name = "app" +version = "1.0.0" + [[tool.poetry.packages]] + include = "app"`, + }); + + const options: BuildExecutorSchema = { + ignorePaths: ['.venv', '.tox', 'tests/'], + silent: false, + outputPath: 'dist/apps/app', + keepBuildFolder: false, + }; + + const result = await executor(options, { + cwd: '', + root: '', + isVerbose: false, + projectName: 'app', + workspace: { + version: 2, + npmScope: '@lucasvieira', + projects: { + app: { + root: 'apps/app', + targets: {}, + } + }, + }, + }) + + expect(result.success).toBe(false); + expect(execSyncMock).not.toHaveBeenCalled(); + expect(existsSync('dist/apps/app/app.fake')).not.toBeTruthy(); + expect(execSyncMock).not.toHaveBeenCalled() + }); + + it('should build python project and delete the build folder', async () => { + fsMock({ + 'apps/app/app/index.py': 'print("Hello from app")', + 'apps/app/pyproject.toml': `[tool.poetry] +name = "app" +version = "1.0.0" + [[tool.poetry.packages]] + include = "app" + + [tool.poetry.dependencies] + python = "^3.8" + click = "click"`, + 'apps/app/poetry.lock': `[[package]] +name = "click" +version = "7.1.2" +description = "Composable command line interface toolkit" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.source] +type = "legacy"`, + }); + + const options: BuildExecutorSchema = { + ignorePaths: ['.venv', '.tox', 'tests/'], + silent: false, + outputPath: 'dist/apps/app', + keepBuildFolder: false, + }; + + const output = await executor(options, { + cwd: '', + root: '', + isVerbose: false, + projectName: 'app', + workspace: { + version: 2, + npmScope: '@lucasvieira', + projects: { + app: { + root: 'apps/app', + targets: {}, + } + }, + }, + }); + + expect(existsSync(buildPath)).not.toBeTruthy(); + expect(existsSync('dist/apps/app/app.fake')).toBeTruthy(); + expect(execSyncMock).toHaveBeenCalledWith('poetry build', { + cwd: buildPath, + stdio: 'inherit' + }); + expect(output.success).toBe(true); + }); + + it('should build python project without python dependencies', async () => { + fsMock({ + 'apps/app/app/index.py': 'print("Hello from app")', + 'apps/app/pyproject.toml': `[tool.poetry] +name = "app" +version = "1.0.0" + [[tool.poetry.packages]] + include = "app" + + [tool.poetry.dependencies] + click = "click"`, + 'apps/app/poetry.lock': `[[package]] +name = "click" +version = "7.1.2" +description = "Composable command line interface toolkit" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.source] +type = "legacy"`, + }); + + const options: BuildExecutorSchema = { + ignorePaths: ['.venv', '.tox', 'tests/'], + silent: false, + outputPath: 'dist/apps/app', + keepBuildFolder: false, + }; + + const output = await executor(options, { + cwd: '', + root: '', + isVerbose: false, + projectName: 'app', + workspace: { + version: 2, + npmScope: '@lucasvieira', + projects: { + app: { + root: 'apps/app', + targets: {}, + } + }, + }, + }); + + expect(existsSync('dist/apps/app/app.fake')).toBeTruthy(); + expect(execSyncMock).toHaveBeenCalledWith('poetry build', { + cwd: buildPath, + stdio: 'inherit' + }); + expect(output.success).toBe(true); + }); + + it('should build python project with poetry.lock has dev dependencies', async () => { + fsMock({ + 'apps/app/app/index.py': 'print("Hello from app")', + 'apps/app/pyproject.toml': `[tool.poetry] +name = "app" +version = "1.0.0" + [[tool.poetry.packages]] + include = "app" + + [tool.poetry.dependencies] + python = "^3.8" + click = "click"`, + 'apps/app/poetry.lock': `[[package]] +name = "click" +version = "7.1.2" +description = "Composable command line interface toolkit" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.source] +type = "legacy" + +[[package]] +name = "mccabe" +version = "0.6.1" +description = "McCabe checker, plugin for flake8" +category = "dev" +optional = false +python-versions = "*" + +[package.source] +type = "legacy"`, + }); + + const options: BuildExecutorSchema = { + ignorePaths: ['.venv', '.tox', 'tests/'], + silent: false, + outputPath: 'dist/apps/app', + keepBuildFolder: false, + }; + + const output = await executor(options, { + cwd: '', + root: '', + isVerbose: false, + projectName: 'app', + workspace: { + version: 2, + npmScope: '@lucasvieira', + projects: { + app: { + root: 'apps/app', + targets: {}, + } + }, + }, + }); + + expect(existsSync('dist/apps/app/app.fake')).toBeTruthy(); + expect(execSyncMock).toHaveBeenCalledWith('poetry build', { + cwd: buildPath, + stdio: 'inherit' + }); + expect(output.success).toBe(true); + }); + + it('should build python project and does not print any log', async () => { + fsMock({ + 'apps/app/app/index.py': 'print("Hello from app")', + 'apps/app/pyproject.toml': `[tool.poetry] +name = "app" +version = "1.0.0" + [[tool.poetry.packages]] + include = "app" + + [tool.poetry.dependencies] + python = "^3.8" + click = "click"`, + 'apps/app/poetry.lock': `[[package]] +name = "click" +version = "7.1.2" +description = "Composable command line interface toolkit" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.source] +type = "legacy"`, + }); + + const options: BuildExecutorSchema = { + ignorePaths: ['.venv', '.tox', 'tests/'], + silent: true, + outputPath: 'dist/apps/app', + keepBuildFolder: false, + }; + + const logInfoMock = jest.fn() + + console.info = logInfoMock; + + const output = await executor(options, { + cwd: '', + root: '', + isVerbose: false, + projectName: 'app', + workspace: { + version: 2, + npmScope: '@lucasvieira', + projects: { + app: { + root: 'apps/app', + targets: {}, + } + }, + }, + }); + + expect(existsSync('dist/apps/app/app.fake')).toBeTruthy(); + expect(execSyncMock).toHaveBeenCalledWith('poetry build', { + cwd: buildPath, + stdio: 'inherit' + }); + expect(logInfoMock).not.toHaveBeenCalled() + expect(output.success).toBe(true); + }); +}); diff --git a/packages/nx-python/src/executors/build/executor.ts b/packages/nx-python/src/executors/build/executor.ts new file mode 100644 index 0000000..a9fd352 --- /dev/null +++ b/packages/nx-python/src/executors/build/executor.ts @@ -0,0 +1,205 @@ +import { ExecutorContext } from '@nrwl/devkit'; +import { BuildExecutorSchema } from './schema'; +import { + readdirSync, + existsSync, + rmdirSync, + copySync, + readFileSync, + writeFileSync, + mkdirsSync, +} from 'fs-extra'; +import { join } from 'path'; +import { getDependencies } from '../../graph/dependency-graph'; +import { parse, stringify } from '@iarna/toml'; +import { execSync } from 'child_process'; +import { tmpdir } from 'os' +import { v4 as uuid } from 'uuid' +import chalk from 'chalk' +import { Logger } from '../utils/logger'; + +const logger = new Logger() + +export default async function run( + options: BuildExecutorSchema, + context: ExecutorContext +) { + logger.setOptions(options) + try { + logger.info(chalk`\n {bold Building project {bgBlue ${context.projectName} }...}\n`); + + const { root } = context.workspace.projects[context.projectName]; + + const buildFolderPath = join(tmpdir(), 'nx-python', 'build', uuid()); + + mkdirsSync(buildFolderPath); + + logger.info(chalk` Copying project files to a temporary folder`) + readdirSync(root).forEach((file) => { + if (!options.ignorePaths.includes(file)) { + const source = join(root, file); + const target = join(buildFolderPath, file); + copySync(source, target, { + recursive: true, + }); + } + }); + + const buildPyProjectToml = join(buildFolderPath, 'pyproject.toml'); + + const buildTomlData = parse( + readFileSync(buildPyProjectToml).toString('utf-8') + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ) as any; + + logger.info(chalk` Resolving {blue.bold poetry.lock} dependencies...`) + resolveLockDependencies(root, options, buildTomlData); + + logger.info(chalk` Resolving workspace dependencies...`) + const deps = getDependencies(context.projectName, context.workspace); + + buildDependencies( + options, + context, + deps, + buildFolderPath, + buildPyProjectToml, + buildTomlData + ); + + writeFileSync(buildPyProjectToml, stringify(buildTomlData)); + + const distFolder = join(buildFolderPath, 'dist'); + + rmdirSync(distFolder, { recursive: true }); + + logger.info(chalk` Generating sdist and wheel artifacts`) + const command = 'poetry build' + logger.info(chalk` Running ${command}`) + execSync(command, { + cwd: buildFolderPath, + stdio: 'inherit' + }); + + rmdirSync(options.outputPath, { recursive: true }); + mkdirsSync(options.outputPath); + logger.info(chalk` Artifacts generated at {bold ${options.outputPath}} folder`) + copySync(distFolder, options.outputPath); + + if (!options.keepBuildFolder) { + rmdirSync(buildFolderPath, { recursive: true }); + } + + return { + success: true, + }; + } catch (error) { + logger.info(chalk`\n {bgRed.bold ERROR } ${error.message}\n`) + return { + success: false, + }; + } +} + +function resolveLockDependencies( + root: string, + options: BuildExecutorSchema, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + buildTomlData: any +) { + const poetryLockPath = join(root, 'poetry.lock'); + + if (!existsSync(poetryLockPath)) { + throw new Error(chalk`File {bold ${poetryLockPath}} not found`); + } + + const lockData = parse( + readFileSync(poetryLockPath).toString('utf-8') + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ) as any; + + const pythonDependency = buildTomlData.tool.poetry.dependencies.python; + + buildTomlData.tool.poetry.dependencies = {}; + + if (pythonDependency) { + buildTomlData.tool.poetry.dependencies['python'] = pythonDependency; + } + + for (const pkg of lockData.package) { + if (pkg.category === 'main') { + logger.info(chalk` â€ĸ Adding {blue.bold ${pkg.name}} dependency`) + + if (!pkg.source || (pkg.source && pkg.source.type !== 'directory')) { + buildTomlData.tool.poetry.dependencies[pkg.name] = pkg.version; + } + } + } +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function buildDependencies( + options: BuildExecutorSchema, + context: ExecutorContext, + deps: string[], + buildFolderPath: string, + buildPyProjectToml: string, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + buildTomlData: any, + level = 1 +) { + for (const dep of deps) { + const tab = " ".repeat(level) + logger.info(chalk`${tab}â€ĸ Adding {blue.bold ${dep}} dependency`) + const project = context.workspace.projects[dep]; + const pyprojectToml = join(project.root, 'pyproject.toml'); + + const tomlData = parse( + readFileSync(pyprojectToml).toString('utf-8') + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ) as any; + + includeDependencyPackage( + tomlData, + project, + buildFolderPath, + options, + buildPyProjectToml, + buildTomlData + ); + + const dependencyRepoDependencies = getDependencies(dep, context.workspace); + + buildDependencies( + options, + context, + dependencyRepoDependencies, + buildFolderPath, + buildPyProjectToml, + buildTomlData, + level+1 + ); + } +} + +function includeDependencyPackage( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + tomlData: any, + project, + buildFolderPath: string, + options: BuildExecutorSchema, + buildPyProjectToml: string, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + buildTomlData: any +) { + for (const pkg of tomlData.tool.poetry.packages) { + const pkgFolder = join(project.root, pkg.include); + const buildPackageFolder = join(buildFolderPath, pkg.include); + + copySync(pkgFolder, buildPackageFolder, { recursive: true }); + + buildTomlData.tool.poetry.packages.push({ + include: pkg.include, + }); + } +} diff --git a/packages/nx-python/src/executors/build/schema.d.ts b/packages/nx-python/src/executors/build/schema.d.ts new file mode 100644 index 0000000..b56d4ff --- /dev/null +++ b/packages/nx-python/src/executors/build/schema.d.ts @@ -0,0 +1,6 @@ +export interface BuildExecutorSchema { + silent: boolean; + ignorePaths: string[] + outputPath: string; + keepBuildFolder: boolean; +} diff --git a/packages/nx-python/src/executors/build/schema.json b/packages/nx-python/src/executors/build/schema.json new file mode 100644 index 0000000..b51309f --- /dev/null +++ b/packages/nx-python/src/executors/build/schema.json @@ -0,0 +1,32 @@ +{ + "$schema": "http://json-schema.org/schema", + "cli": "nx", + "title": "Build executor", + "description": "", + "type": "object", + "properties": { + "silent": { + "type": "boolean", + "description": "Hide output text.", + "default": false + }, + "outputPath": { + "type": "string", + "description": "Output path for the python tar/whl files" + }, + "keepBuildFolder": { + "type": "boolean", + "description": "Keep build folder", + "default": false + }, + "ignorePaths": { + "type": "array", + "description": "Ignore folder/files on build process", + "default": [".venv", ".tox", "tests"], + "items": { + "type": "string" + } + } + }, + "required": ["outputPath"] +} diff --git a/packages/nx-python/src/executors/flake8/executor.spec.ts b/packages/nx-python/src/executors/flake8/executor.spec.ts new file mode 100644 index 0000000..cd38e15 --- /dev/null +++ b/packages/nx-python/src/executors/flake8/executor.spec.ts @@ -0,0 +1,114 @@ +import chalk from 'chalk'; +import { execSyncMock } from '../../utils/mocks/child_process.mock'; +import executor from './executor'; +import { tmpdir } from 'os'; +import { join } from 'path'; +import { v4 as uuid } from "uuid"; +import { + mkdirsSync +} from 'fs-extra'; + +describe('Flake8 Executor', () => { + + let tmppath = null + + beforeEach(() => { + tmppath = join(tmpdir(), 'nx-python', 'flake8', uuid()) + }) + + beforeAll(() => { + console.log(chalk`init chalk`); + }); + + afterEach(() => { + jest.resetAllMocks(); + }); + + it('should execute flake8 linting', async () => { + const output = await executor( + { + outputFile: join(tmppath, 'reports/apps/app/pylint.txt'), + silent: false, + }, + { + cwd: '', + root: '', + isVerbose: false, + projectName: 'app', + workspace: { + version: 2, + npmScope: '@lucasvieira', + projects: { + app: { + root: 'apps/app', + targets: {}, + }, + }, + }, + } + ); + expect(execSyncMock).toHaveBeenCalledTimes(1); + expect(output.success).toBe(true); + }); + + + it('should execute flake8 linting when the reports folder already exists', async () => { + mkdirsSync(join(tmppath, 'reports/apps/app')) + + const output = await executor( + { + outputFile: join(tmppath, 'reports/apps/app/pylint.txt'), + silent: false, + }, + { + cwd: '', + root: '', + isVerbose: false, + projectName: 'app', + workspace: { + version: 2, + npmScope: '@lucasvieira', + projects: { + app: { + root: 'apps/app', + targets: {}, + }, + }, + }, + } + ); + expect(execSyncMock).toHaveBeenCalledTimes(1); + expect(output.success).toBe(true); + }); + + it('should returns a error when run the flake8 CLI', async () => { + execSyncMock.mockImplementation(() => { + throw new Error('Some error') + }) + + const output = await executor( + { + outputFile: join(tmppath, 'reports/apps/app/pylint.txt'), + silent: false, + }, + { + cwd: '', + root: '', + isVerbose: false, + projectName: 'app', + workspace: { + version: 2, + npmScope: '@lucasvieira', + projects: { + app: { + root: 'apps/app', + targets: {}, + }, + }, + }, + } + ); + expect(execSyncMock).toHaveBeenCalledTimes(1); + expect(output.success).toBe(false); + }); +}); diff --git a/packages/nx-python/src/executors/flake8/executor.ts b/packages/nx-python/src/executors/flake8/executor.ts new file mode 100644 index 0000000..03cde4d --- /dev/null +++ b/packages/nx-python/src/executors/flake8/executor.ts @@ -0,0 +1,52 @@ +import { ExecutorContext } from '@nrwl/devkit'; +import chalk from 'chalk'; +import { execSync } from 'child_process'; +import { Logger } from '../utils/logger'; +import { Flake8ExecutorSchema } from './schema'; +import path from 'path' +import { + mkdirsSync, + existsSync +} from 'fs-extra'; + +const logger = new Logger(); + +export default async function runExecutor( + options: Flake8ExecutorSchema, + context: ExecutorContext +) { + logger.setOptions(options); + try { + logger.info( + chalk`\n {bold Running flake8 linting on project {bgBlue ${context.projectName} }...}\n` + ); + + const projectConfig = context.workspace.projects[context.projectName]; + const cwd = projectConfig.root + + const absPath = path.resolve(options.outputFile) + const reportFolder = path.dirname(absPath) + if (!existsSync(reportFolder)) { + mkdirsSync(reportFolder) + } + + const lintingCommand = `poetry run flake8 --output-file ${absPath}`; + execSync(lintingCommand, { + cwd: cwd, + stdio: 'inherit' + }); + + console.log( + chalk`\n {green All files pass linting.}\n` + ); + + return { + success: true, + }; + } catch (error) { + logger.info(chalk`\n {bgRed.bold ERROR } ${error.message}\n`); + return { + success: false, + }; + } +} diff --git a/packages/nx-python/src/executors/flake8/schema.d.ts b/packages/nx-python/src/executors/flake8/schema.d.ts new file mode 100644 index 0000000..c157687 --- /dev/null +++ b/packages/nx-python/src/executors/flake8/schema.d.ts @@ -0,0 +1,4 @@ +export interface Flake8ExecutorSchema { + outputFile: string + silent: boolean +} diff --git a/packages/nx-python/src/executors/flake8/schema.json b/packages/nx-python/src/executors/flake8/schema.json new file mode 100644 index 0000000..6013049 --- /dev/null +++ b/packages/nx-python/src/executors/flake8/schema.json @@ -0,0 +1,19 @@ +{ + "$schema": "http://json-schema.org/schema", + "cli": "nx", + "title": "Flake8 executor", + "description": "", + "type": "object", + "properties": { + "outputFile": { + "type": "string", + "description": "Output pylint file path" + }, + "silent": { + "type": "boolean", + "description": "Hide output text.", + "default": false + } + }, + "required": ["outputFile"] +} diff --git a/packages/nx-python/src/executors/install/executor.spec.ts b/packages/nx-python/src/executors/install/executor.spec.ts new file mode 100644 index 0000000..790ddb2 --- /dev/null +++ b/packages/nx-python/src/executors/install/executor.spec.ts @@ -0,0 +1,129 @@ +import { execSyncMock } from '../../utils/mocks/child_process.mock'; +import executor from './executor'; +import path from 'path' + +describe('Install Executor', () => { + const context = { + cwd: '', + root: '', + isVerbose: false, + projectName: 'app', + workspace: { + version: 2, + npmScope: '@lucasvieira', + projects: { + app: { + root: 'apps/app', + targets: {}, + }, + }, + }, + }; + + it('should install the poetry dependencies using default values', async () => { + const options = { + silent: false, + debug: false, + verbose: false + } + + const output = await executor(options, context); + expect(execSyncMock).toHaveBeenCalledWith( + 'poetry install -v ', { + stdio: 'inherit', + cwd: 'apps/app' + }) + expect(output.success).toBe(true); + }); + + it('should install the poetry dependencies with args', async () => { + const options = { + silent: false, + debug: false, + verbose: false, + args: '--no-dev' + } + + const output = await executor(options, context); + expect(execSyncMock).toHaveBeenCalledWith( + 'poetry install -v --no-dev', { + stdio: 'inherit', + cwd: 'apps/app' + }) + expect(output.success).toBe(true); + }); + + it('should install the poetry dependencies with verbose flag', async () => { + const options = { + silent: false, + debug: false, + verbose: true + } + + const output = await executor(options, context); + expect(execSyncMock).toHaveBeenCalledWith( + 'poetry install -vv ', { + stdio: 'inherit', + cwd: 'apps/app' + }) + expect(output.success).toBe(true); + }); + + it('should install the poetry dependencies with debug flag', async () => { + const options = { + silent: false, + debug: true, + verbose: false + } + + const output = await executor(options, context); + expect(execSyncMock).toHaveBeenCalledWith( + 'poetry install -vv ', { + stdio: 'inherit', + cwd: 'apps/app' + }) + expect(output.success).toBe(true); + }); + + it('should install the poetry dependencies with custom cache dir', async () => { + const options = { + silent: false, + debug: false, + verbose: false, + cacheDir: 'apps/app/.cache/pypoetry' + } + + const output = await executor(options, context); + expect(execSyncMock).toHaveBeenCalledWith( + 'poetry install -v ', { + stdio: 'inherit', + cwd: 'apps/app', + env: { + ...process.env, + POETRY_CACHE_DIR: path.resolve('apps/app/.cache/pypoetry') + } + }) + expect(output.success).toBe(true); + }); + + it('should not install when the command fail', async () => { + execSyncMock.mockImplementation(() => { + throw new Error('fake') + }) + + const options = { + silent: false, + debug: false, + verbose: false, + cacheDir: 'apps/app/.cache/pypoetry' + } + + const output = await executor(options, context); + expect(execSyncMock).toHaveBeenCalledWith( + 'poetry install -v ', { + stdio: 'inherit', + cwd: 'apps/app' + }) + expect(output.success).toBe(false); + }); +}); diff --git a/packages/nx-python/src/executors/install/executor.ts b/packages/nx-python/src/executors/install/executor.ts new file mode 100644 index 0000000..e418826 --- /dev/null +++ b/packages/nx-python/src/executors/install/executor.ts @@ -0,0 +1,50 @@ +import { InstallExecutorSchema } from './schema'; +import { Logger } from '../utils/logger'; +import { ExecutorContext } from '@nrwl/devkit'; +import chalk from 'chalk'; +import { execSync, ExecSyncOptions } from 'child_process'; +import path from 'path' + +const logger = new Logger(); + +export default async function runExecutor( + options: InstallExecutorSchema, + context: ExecutorContext +) { + logger.setOptions(options); + try { + const projectConfig = context.workspace.projects[context.projectName]; + let verboseArg = '-v' + + if (options.debug) { + verboseArg = '-vvv' + } else if (options.verbose) { + verboseArg = '-vv' + } + + const command = `poetry install ${verboseArg} ${options.args ?? ""}` + logger.info(chalk`\n Running Command: {bold ${command}}\n`); + const execOpts: ExecSyncOptions = { + stdio: 'inherit', + cwd: projectConfig.root + } + + if (options.cacheDir) { + execOpts.env = { + ...process.env, + POETRY_CACHE_DIR: path.resolve(options.cacheDir) + } + } + + execSync(command, execOpts) + + return { + success: true, + }; + } catch (error) { + logger.info(chalk`\n {bgRed.bold ERROR } ${error.message}\n`); + return { + success: false, + }; + } +} diff --git a/packages/nx-python/src/executors/install/schema.d.ts b/packages/nx-python/src/executors/install/schema.d.ts new file mode 100644 index 0000000..ac8aa50 --- /dev/null +++ b/packages/nx-python/src/executors/install/schema.d.ts @@ -0,0 +1,7 @@ +export interface InstallExecutorSchema { + silent: boolean; + args?: string; + cacheDir?: string; + verbose: boolean; + debug: boolean; +} diff --git a/packages/nx-python/src/executors/install/schema.json b/packages/nx-python/src/executors/install/schema.json new file mode 100644 index 0000000..d845776 --- /dev/null +++ b/packages/nx-python/src/executors/install/schema.json @@ -0,0 +1,33 @@ +{ + "$schema": "http://json-schema.org/schema", + "cli": "nx", + "title": "Install executor", + "description": "", + "type": "object", + "properties": { + "silent": { + "type": "boolean", + "description": "Hide output text.", + "default": false + }, + "args": { + "type": "string", + "description": "Install custom args" + }, + "cacheDir": { + "type": "string", + "description": "Poetry Install Custom Cache Directory" + }, + "verbose": { + "type": "boolean", + "description": "Verbose level '-vv'", + "default": false + }, + "debug": { + "type": "boolean", + "description": "Verbose debug level '-vvv'", + "default": false + } + }, + "required": [] +} diff --git a/packages/nx-python/src/executors/remove/executor.spec.ts b/packages/nx-python/src/executors/remove/executor.spec.ts new file mode 100644 index 0000000..0a1c7b6 --- /dev/null +++ b/packages/nx-python/src/executors/remove/executor.spec.ts @@ -0,0 +1,322 @@ +import { execSyncMock } from '../../utils/mocks/child_process.mock'; +import chalk from 'chalk'; +import executor from './executor'; +import fsMock from 'mock-fs'; + +describe('Delete Executor', () => { + beforeAll(() => { + console.log(chalk`init chalk`); + }); + + afterEach(() => { + fsMock.restore(); + jest.resetAllMocks(); + }); + + it('should remove local dependency and update all the dependency tree', async () => { + fsMock({ + 'apps/app/pyproject.toml': `[tool.poetry] +name = "app" +version = "1.0.0" + [[tool.poetry.packages]] + include = "app" + + [tool.poetry.dependencies] + python = "^3.8" + click = "click" + lib1 = { path = "../../libs/lib1" } +`, + + 'apps/app1/pyproject.toml': `[tool.poetry] +name = "app1" +version = "1.0.0" + [[tool.poetry.packages]] + include = "app" + + [tool.poetry.dependencies] + python = "^3.8" + click = "click" + lib1 = { path = "../../libs/lib1" } +`, + + 'libs/lib1/pyproject.toml': `[tool.poetry] + name = "lib1" + version = "1.0.0" + [[tool.poetry.packages]] + include = "app" + + [tool.poetry.dependencies] + python = "^3.8" + shared1 = { path = "../../libs/shared1" }`, + + 'libs/shared1/pyproject.toml': `[tool.poetry] + name = "shared1" + version = "1.0.0" + [[tool.poetry.packages]] + include = "app" + + [tool.poetry.dependencies] + python = "^3.8"`, + }); + + const options = { + name: 'shared1', + local: true, + }; + + const context = { + cwd: '', + root: '', + isVerbose: false, + projectName: 'lib1', + workspace: { + version: 2, + npmScope: '@lucasvieira', + projects: { + app: { + root: 'apps/app', + targets: {}, + }, + app1: { + root: 'apps/app1', + targets: {}, + }, + app3: { + root: 'apps/app3', + targets: {}, + }, + lib1: { + root: 'libs/lib1', + targets: {}, + }, + shared1: { + root: 'libs/shared1', + targets: {}, + }, + }, + }, + }; + + const output = await executor(options, context); + expect(execSyncMock).toHaveBeenCalledTimes(3); + expect(execSyncMock).toHaveBeenNthCalledWith(1, 'poetry remove shared1 ', { + cwd: 'libs/lib1', + stdio: 'inherit', + }); + expect(execSyncMock).toHaveBeenNthCalledWith(2, 'poetry update lib1', { + cwd: 'apps/app', + stdio: 'inherit', + }); + expect(execSyncMock).toHaveBeenNthCalledWith(3, 'poetry update lib1', { + cwd: 'apps/app1', + stdio: 'inherit', + }); + expect(output.success).toBe(true); + }); + + it('should remove the external dependency and update all the dependency tree', async () => { + fsMock({ + 'apps/app/pyproject.toml': `[tool.poetry] +name = "app" +version = "1.0.0" + [[tool.poetry.packages]] + include = "app" + + [tool.poetry.dependencies] + python = "^3.8" + click = "click" + lib1 = { path = "../../libs/lib1" } +`, + + 'apps/app1/pyproject.toml': `[tool.poetry] +name = "app1" +version = "1.0.0" + [[tool.poetry.packages]] + include = "app" + + [tool.poetry.dependencies] + python = "^3.8" + click = "click" + lib1 = { path = "../../libs/lib1" } +`, + + 'libs/lib1/pyproject.toml': `[tool.poetry] + name = "lib1" + version = "1.0.0" + [[tool.poetry.packages]] + include = "app" + + [tool.poetry.dependencies] + python = "^3.8" + shared1 = { path = "../../libs/shared1" }`, + + 'libs/shared1/pyproject.toml': `[tool.poetry] + name = "lib1" + version = "1.0.0" + [[tool.poetry.packages]] + include = "app" + + [tool.poetry.dependencies] + python = "^3.8"`, + }); + + const options = { + name: 'numpy', + local: false, + }; + + const context = { + cwd: '', + root: '', + isVerbose: false, + projectName: 'shared1', + workspace: { + version: 2, + npmScope: '@lucasvieira', + projects: { + app: { + root: 'apps/app', + targets: {}, + }, + app1: { + root: 'apps/app1', + targets: {}, + }, + app3: { + root: 'apps/app3', + targets: {}, + }, + lib1: { + root: 'libs/lib1', + targets: {}, + }, + shared1: { + root: 'libs/shared1', + targets: {}, + }, + }, + }, + }; + + const output = await executor(options, context); + expect(execSyncMock).toHaveBeenCalledTimes(4); + expect(execSyncMock).toHaveBeenNthCalledWith(1, 'poetry remove numpy ', { + cwd: 'libs/shared1', + stdio: 'inherit', + }); + expect(execSyncMock).toHaveBeenNthCalledWith(2, 'poetry update lib1', { + cwd: 'libs/lib1', + stdio: 'inherit', + }); + expect(execSyncMock).toHaveBeenNthCalledWith(3, 'poetry update lib1', { + cwd: 'apps/app', + stdio: 'inherit', + }); + expect(execSyncMock).toHaveBeenNthCalledWith(4, 'poetry update lib1', { + cwd: 'apps/app1', + stdio: 'inherit', + }); + expect(output.success).toBe(true); + }); + + it('should remove external dependency with args', async () => { + fsMock({ + 'apps/app/pyproject.toml': `[tool.poetry] +name = "app" +version = "1.0.0" + [[tool.poetry.packages]] + include = "app" + + [tool.poetry.dependencies] + python = "^3.8" + click = "1.8" +`, + }); + const options = { + name: 'click', + local: false, + args: '-vvv', + }; + + const context = { + cwd: '', + root: '', + isVerbose: false, + projectName: 'app', + workspace: { + version: 2, + npmScope: '@lucasvieira', + projects: { + app: { + root: 'apps/app', + targets: {}, + }, + }, + }, + }; + + const output = await executor(options, context); + expect(execSyncMock).toHaveBeenCalledTimes(1); + expect(execSyncMock).toHaveBeenNthCalledWith( + 1, + 'poetry remove click -vvv', + { + cwd: 'apps/app', + stdio: 'inherit', + } + ); + expect(output.success).toBe(true); + }); + + it('should remove external dependency with error', async () => { + execSyncMock.mockImplementation(() => { + throw new Error('fake error'); + }); + + fsMock({ + 'apps/app/pyproject.toml': `[tool.poetry] +name = "app" +version = "1.0.0" + [[tool.poetry.packages]] + include = "app" + + [tool.poetry.dependencies] + python = "^3.8" + click = "1.8" +`, + }); + const options = { + name: 'click', + local: false + }; + + const context = { + cwd: '', + root: '', + isVerbose: false, + projectName: 'app', + workspace: { + version: 2, + npmScope: '@lucasvieira', + projects: { + app: { + root: 'apps/app', + targets: {}, + }, + }, + }, + }; + + const output = await executor(options, context); + expect(execSyncMock).toHaveBeenCalledTimes(1); + expect(execSyncMock).toHaveBeenNthCalledWith( + 1, + 'poetry remove click ', + { + cwd: 'apps/app', + stdio: 'inherit', + } + ); + expect(output.success).toBe(false); + }); +}); diff --git a/packages/nx-python/src/executors/remove/executor.ts b/packages/nx-python/src/executors/remove/executor.ts new file mode 100644 index 0000000..c73bac2 --- /dev/null +++ b/packages/nx-python/src/executors/remove/executor.ts @@ -0,0 +1,59 @@ +import { ExecutorContext } from '@nrwl/devkit'; +import chalk from 'chalk'; +import { execSync } from 'child_process'; +import { updateDependencyTree } from '../../dependency/update-dependency'; +import { getLocalDependencyConfig } from '../update/executor'; +import { getProjectTomlPath, parseToml } from '../utils/poetry'; +import { RemoveExecutorSchema } from './schema'; + +export default async function runExecutor( + options: RemoveExecutorSchema, + context: ExecutorContext +) { + try { + const projectConfig = context.workspace.projects[context.projectName]; + console.log( + chalk`\n {bold Removing {bgBlue ${options.name} } dependency...}\n` + ); + + let dependencyName = options.name; + if (options.local) { + const dependencyConfig = getLocalDependencyConfig(context, options.name); + + const pyprojectTomlPath = getProjectTomlPath(dependencyConfig); + const { + tool: { + poetry: { name }, + }, + } = parseToml(pyprojectTomlPath); + + dependencyName = name; + } + + const removeCommand = `poetry remove ${dependencyName} ${ + options.args ?? '' + }`; + console.log( + chalk`{bold Running command}: ${removeCommand} at {bold ${projectConfig.root}} folder\n` + ); + execSync(removeCommand, { + cwd: projectConfig.root, + stdio: 'inherit', + }); + + updateDependencyTree(context); + + console.log( + chalk`\n {green.bold '${options.name}'} {green dependency has been successfully removed}\n` + ); + + return { + success: true, + }; + } catch (error) { + console.log(chalk`\n {bgRed.bold ERROR } ${error.message}\n`); + return { + success: false, + }; + } +} diff --git a/packages/nx-python/src/executors/remove/schema.d.ts b/packages/nx-python/src/executors/remove/schema.d.ts new file mode 100644 index 0000000..62c4cd2 --- /dev/null +++ b/packages/nx-python/src/executors/remove/schema.d.ts @@ -0,0 +1,5 @@ +export interface RemoveExecutorSchema { + name: string; + local: boolean; + args?: string; +} diff --git a/packages/nx-python/src/executors/remove/schema.json b/packages/nx-python/src/executors/remove/schema.json new file mode 100644 index 0000000..3967515 --- /dev/null +++ b/packages/nx-python/src/executors/remove/schema.json @@ -0,0 +1,24 @@ +{ + "$schema": "http://json-schema.org/schema", + "cli": "nx", + "title": "Remove executor", + "description": "", + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Package name", + "x-prompt": "What is the Dependency name?" + }, + "args": { + "type": "string", + "description": "Install additional arguments" + }, + "local": { + "type": "boolean", + "description": "Local dependency", + "default": false + } + }, + "required": ["name"] +} diff --git a/packages/nx-python/src/executors/tox/executor.spec.ts b/packages/nx-python/src/executors/tox/executor.spec.ts new file mode 100644 index 0000000..441f665 --- /dev/null +++ b/packages/nx-python/src/executors/tox/executor.spec.ts @@ -0,0 +1,168 @@ +import { execSyncMock } from '../../utils/mocks/child_process.mock'; + +const buildExecutorMock = jest.fn(); + +jest.mock('../build/executor', () => { + return buildExecutorMock; +}); + +import { ToxExecutorSchema } from './schema'; +import executor from './executor'; +import fsMock from 'mock-fs'; +import chalk from 'chalk'; + +const options: ToxExecutorSchema = { + silent: false, +}; + +describe('Tox Executor', () => { + const context = { + cwd: '', + root: '', + isVerbose: false, + projectName: 'app', + workspace: { + version: 2, + npmScope: '@lucasvieira', + projects: { + app: { + root: 'apps/app', + targets: {}, + }, + }, + }, + }; + + beforeAll(() => { + console.log(chalk`init chalk`); + }); + + afterEach(() => { + fsMock.restore(); + jest.resetAllMocks(); + }); + + it('should build and run tox successfully', async () => { + buildExecutorMock.mockResolvedValue({ + success: true + }) + + fsMock({ + 'apps/app/dist/package.tar.gz': 'fake', + }) + + const output = await executor(options, context); + + expect(buildExecutorMock).toBeCalledWith( + { + silent: options.silent, + keepBuildFolder: false, + ignorePaths: ['.venv', '.tox', 'tests'], + outputPath: 'apps/app/dist', + }, + context + ); + expect(execSyncMock).toBeCalledWith( + 'poetry run tox --installpkg dist/package.tar.gz ', { + cwd: 'apps/app', + stdio: 'inherit', + }); + expect(output.success).toBe(true); + }); + + it('should build and run tox successfully with args', async () => { + buildExecutorMock.mockResolvedValue({ + success: true + }) + + fsMock({ + 'apps/app/dist/package.tar.gz': 'fake', + }) + + const output = await executor({ + silent: false, + args: '-e linters' + }, context); + + expect(buildExecutorMock).toBeCalledWith( + { + silent: options.silent, + keepBuildFolder: false, + ignorePaths: ['.venv', '.tox', 'tests'], + outputPath: 'apps/app/dist', + }, + context + ); + expect(execSyncMock).toBeCalledWith( + 'poetry run tox --installpkg dist/package.tar.gz -e linters', { + cwd: 'apps/app', + stdio: 'inherit', + }); + expect(output.success).toBe(true); + }); + + it('should failure the build and not run tox command', async () => { + buildExecutorMock.mockResolvedValue({ + success: false + }) + + const output = await executor(options, context); + + expect(buildExecutorMock).toBeCalledWith( + { + silent: options.silent, + keepBuildFolder: false, + ignorePaths: ['.venv', '.tox', 'tests'], + outputPath: 'apps/app/dist', + }, + context + ); + expect(execSyncMock).not.toBeCalled(); + expect(output.success).toBe(false); + }); + + it('should dist folder not exists and not run tox command', async () => { + buildExecutorMock.mockResolvedValue({ + success: true + }) + + const output = await executor(options, context); + + expect(buildExecutorMock).toBeCalledWith( + { + silent: options.silent, + keepBuildFolder: false, + ignorePaths: ['.venv', '.tox', 'tests'], + outputPath: 'apps/app/dist', + }, + context + ); + expect(execSyncMock).not.toBeCalled(); + expect(output.success).toBe(false); + }); + + it('should not generate the tar.gz and not run tox command', async () => { + + fsMock({ + 'apps/app/dist/something.txt': 'fake', + }) + + buildExecutorMock.mockResolvedValue({ + success: true + }) + + const output = await executor(options, context); + + expect(buildExecutorMock).toBeCalledWith( + { + silent: options.silent, + keepBuildFolder: false, + ignorePaths: ['.venv', '.tox', 'tests'], + outputPath: 'apps/app/dist', + }, + context + ); + expect(execSyncMock).not.toBeCalled(); + expect(output.success).toBe(false); + }); +}); diff --git a/packages/nx-python/src/executors/tox/executor.ts b/packages/nx-python/src/executors/tox/executor.ts new file mode 100644 index 0000000..4bf3448 --- /dev/null +++ b/packages/nx-python/src/executors/tox/executor.ts @@ -0,0 +1,65 @@ +import { ExecutorContext } from '@nrwl/devkit'; +import { ToxExecutorSchema } from './schema'; +import buildExecutor from '../build/executor'; +import path from 'path'; +import chalk from 'chalk'; +import { Logger } from '../utils/logger'; +import { execSync } from 'child_process'; +import { readdirSync, existsSync } from 'fs-extra'; + +const logger = new Logger(); + +export default async function runExecutor( + options: ToxExecutorSchema, + context: ExecutorContext +) { + logger.setOptions(options); + try { + const projectConfig = context.workspace.projects[context.projectName]; + const distFolder = path.join(projectConfig.root, 'dist'); + + const buildResult = await buildExecutor( + { + silent: options.silent, + keepBuildFolder: false, + ignorePaths: ['.venv', '.tox', 'tests'], + outputPath: distFolder, + }, + context + ); + + if (!buildResult.success) { + return buildResult; + } + + if (!existsSync(distFolder)) { + throw new Error(chalk`Folder {blue.bold ${distFolder}} not found`) + } + + const packageFile = readdirSync(distFolder).find((file) => + file.endsWith('.tar.gz') + ); + + if (!packageFile) { + throw new Error(chalk`No package file {blue.bold *.tar.gz} found in the {bold ${distFolder}}`) + } + + const packagePath = path.relative(projectConfig.root, path.join(distFolder, packageFile)) + + const command = `poetry run tox --installpkg ${packagePath} ${options.args ?? ""}`; + logger.info(chalk`\n Running Command: {bold ${command}}\n`); + execSync(command, { + cwd: projectConfig.root, + stdio: 'inherit', + }); + + return { + success: true, + }; + } catch (error) { + logger.info(chalk`\n {bgRed.bold ERROR } ${error.message}\n`); + return { + success: false, + }; + } +} diff --git a/packages/nx-python/src/executors/tox/schema.d.ts b/packages/nx-python/src/executors/tox/schema.d.ts new file mode 100644 index 0000000..fb07d42 --- /dev/null +++ b/packages/nx-python/src/executors/tox/schema.d.ts @@ -0,0 +1,4 @@ +export interface ToxExecutorSchema { + silent: boolean; + args?: string; +} diff --git a/packages/nx-python/src/executors/tox/schema.json b/packages/nx-python/src/executors/tox/schema.json new file mode 100644 index 0000000..f0f7f43 --- /dev/null +++ b/packages/nx-python/src/executors/tox/schema.json @@ -0,0 +1,19 @@ +{ + "$schema": "http://json-schema.org/schema", + "cli": "nx", + "title": "Tox executor", + "description": "", + "type": "object", + "properties": { + "silent": { + "type": "boolean", + "description": "Hide output text.", + "default": false + }, + "args": { + "type": "string", + "description": "Tox custom args" + } + }, + "required": [] +} diff --git a/packages/nx-python/src/executors/update/executor.spec.ts b/packages/nx-python/src/executors/update/executor.spec.ts new file mode 100644 index 0000000..96f6115 --- /dev/null +++ b/packages/nx-python/src/executors/update/executor.spec.ts @@ -0,0 +1,467 @@ +import { execSyncMock } from '../../utils/mocks/child_process.mock'; +import executor from './executor'; +import fsMock from 'mock-fs'; +import chalk from 'chalk'; +import { parseToml } from '../utils/poetry'; + +describe('Update Executor', () => { + beforeAll(() => { + console.log(chalk`init chalk`); + }); + + afterEach(() => { + fsMock.restore(); + jest.resetAllMocks(); + }); + + it('run update target and should update the dependency to the project', async () => { + fsMock({ + 'apps/app/pyproject.toml': `[tool.poetry] +name = "app" +version = "1.0.0" + [[tool.poetry.packages]] + include = "app" + + [tool.poetry.dependencies] + python = "^3.8" + click = "click" +`, + }); + + const options = { + name: 'numpy', + local: false, + }; + + const context = { + cwd: '', + root: '', + isVerbose: false, + projectName: 'app', + workspace: { + version: 2, + npmScope: '@lucasvieira', + projects: { + app: { + root: 'apps/app', + targets: {}, + }, + }, + }, + }; + + const output = await executor(options, context); + expect(execSyncMock).toHaveBeenCalledWith('poetry update numpy ', { + cwd: 'apps/app', + stdio: 'inherit', + }); + expect(output.success).toBe(true); + }); + + it('run update target and should not update the dependency to the project because the project does not exist', async () => { + fsMock({ + 'apps/app/pyproject.toml': `[tool.poetry] +name = "app" +version = "1.0.0" + [[tool.poetry.packages]] + include = "app" + + [tool.poetry.dependencies] + python = "^3.8" + click = "click" +`, + }); + + const options = { + local: true, + name: 'lib1', + }; + + const context = { + cwd: '', + root: '', + isVerbose: false, + projectName: 'app', + workspace: { + version: 2, + npmScope: '@lucasvieira', + projects: { + app: { + root: 'apps/app', + targets: {}, + }, + }, + }, + }; + + const output = await executor(options, context); + expect(execSyncMock).not.toHaveBeenCalled(); + expect(output.success).toBe(false); + }); + + it('run update target and should throw an exception', async () => { + fsMock({ + 'apps/app/pyproject.toml': `[tool.poetry] +name = "app" +version = "1.0.0" + [[tool.poetry.packages]] + include = "app" + + [tool.poetry.dependencies] + python = "^3.8" + click = "click" +`, + }); + + execSyncMock.mockImplementation(() => { + throw new Error('fake error'); + }); + + const options = { + name: 'numpy', + local: false, + }; + + const context = { + cwd: '', + root: '', + isVerbose: false, + projectName: 'app', + workspace: { + version: 2, + npmScope: '@lucasvieira', + projects: { + app: { + root: 'apps/app', + targets: {}, + }, + }, + }, + }; + + const output = await executor(options, context); + expect(execSyncMock).toHaveBeenCalledWith('poetry update numpy ', { + cwd: 'apps/app', + stdio: 'inherit', + }); + expect(output.success).toBe(false); + }); + + it('run update target and should update all the dependency tree', async () => { + fsMock({ + 'apps/app/pyproject.toml': `[tool.poetry] +name = "app" +version = "1.0.0" + [[tool.poetry.packages]] + include = "app" + + [tool.poetry.dependencies] + python = "^3.8" + click = "click" + lib1 = { path = "../../libs/lib1" } +`, + + 'apps/app1/pyproject.toml': `[tool.poetry] +name = "app1" +version = "1.0.0" + [[tool.poetry.packages]] + include = "app" + + [tool.poetry.dependencies] + python = "^3.8" + click = "click" + lib1 = { path = "../../libs/lib1" } +`, + + 'libs/lib1/pyproject.toml': `[tool.poetry] + name = "lib1" + version = "1.0.0" + [[tool.poetry.packages]] + include = "app" + + [tool.poetry.dependencies] + python = "^3.8" + shared1 = { path = "../../libs/shared1" }`, + + 'libs/shared1/pyproject.toml': `[tool.poetry] + name = "lib1" + version = "1.0.0" + [[tool.poetry.packages]] + include = "app" + + [tool.poetry.dependencies] + python = "^3.8"`, + }); + + const options = { + name: 'numpy', + local: false, + }; + + const context = { + cwd: '', + root: '', + isVerbose: false, + projectName: 'shared1', + workspace: { + version: 2, + npmScope: '@lucasvieira', + projects: { + app: { + root: 'apps/app', + targets: {}, + }, + app1: { + root: 'apps/app1', + targets: {}, + }, + app3: { + root: 'apps/app3', + targets: {}, + }, + lib1: { + root: 'libs/lib1', + targets: {}, + }, + shared1: { + root: 'libs/shared1', + targets: {}, + }, + }, + }, + }; + + const output = await executor(options, context); + expect(execSyncMock).toHaveBeenCalledTimes(4); + expect(execSyncMock).toHaveBeenNthCalledWith(1, 'poetry update numpy ', { + cwd: 'libs/shared1', + stdio: 'inherit', + }); + expect(execSyncMock).toHaveBeenNthCalledWith(2, 'poetry update lib1', { + cwd: 'libs/lib1', + stdio: 'inherit', + }); + expect(execSyncMock).toHaveBeenNthCalledWith(3, 'poetry update lib1', { + cwd: 'apps/app', + stdio: 'inherit', + }); + expect(execSyncMock).toHaveBeenNthCalledWith(4, 'poetry update lib1', { + cwd: 'apps/app1', + stdio: 'inherit', + }); + expect(output.success).toBe(true); + }); + + it('run update target with local dependency', async () => { + fsMock({ + 'apps/app/pyproject.toml': `[tool.poetry] +name = "app" +version = "1.0.0" + [[tool.poetry.packages]] + include = "app" + + [tool.poetry.dependencies] + python = "^3.8" + click = "click"`, + + 'libs/lib1/pyproject.toml': `[tool.poetry] +name = "lib1" +version = "1.0.0" + [[tool.poetry.packages]] + include = "app" + + [tool.poetry.dependencies] + python = "^3.8"`, + }); + + const options = { + name: 'lib1', + local: true, + }; + + const context = { + cwd: '', + root: '', + isVerbose: false, + projectName: 'app', + workspace: { + version: 2, + npmScope: '@lucasvieira', + projects: { + app: { + root: 'apps/app', + targets: {}, + }, + lib1: { + root: 'libs/lib1', + targets: {}, + }, + }, + }, + }; + + const output = await executor(options, context); + expect(execSyncMock).toHaveBeenCalledTimes(1); + expect(execSyncMock).toHaveBeenNthCalledWith(1, 'poetry update lib1', { + cwd: 'apps/app', + stdio: 'inherit', + }); + expect(output.success).toBe(true); + }); + + it('run update target with local dependency with project name and package name different', async () => { + fsMock({ + 'apps/app/pyproject.toml': `[tool.poetry] +name = "dgx-devops-app" +version = "1.0.0" + [[tool.poetry.packages]] + include = "app" + + [tool.poetry.dependencies] + python = "^3.8" + click = "click"`, + + 'libs/lib1/pyproject.toml': `[tool.poetry] +name = "dgx-devops-lib1" +version = "1.0.0" + [[tool.poetry.packages]] + include = "app" + + [tool.poetry.dependencies] + python = "^3.8"`, + }); + + const options = { + name: 'lib1', + local: true, + }; + + const context = { + cwd: '', + root: '', + isVerbose: false, + projectName: 'app', + workspace: { + version: 2, + npmScope: '@lucasvieira', + projects: { + app: { + root: 'apps/app', + targets: {}, + }, + lib1: { + root: 'libs/lib1', + targets: {}, + }, + }, + }, + }; + + const output = await executor(options, context); + expect(execSyncMock).toHaveBeenCalledTimes(1); + expect(execSyncMock).toHaveBeenNthCalledWith(1, 'poetry update dgx-devops-lib1', { + cwd: 'apps/app', + stdio: 'inherit', + }); + expect(output.success).toBe(true); + + const { + tool: { + poetry: { dependencies }, + }, + } = parseToml('apps/app/pyproject.toml'); + + expect(dependencies['dgx-devops-lib1']).toStrictEqual({ + path: '../../libs/lib1', + develop: true, + }); + }); + + it('run update target and should update the dependency using custom args', async () => { + fsMock({ + 'apps/app/pyproject.toml': `[tool.poetry] +name = "app" +version = "1.0.0" + [[tool.poetry.packages]] + include = "app" + + [tool.poetry.dependencies] + python = "^3.8" + click = "click" +`, + }); + + const options = { + name: 'numpy', + local: false, + args: "--group dev" + }; + + const context = { + cwd: '', + root: '', + isVerbose: false, + projectName: 'app', + workspace: { + version: 2, + npmScope: '@lucasvieira', + projects: { + app: { + root: 'apps/app', + targets: {}, + }, + }, + }, + }; + + const output = await executor(options, context); + expect(execSyncMock).toHaveBeenCalledWith('poetry update numpy --group dev', { + cwd: 'apps/app', + stdio: 'inherit', + }); + expect(output.success).toBe(true); + }); + + it('run update target and should update all dependencies', async () => { + fsMock({ + 'apps/app/pyproject.toml': `[tool.poetry] +name = "app" +version = "1.0.0" + [[tool.poetry.packages]] + include = "app" + + [tool.poetry.dependencies] + python = "^3.8" + click = "click" +`, + }); + + const options = { + local: false + }; + + const context = { + cwd: '', + root: '', + isVerbose: false, + projectName: 'app', + workspace: { + version: 2, + npmScope: '@lucasvieira', + projects: { + app: { + root: 'apps/app', + targets: {}, + }, + }, + }, + }; + + const output = await executor(options, context); + expect(execSyncMock).toHaveBeenCalledWith('poetry update ', { + cwd: 'apps/app', + stdio: 'inherit', + }); + expect(output.success).toBe(true); + }); +}); diff --git a/packages/nx-python/src/executors/update/executor.ts b/packages/nx-python/src/executors/update/executor.ts new file mode 100644 index 0000000..237ee80 --- /dev/null +++ b/packages/nx-python/src/executors/update/executor.ts @@ -0,0 +1,90 @@ +import { ExecutorContext, ProjectConfiguration } from '@nrwl/devkit'; +import chalk from 'chalk'; +import { UpdateExecutorSchema } from './schema'; +import path from 'path'; +import { addLocalProjectToPoetryProject, updateProject } from '../utils/poetry'; +import { execSync } from 'child_process'; +import { updateDependencyTree } from '../../dependency/update-dependency'; + +export default async function runExecutor( + options: UpdateExecutorSchema, + context: ExecutorContext +) { + try { + const projectConfig = context.workspace.projects[context.projectName]; + + if (options.local && options.name) { + console.log( + chalk`\n {bold Updating {bgBlue ${options.name} } workspace dependency...}\n` + ); + updateLocalProject(context, options.name, projectConfig); + } else { + if (options.name) { + console.log( + chalk`\n {bold Updating {bgBlue ${options.name} } dependency...}\n` + ); + } else { + console.log(chalk`\n {bold Updating project dependencies...}\n`); + } + + const updateCommand = `poetry update ${options.name ?? ''} ${ + options.args ?? '' + }`; + console.log( + chalk`{bold Running command}: ${updateCommand} at {bold ${projectConfig.root}} folder\n` + ); + execSync(updateCommand, { + cwd: projectConfig.root, + stdio: 'inherit', + }); + } + + updateDependencyTree(context); + + console.log( + chalk`\n {green.bold '${options.name}'} {green dependency has been successfully added to the project}\n` + ); + + return { + success: true, + }; + } catch (error) { + console.log(chalk`\n {bgRed.bold ERROR } ${error.message}\n`); + return { + success: false, + }; + } +} + +export function updateLocalProject( + context: ExecutorContext, + dependencyName: string, + projectConfig: ProjectConfiguration +) { + const dependencyConfig = getLocalDependencyConfig(context, dependencyName); + + const dependencyPath = path.relative( + projectConfig.root, + dependencyConfig.root + ); + + const dependencyPkgName = addLocalProjectToPoetryProject( + projectConfig, + dependencyConfig, + dependencyPath + ); + updateProject(dependencyPkgName, projectConfig.root); +} + +export function getLocalDependencyConfig( + context: ExecutorContext, + dependencyName: string +) { + const dependencyConfig = context.workspace.projects[dependencyName]; + if (!dependencyConfig) { + throw new Error( + chalk`project {bold ${dependencyName}} not found in the Nx workspace` + ); + } + return dependencyConfig; +} diff --git a/packages/nx-python/src/executors/update/schema.d.ts b/packages/nx-python/src/executors/update/schema.d.ts new file mode 100644 index 0000000..6668959 --- /dev/null +++ b/packages/nx-python/src/executors/update/schema.d.ts @@ -0,0 +1,5 @@ +export interface UpdateExecutorSchema { + name?: string; + local: boolean; + args?: string; +} diff --git a/packages/nx-python/src/executors/update/schema.json b/packages/nx-python/src/executors/update/schema.json new file mode 100644 index 0000000..3a3f8b4 --- /dev/null +++ b/packages/nx-python/src/executors/update/schema.json @@ -0,0 +1,23 @@ +{ + "$schema": "http://json-schema.org/schema", + "cli": "nx", + "title": "Update executor", + "description": "", + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Package name" + }, + "args": { + "type": "string", + "description": "Install additional arguments" + }, + "local": { + "type": "boolean", + "description": "Local dependency", + "default": false + } + }, + "required": [] +} diff --git a/packages/nx-python/src/executors/utils/logger.spec.ts b/packages/nx-python/src/executors/utils/logger.spec.ts new file mode 100644 index 0000000..29470a5 --- /dev/null +++ b/packages/nx-python/src/executors/utils/logger.spec.ts @@ -0,0 +1,44 @@ +import { Logger } from "./logger" + +describe('Executor logger', () => { + + let consoleSpy = null + beforeEach(() => { + consoleSpy = jest.spyOn(console, 'info'); + }) + + afterEach(() => { + jest.resetAllMocks() + }) + + it('should print the message in the console', () => { + const logger = new Logger() + logger.setOptions({ + silent: false + }) + + logger.info("hello") + expect(consoleSpy).toHaveBeenCalledWith( + expect.stringContaining('hello') + ); + }) + + it('should print the message in the console when the options are not specified', () => { + const logger = new Logger() + + logger.info("hello") + expect(consoleSpy).toHaveBeenCalledWith( + expect.stringContaining('hello') + ); + }) + + it('should not print the message in the console when the options.silent is true', () => { + const logger = new Logger() + logger.setOptions({ + silent: true + }) + + logger.info("hello") + expect(consoleSpy).not.toHaveBeenCalled(); + }) +}) diff --git a/packages/nx-python/src/executors/utils/logger.ts b/packages/nx-python/src/executors/utils/logger.ts new file mode 100644 index 0000000..788c8e5 --- /dev/null +++ b/packages/nx-python/src/executors/utils/logger.ts @@ -0,0 +1,23 @@ + +interface ExecutorLoggerOption { + silent: boolean; +} + +export class Logger { + + private options: ExecutorLoggerOption | null + + public setOptions(options: ExecutorLoggerOption) { + this.options = options + } + + public info(message: unknown) { + this.log(message, 'info'); + } + + private log(message: unknown, level: string) { + if (!this.options?.silent) { + console[level](message); + } + } +} diff --git a/packages/nx-python/src/executors/utils/poetry.ts b/packages/nx-python/src/executors/utils/poetry.ts new file mode 100644 index 0000000..625b250 --- /dev/null +++ b/packages/nx-python/src/executors/utils/poetry.ts @@ -0,0 +1,49 @@ +import { ProjectConfiguration } from '@nrwl/devkit'; +import chalk from 'chalk'; +import { execSync } from 'child_process'; +import path from 'path'; +import toml from '@iarna/toml'; +import fs from 'fs'; + +export function addLocalProjectToPoetryProject( + targetConfig: ProjectConfiguration, + dependencyConfig: ProjectConfiguration, + dependencyPath: string +): string { + const targetToml = getProjectTomlPath(targetConfig); + const dependencyToml = getProjectTomlPath(dependencyConfig); + const targetTomlData = parseToml(targetToml); + const dependencyTomlData = parseToml(dependencyToml); + + const dependencyName = dependencyTomlData.tool.poetry.name; + targetTomlData.tool.poetry.dependencies[dependencyName] = { + path: dependencyPath, + develop: true, + }; + + fs.writeFileSync(targetToml, toml.stringify(targetTomlData)); + + return dependencyName; +} + +export function updateProject(projectName: string, cwd: string) { + const updateLockCommand = `poetry update ${projectName}`; + console.log( + chalk`{bold Running command}: ${updateLockCommand} at {bold ${cwd}} folder\n` + ); + execSync(updateLockCommand, { + cwd, + stdio: 'inherit', + }); +} + +export function getProjectTomlPath(targetConfig: ProjectConfiguration) { + return path.join(targetConfig.root, 'pyproject.toml'); +} + +export function parseToml(tomlFile: string) { + return toml.parse( + fs.readFileSync(tomlFile, 'utf-8') + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ) as any; +} diff --git a/packages/nx-python/src/generators/project/__snapshots__/generator.spec.ts.snap b/packages/nx-python/src/generators/project/__snapshots__/generator.spec.ts.snap new file mode 100644 index 0000000..c7f6aca --- /dev/null +++ b/packages/nx-python/src/generators/project/__snapshots__/generator.spec.ts.snap @@ -0,0 +1,716 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`nx-python project generator should generate a python project into a different folder 1`] = ` +Object { + "projectType": "application", + "root": "apps/shared/test", + "sourceRoot": "apps/shared/test/shared_test", + "tags": Array [], + "targets": Object { + "add": Object { + "executor": "@nxlv/python:add", + "options": Object {}, + }, + "build": Object { + "executor": "@nxlv/python:build", + "options": Object { + "outputPath": "apps/shared/test/dist", + "publish": true, + }, + "outputs": Array [ + "apps/shared/test/dist", + ], + }, + "install": Object { + "executor": "@nxlv/python:install", + "options": Object { + "args": "", + "cacheDir": ".cache/pypoetry", + "debug": false, + "silent": false, + "verbose": false, + }, + }, + "lint": Object { + "executor": "@nxlv/python:flake8", + "options": Object { + "outputFile": "reports/apps/shared/test/pylint.txt", + }, + "outputs": Array [ + "reports/apps/shared/test/pylint.txt", + ], + }, + "remove": Object { + "executor": "@nxlv/python:remove", + "options": Object {}, + }, + "test": Object { + "executor": "@nrwl/workspace:run-commands", + "options": Object { + "command": "poetry run pytest tests/", + "cwd": "apps/shared/test", + }, + "outputs": Array [ + "reports/apps/shared/test/unittests", + "coverage/apps/shared/test", + ], + }, + "tox": Object { + "executor": "@nxlv/python:tox", + "options": Object { + "args": "", + "silent": false, + }, + "outputs": Array [ + "reports/apps/shared/test/unittests", + "coverage/apps/shared/test", + ], + }, + "update": Object { + "executor": "@nxlv/python:update", + "options": Object {}, + }, + }, +} +`; + +exports[`nx-python project generator should generate a python project into a different folder 2`] = ` +"[tox] +isolated_build = True +envlist = py38 + +[testenv] +whitelist_externals = + poetry +commands = + poetry install -vv --no-root + poetry run pytest {posargs} tests/ +" +`; + +exports[`nx-python project generator should generate a python project into a different folder 3`] = ` +"# shared-test + +## About + +Project description here. + +## [Change log](CHANGELOG.md) +" +`; + +exports[`nx-python project generator should generate a python project into a different folder 4`] = ` +"[tool.coverage.run] +branch = true +source = [ \\"shared_test\\" ] + +[tool.coverage.report] +exclude_lines = [ ] +show_missing = true + +[tool.pytest.ini_options] +addopts = \\"--cov --cov-report html:'../../../coverage/apps/shared/test/html' --cov-report xml:'../../../coverage/apps/shared/test/coverage.xml' --junitxml='../../../reports/apps/shared/test/unittests/junit.xml' --html='../../../reports/apps/shared/test/unittests/html/index.html'\\" + +[tool.poetry] +name = \\"unittest-test-pkg-name\\" +version = \\"1.0.0\\" +description = \\"\\" +authors = [ ] +license = 'Proprietary' +readme = 'README.md' + + [[tool.poetry.packages]] + include = \\"shared_test\\" + + [tool.poetry.dependencies] + python = \\"^3.8\\" + + [tool.poetry.dev-dependencies] + pytest = \\"6.2.4\\" + pytest-cov = \\"2.11.1\\" + pytest-html = \\"3.1.1\\" + pytest-sugar = \\"0.9.4\\" + tox = \\"3.23.1\\" + flake8 = \\"4.0.1\\" + autopep8 = \\"1.5.7\\" + +[build-system] +requires = [\\"poetry-core==1.0.3\\"] +build-backend = \\"poetry.core.masonry.api\\" +" +`; + +exports[`nx-python project generator should generate a python project into a different folder 5`] = ` +"# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## Unreleased +" +`; + +exports[`nx-python project generator should generate a python project into a different folder 6`] = ` +"[flake8] +exclude = + .git, + __pycache__, + build, + dist, + .tox, + venv, + .venv, + .pytest_cache +max-line-length = 120 +" +`; + +exports[`nx-python project generator should generate a python project into a different folder 7`] = ` +" +def hello(): + return \\"Hello shared-test\\" +" +`; + +exports[`nx-python project generator should generate a python project into a different folder 8`] = ` +"from shared_test import index + + +def test_index(): + assert index.hello() == \\"Hello shared-test\\" +" +`; + +exports[`nx-python project generator should generate a python project using custom source 1`] = ` +"[tool.coverage.run] +branch = true +source = [ \\"test\\" ] + +[tool.coverage.report] +exclude_lines = [ ] +show_missing = true + +[tool.pytest.ini_options] +addopts = \\"--cov --cov-report html:'../../coverage/apps/test/html' --cov-report xml:'../../coverage/apps/test/coverage.xml' --junitxml='../../reports/apps/test/unittests/junit.xml' --html='../../reports/apps/test/unittests/html/index.html'\\" + +[tool.poetry] +name = \\"unittest-test-pkg-name\\" +version = \\"1.0.0\\" +description = \\"\\" +authors = [ ] +license = \\"Proprietary\\" +readme = \\"README.md\\" + + [[tool.poetry.packages]] + include = \\"test\\" + + [tool.poetry.dependencies] + python = \\"^3.8\\" + + [tool.poetry.dev-dependencies] + pytest = \\"6.2.4\\" + pytest-cov = \\"2.11.1\\" + pytest-html = \\"3.1.1\\" + pytest-sugar = \\"0.9.4\\" + tox = \\"3.23.1\\" + flake8 = \\"4.0.1\\" + autopep8 = \\"1.5.7\\" + + [[tool.poetry.source]] + name = \\"aws\\" + url = \\"https://custom.com\\" + secondary = true + +[build-system] +requires = [ \\"poetry-core==1.0.3\\" ] +build-backend = \\"poetry.core.masonry.api\\" +" +`; + +exports[`nx-python project generator should generate a python project using description option 1`] = ` +"[tool.coverage.run] +branch = true +source = [ \\"test\\" ] + +[tool.coverage.report] +exclude_lines = [ ] +show_missing = true + +[tool.pytest.ini_options] +addopts = \\"--cov --cov-report html:'../../coverage/apps/test/html' --cov-report xml:'../../coverage/apps/test/coverage.xml' --junitxml='../../reports/apps/test/unittests/junit.xml' --html='../../reports/apps/test/unittests/html/index.html'\\" + +[tool.poetry] +name = \\"unittest-test-pkg-name\\" +version = \\"1.0.0\\" +description = \\"My custom description\\" +authors = [ ] +license = 'Proprietary' +readme = 'README.md' + + [[tool.poetry.packages]] + include = \\"test\\" + + [tool.poetry.dependencies] + python = \\"^3.8\\" + + [tool.poetry.dev-dependencies] + pytest = \\"6.2.4\\" + pytest-cov = \\"2.11.1\\" + pytest-html = \\"3.1.1\\" + pytest-sugar = \\"0.9.4\\" + tox = \\"3.23.1\\" + flake8 = \\"4.0.1\\" + autopep8 = \\"1.5.7\\" + +[build-system] +requires = [\\"poetry-core==1.0.3\\"] +build-backend = \\"poetry.core.masonry.api\\" +" +`; + +exports[`nx-python project generator should generate a python project with tags 1`] = ` +Object { + "projectType": "application", + "root": "apps/test", + "sourceRoot": "apps/test/test", + "tags": Array [ + "python-project", + "nx", + "poetry", + "tox", + ], + "targets": Object { + "add": Object { + "executor": "@nxlv/python:add", + "options": Object {}, + }, + "build": Object { + "executor": "@nxlv/python:build", + "options": Object { + "outputPath": "apps/test/dist", + "publish": true, + }, + "outputs": Array [ + "apps/test/dist", + ], + }, + "install": Object { + "executor": "@nxlv/python:install", + "options": Object { + "args": "", + "cacheDir": ".cache/pypoetry", + "debug": false, + "silent": false, + "verbose": false, + }, + }, + "lint": Object { + "executor": "@nxlv/python:flake8", + "options": Object { + "outputFile": "reports/apps/test/pylint.txt", + }, + "outputs": Array [ + "reports/apps/test/pylint.txt", + ], + }, + "remove": Object { + "executor": "@nxlv/python:remove", + "options": Object {}, + }, + "test": Object { + "executor": "@nrwl/workspace:run-commands", + "options": Object { + "command": "poetry run pytest tests/", + "cwd": "apps/test", + }, + "outputs": Array [ + "reports/apps/test/unittests", + "coverage/apps/test", + ], + }, + "tox": Object { + "executor": "@nxlv/python:tox", + "options": Object { + "args": "", + "silent": false, + }, + "outputs": Array [ + "reports/apps/test/unittests", + "coverage/apps/test", + ], + }, + "update": Object { + "executor": "@nxlv/python:update", + "options": Object {}, + }, + }, +} +`; + +exports[`nx-python project generator should successfully generate a python library project 1`] = ` +Object { + "projectType": "library", + "root": "libs/test", + "sourceRoot": "libs/test/test", + "tags": Array [], + "targets": Object { + "add": Object { + "executor": "@nxlv/python:add", + "options": Object {}, + }, + "build": Object { + "executor": "@nxlv/python:build", + "options": Object { + "outputPath": "libs/test/dist", + "publish": true, + }, + "outputs": Array [ + "libs/test/dist", + ], + }, + "install": Object { + "executor": "@nxlv/python:install", + "options": Object { + "args": "", + "cacheDir": ".cache/pypoetry", + "debug": false, + "silent": false, + "verbose": false, + }, + }, + "lint": Object { + "executor": "@nxlv/python:flake8", + "options": Object { + "outputFile": "reports/libs/test/pylint.txt", + }, + "outputs": Array [ + "reports/libs/test/pylint.txt", + ], + }, + "remove": Object { + "executor": "@nxlv/python:remove", + "options": Object {}, + }, + "test": Object { + "executor": "@nrwl/workspace:run-commands", + "options": Object { + "command": "poetry run pytest tests/", + "cwd": "libs/test", + }, + "outputs": Array [ + "reports/libs/test/unittests", + "coverage/libs/test", + ], + }, + "tox": Object { + "executor": "@nxlv/python:tox", + "options": Object { + "args": "", + "silent": false, + }, + "outputs": Array [ + "reports/libs/test/unittests", + "coverage/libs/test", + ], + }, + "update": Object { + "executor": "@nxlv/python:update", + "options": Object {}, + }, + }, +} +`; + +exports[`nx-python project generator should successfully generate a python library project 2`] = ` +"[tox] +isolated_build = True +envlist = py38 + +[testenv] +whitelist_externals = + poetry +commands = + poetry install -vv --no-root + poetry run pytest {posargs} tests/ +" +`; + +exports[`nx-python project generator should successfully generate a python library project 3`] = ` +"# test + +## About + +Project description here. + +## [Change log](CHANGELOG.md) +" +`; + +exports[`nx-python project generator should successfully generate a python library project 4`] = ` +"[tool.coverage.run] +branch = true +source = [ \\"test\\" ] + +[tool.coverage.report] +exclude_lines = [ ] +show_missing = true + +[tool.pytest.ini_options] +addopts = \\"--cov --cov-report html:'../../coverage/libs/test/html' --cov-report xml:'../../coverage/libs/test/coverage.xml' --junitxml='../../reports/libs/test/unittests/junit.xml' --html='../../reports/libs/test/unittests/html/index.html'\\" + +[tool.poetry] +name = \\"unittest-test-pkg-name\\" +version = \\"1.0.0\\" +description = \\"\\" +authors = [ ] +license = 'Proprietary' +readme = 'README.md' + + [[tool.poetry.packages]] + include = \\"test\\" + + [tool.poetry.dependencies] + python = \\"^3.8\\" + + [tool.poetry.dev-dependencies] + pytest = \\"6.2.4\\" + pytest-cov = \\"2.11.1\\" + pytest-html = \\"3.1.1\\" + pytest-sugar = \\"0.9.4\\" + tox = \\"3.23.1\\" + flake8 = \\"4.0.1\\" + autopep8 = \\"1.5.7\\" + +[build-system] +requires = [\\"poetry-core==1.0.3\\"] +build-backend = \\"poetry.core.masonry.api\\" +" +`; + +exports[`nx-python project generator should successfully generate a python library project 5`] = ` +"# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## Unreleased +" +`; + +exports[`nx-python project generator should successfully generate a python library project 6`] = ` +"[flake8] +exclude = + .git, + __pycache__, + build, + dist, + .tox, + venv, + .venv, + .pytest_cache +max-line-length = 120 +" +`; + +exports[`nx-python project generator should successfully generate a python library project 7`] = ` +" +def hello(): + return \\"Hello test\\" +" +`; + +exports[`nx-python project generator should successfully generate a python library project 8`] = ` +"from test import index + + +def test_index(): + assert index.hello() == \\"Hello test\\" +" +`; + +exports[`nx-python project generator should successfully generate a python project 1`] = ` +Object { + "projectType": "application", + "root": "apps/test", + "sourceRoot": "apps/test/test", + "tags": Array [], + "targets": Object { + "add": Object { + "executor": "@nxlv/python:add", + "options": Object {}, + }, + "build": Object { + "executor": "@nxlv/python:build", + "options": Object { + "outputPath": "apps/test/dist", + "publish": true, + }, + "outputs": Array [ + "apps/test/dist", + ], + }, + "install": Object { + "executor": "@nxlv/python:install", + "options": Object { + "args": "", + "cacheDir": ".cache/pypoetry", + "debug": false, + "silent": false, + "verbose": false, + }, + }, + "lint": Object { + "executor": "@nxlv/python:flake8", + "options": Object { + "outputFile": "reports/apps/test/pylint.txt", + }, + "outputs": Array [ + "reports/apps/test/pylint.txt", + ], + }, + "remove": Object { + "executor": "@nxlv/python:remove", + "options": Object {}, + }, + "test": Object { + "executor": "@nrwl/workspace:run-commands", + "options": Object { + "command": "poetry run pytest tests/", + "cwd": "apps/test", + }, + "outputs": Array [ + "reports/apps/test/unittests", + "coverage/apps/test", + ], + }, + "tox": Object { + "executor": "@nxlv/python:tox", + "options": Object { + "args": "", + "silent": false, + }, + "outputs": Array [ + "reports/apps/test/unittests", + "coverage/apps/test", + ], + }, + "update": Object { + "executor": "@nxlv/python:update", + "options": Object {}, + }, + }, +} +`; + +exports[`nx-python project generator should successfully generate a python project 2`] = ` +"[tox] +isolated_build = True +envlist = py38 + +[testenv] +whitelist_externals = + poetry +commands = + poetry install -vv --no-root + poetry run pytest {posargs} tests/ +" +`; + +exports[`nx-python project generator should successfully generate a python project 3`] = ` +"# test + +## About + +Project description here. + +## [Change log](CHANGELOG.md) +" +`; + +exports[`nx-python project generator should successfully generate a python project 4`] = ` +"[tool.coverage.run] +branch = true +source = [ \\"test\\" ] + +[tool.coverage.report] +exclude_lines = [ ] +show_missing = true + +[tool.pytest.ini_options] +addopts = \\"--cov --cov-report html:'../../coverage/apps/test/html' --cov-report xml:'../../coverage/apps/test/coverage.xml' --junitxml='../../reports/apps/test/unittests/junit.xml' --html='../../reports/apps/test/unittests/html/index.html'\\" + +[tool.poetry] +name = \\"unittest-test-pkg-name\\" +version = \\"1.0.0\\" +description = \\"\\" +authors = [ ] +license = 'Proprietary' +readme = 'README.md' + + [[tool.poetry.packages]] + include = \\"test\\" + + [tool.poetry.dependencies] + python = \\"^3.8\\" + + [tool.poetry.dev-dependencies] + pytest = \\"6.2.4\\" + pytest-cov = \\"2.11.1\\" + pytest-html = \\"3.1.1\\" + pytest-sugar = \\"0.9.4\\" + tox = \\"3.23.1\\" + flake8 = \\"4.0.1\\" + autopep8 = \\"1.5.7\\" + +[build-system] +requires = [\\"poetry-core==1.0.3\\"] +build-backend = \\"poetry.core.masonry.api\\" +" +`; + +exports[`nx-python project generator should successfully generate a python project 5`] = ` +"# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## Unreleased +" +`; + +exports[`nx-python project generator should successfully generate a python project 6`] = ` +"[flake8] +exclude = + .git, + __pycache__, + build, + dist, + .tox, + venv, + .venv, + .pytest_cache +max-line-length = 120 +" +`; + +exports[`nx-python project generator should successfully generate a python project 7`] = ` +" +def hello(): + return \\"Hello test\\" +" +`; + +exports[`nx-python project generator should successfully generate a python project 8`] = ` +"from test import index + + +def test_index(): + assert index.hello() == \\"Hello test\\" +" +`; diff --git a/packages/nx-python/src/generators/project/files/CHANGELOG.md b/packages/nx-python/src/generators/project/files/CHANGELOG.md new file mode 100644 index 0000000..a4af7e8 --- /dev/null +++ b/packages/nx-python/src/generators/project/files/CHANGELOG.md @@ -0,0 +1,8 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## Unreleased diff --git a/packages/nx-python/src/generators/project/files/README.md b/packages/nx-python/src/generators/project/files/README.md new file mode 100644 index 0000000..5da3b36 --- /dev/null +++ b/packages/nx-python/src/generators/project/files/README.md @@ -0,0 +1,7 @@ +# <%= projectName %> + +## About + +Project description here. + +## [Change log](CHANGELOG.md) diff --git a/packages/nx-python/src/generators/project/files/__dot__flake8.template b/packages/nx-python/src/generators/project/files/__dot__flake8.template new file mode 100644 index 0000000..3da962c --- /dev/null +++ b/packages/nx-python/src/generators/project/files/__dot__flake8.template @@ -0,0 +1,11 @@ +[flake8] +exclude = + .git, + __pycache__, + build, + dist, + .tox, + venv, + .venv, + .pytest_cache +max-line-length = 120 diff --git a/packages/nx-python/src/generators/project/files/__moduleName__/index.py b/packages/nx-python/src/generators/project/files/__moduleName__/index.py new file mode 100644 index 0000000..7938e31 --- /dev/null +++ b/packages/nx-python/src/generators/project/files/__moduleName__/index.py @@ -0,0 +1,3 @@ + +def hello(): + return "Hello <%= projectName %>" diff --git a/packages/nx-python/src/generators/project/files/poetry.toml b/packages/nx-python/src/generators/project/files/poetry.toml new file mode 100644 index 0000000..ab1033b --- /dev/null +++ b/packages/nx-python/src/generators/project/files/poetry.toml @@ -0,0 +1,2 @@ +[virtualenvs] +in-project = true diff --git a/packages/nx-python/src/generators/project/files/pyproject.toml b/packages/nx-python/src/generators/project/files/pyproject.toml new file mode 100644 index 0000000..945396c --- /dev/null +++ b/packages/nx-python/src/generators/project/files/pyproject.toml @@ -0,0 +1,37 @@ +[tool.coverage.run] +branch = true +source = [ "<%= moduleName %>" ] + +[tool.coverage.report] +exclude_lines = [ ] +show_missing = true + +[tool.pytest.ini_options] +addopts = "--cov --cov-report html:'<%= offsetFromRoot %>coverage/<%= projectRoot %>/html' --cov-report xml:'<%= offsetFromRoot %>coverage/<%= projectRoot %>/coverage.xml' --junitxml='<%= offsetFromRoot %>reports/<%= projectRoot %>/unittests/junit.xml' --html='<%= offsetFromRoot %>reports/<%= projectRoot %>/unittests/html/index.html'" + +[tool.poetry] +name = "<%= packageName %>" +version = "1.0.0" +description = "<%= description %>" +authors = [ ] +license = 'Proprietary' +readme = 'README.md' + + [[tool.poetry.packages]] + include = "<%= moduleName %>" + + [tool.poetry.dependencies] + python = "^3.8" + + [tool.poetry.dev-dependencies] + pytest = "6.2.4" + pytest-cov = "2.11.1" + pytest-html = "3.1.1" + pytest-sugar = "0.9.4" + tox = "3.23.1" + flake8 = "4.0.1" + autopep8 = "1.5.7" + +[build-system] +requires = ["poetry-core==1.0.3"] +build-backend = "poetry.core.masonry.api" diff --git a/packages/nx-python/src/generators/project/files/tests/test_index.py b/packages/nx-python/src/generators/project/files/tests/test_index.py new file mode 100644 index 0000000..e38d089 --- /dev/null +++ b/packages/nx-python/src/generators/project/files/tests/test_index.py @@ -0,0 +1,5 @@ +from <%= moduleName %> import index + + +def test_index(): + assert index.hello() == "Hello <%= projectName %>" diff --git a/packages/nx-python/src/generators/project/files/tox.ini b/packages/nx-python/src/generators/project/files/tox.ini new file mode 100644 index 0000000..a57f2cd --- /dev/null +++ b/packages/nx-python/src/generators/project/files/tox.ini @@ -0,0 +1,10 @@ +[tox] +isolated_build = True +envlist = py38 + +[testenv] +whitelist_externals = + poetry +commands = + poetry install -vv --no-root + poetry run pytest {posargs} tests/ diff --git a/packages/nx-python/src/generators/project/generator.spec.ts b/packages/nx-python/src/generators/project/generator.spec.ts new file mode 100644 index 0000000..23c5e46 --- /dev/null +++ b/packages/nx-python/src/generators/project/generator.spec.ts @@ -0,0 +1,129 @@ +import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing'; +import { Tree, readProjectConfiguration } from '@nrwl/devkit'; + +import generator from './generator'; +import { Schema } from './schema'; + +describe('nx-python project generator', () => { + let appTree: Tree; + const options: Schema = { + name: 'test', + type: 'application', + packageName: 'unittest-test-pkg-name', + publishable: true, + customSource: false, + }; + + beforeEach(() => { + appTree = createTreeWithEmptyWorkspace(); + }); + + it('should successfully generate a python project', async () => { + await generator(appTree, options); + const config = readProjectConfiguration(appTree, 'test'); + expect(config).toMatchSnapshot(); + + assertGenerateFiles(appTree, 'apps/test', 'test'); + }); + + it('should successfully generate a python library project', async () => { + await generator(appTree, { ...options, type: 'library' }); + const config = readProjectConfiguration(appTree, 'test'); + expect(config).toMatchSnapshot(); + + assertGenerateFiles(appTree, 'libs/test', 'test'); + }); + + it('should generate a python project into a different folder', async () => { + await generator(appTree, { + ...options, + directory: 'shared', + }); + const config = readProjectConfiguration(appTree, 'shared-test'); + expect(config).toMatchSnapshot(); + + assertGenerateFiles(appTree, 'apps/shared/test', 'shared_test'); + }); + + it('should generate a python project using description option', async () => { + await generator(appTree, { + ...options, + description: 'My custom description', + }); + expect( + appTree.read('apps/test/pyproject.toml').toString() + ).toMatchSnapshot(); + }); + + it('should generate a python project using custom source', async () => { + await generator(appTree, { + ...options, + customSource: true, + sourceName: 'aws', + sourceUrl: 'https://custom.com', + sourceSecondary: true, + }); + expect( + appTree.read('apps/test/pyproject.toml').toString() + ).toMatchSnapshot(); + }); + + it('should not generate a python project when the customSource is true and the name or url is empty', async () => { + await expect( + generator(appTree, { + ...options, + customSource: true, + }) + ).rejects.toThrow( + "Fields 'sourceName', 'sourceUrl' are required when the flag 'customSource' is true" + ); + }); + + it('should generate a python project with tags', async () => { + await generator(appTree, { + ...options, + tags: 'python-project, nx, poetry, tox', + }); + const config = readProjectConfiguration(appTree, 'test'); + expect(config).toMatchSnapshot(); + }); +}); + +function assertGenerateFiles( + appTree: Tree, + projectDirectory: string, + moduleName: string +) { + expect(appTree.exists(`${projectDirectory}/tox.ini`)).toBeTruthy(); + expect( + appTree.read(`${projectDirectory}/tox.ini`).toString() + ).toMatchSnapshot(); + expect(appTree.exists(`${projectDirectory}/README.md`)).toBeTruthy(); + expect( + appTree.read(`${projectDirectory}/README.md`).toString() + ).toMatchSnapshot(); + expect(appTree.exists(`${projectDirectory}/pyproject.toml`)).toBeTruthy(); + expect( + appTree.read(`${projectDirectory}/pyproject.toml`).toString() + ).toMatchSnapshot(); + expect(appTree.exists(`${projectDirectory}/CHANGELOG.md`)).toBeTruthy(); + expect( + appTree.read(`${projectDirectory}/CHANGELOG.md`).toString() + ).toMatchSnapshot(); + expect(appTree.exists(`${projectDirectory}/.flake8`)).toBeTruthy(); + expect( + appTree.read(`${projectDirectory}/.flake8`).toString() + ).toMatchSnapshot(); + expect( + appTree.exists(`${projectDirectory}/${moduleName}/index.py`) + ).toBeTruthy(); + expect( + appTree.read(`${projectDirectory}/${moduleName}/index.py`).toString() + ).toMatchSnapshot(); + expect( + appTree.exists(`${projectDirectory}/tests/test_index.py`) + ).toBeTruthy(); + expect( + appTree.read(`${projectDirectory}/tests/test_index.py`).toString() + ).toMatchSnapshot(); +} diff --git a/packages/nx-python/src/generators/project/generator.ts b/packages/nx-python/src/generators/project/generator.ts new file mode 100644 index 0000000..5442051 --- /dev/null +++ b/packages/nx-python/src/generators/project/generator.ts @@ -0,0 +1,180 @@ +import { + addProjectConfiguration, + convertNxGenerator, + formatFiles, + generateFiles, + getWorkspaceLayout, + names, + offsetFromRoot, + Tree, +} from '@nrwl/devkit'; +import path from 'path'; +import { Schema } from './schema'; +import { parse, stringify } from '@iarna/toml'; + +interface NormalizedSchema extends Schema { + projectName: string; + projectRoot: string; + moduleName: string; + projectDirectory: string; + parsedTags: string[]; +} + +function normalizeOptions(host: Tree, options: Schema): 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 moduleName = projectName.replace(new RegExp('-', 'g'), '_'); + + let projectRoot = ''; + + if (options.type === 'application') { + projectRoot = `${getWorkspaceLayout(host).appsDir}/${projectDirectory}`; + } else { + projectRoot = `${getWorkspaceLayout(host).libsDir}/${projectDirectory}`; + } + const parsedTags = options.tags + ? options.tags.split(',').map((s) => s.trim()) + : []; + + return { + ...options, + description: options.description ?? '', + projectName, + moduleName, + projectRoot, + projectDirectory, + parsedTags, + }; +} + +function addFiles(host: Tree, options: NormalizedSchema) { + const templateOptions = { + ...options, + ...names(options.name), + offsetFromRoot: offsetFromRoot(options.projectRoot), + template: '', + dot: '.', + }; + + generateFiles( + host, + path.join(__dirname, 'files'), + options.projectRoot, + templateOptions + ); +} + +function addPackageSource(normalizedOptions: NormalizedSchema, host: Tree) { + if (normalizedOptions.customSource) { + if ( + !normalizedOptions.sourceName || + !normalizedOptions.sourceUrl + ) { + throw new Error( + "Fields 'sourceName', 'sourceUrl' are required when the flag 'customSource' is true" + ); + } + + const pyprojectTomlPath = path.join( + normalizedOptions.projectRoot, + 'pyproject.toml' + ); + + const pyprojectTomlContent = host.read(pyprojectTomlPath).toString('utf-8'); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const pyprojectToml = parse(pyprojectTomlContent) as any; + + pyprojectToml.tool.poetry.source = [ + { + name: normalizedOptions.sourceName, + url: normalizedOptions.sourceUrl, + secondary: normalizedOptions.sourceSecondary, + }, + ]; + + host.write(pyprojectTomlPath, stringify(pyprojectToml)); + } +} + +async function generator(host: Tree, options: Schema) { + const normalizedOptions = normalizeOptions(host, options); + addProjectConfiguration(host, normalizedOptions.projectName, { + root: normalizedOptions.projectRoot, + projectType: normalizedOptions.type, + sourceRoot: `${normalizedOptions.projectRoot}/${normalizedOptions.moduleName}`, + targets: { + add: { + executor: '@nxlv/python:add', + options: {}, + }, + update: { + executor: '@nxlv/python:update', + options: {}, + }, + remove: { + executor: '@nxlv/python:remove', + options: {}, + }, + build: { + executor: '@nxlv/python:build', + outputs: [`${normalizedOptions.projectRoot}/dist`], + options: { + outputPath: `${normalizedOptions.projectRoot}/dist`, + publish: normalizedOptions.publishable + }, + }, + install: { + executor: '@nxlv/python:install', + options: { + silent: false, + args: "", + cacheDir: `.cache/pypoetry`, + verbose: false, + debug: false + }, + }, + lint: { + executor: '@nxlv/python:flake8', + outputs: [`reports/${normalizedOptions.projectRoot}/pylint.txt`], + options: { + outputFile: `reports/${normalizedOptions.projectRoot}/pylint.txt`, + }, + }, + test: { + executor: '@nrwl/workspace:run-commands', + outputs: [ + `reports/${normalizedOptions.projectRoot}/unittests`, + `coverage/${normalizedOptions.projectRoot}`, + ], + options: { + command: `poetry run pytest tests/`, + cwd: normalizedOptions.projectRoot, + }, + }, + tox: { + executor: '@nxlv/python:tox', + outputs: [ + `reports/${normalizedOptions.projectRoot}/unittests`, + `coverage/${normalizedOptions.projectRoot}`, + ], + options: { + silent: false, + args: "" + }, + }, + }, + tags: normalizedOptions.parsedTags, + }); + addFiles(host, normalizedOptions); + + addPackageSource(normalizedOptions, host); + + await formatFiles(host); +} + +export default generator; +export const schematic = convertNxGenerator(generator); diff --git a/packages/nx-python/src/generators/project/schema.d.ts b/packages/nx-python/src/generators/project/schema.d.ts new file mode 100644 index 0000000..7e9b7fe --- /dev/null +++ b/packages/nx-python/src/generators/project/schema.d.ts @@ -0,0 +1,13 @@ +export interface Schema { + name: string; + type: 'library' | 'application'; + description?: string; + tags?: string; + directory?: string; + customSource: boolean; + sourceName?: string; + sourceUrl?: string; + sourceSecondary?: boolean; + packageName: string; + publishable: boolean; +} diff --git a/packages/nx-python/src/generators/project/schema.json b/packages/nx-python/src/generators/project/schema.json new file mode 100644 index 0000000..3bb0f4a --- /dev/null +++ b/packages/nx-python/src/generators/project/schema.json @@ -0,0 +1,81 @@ +{ + "$schema": "http://json-schema.org/schema", + "$id": "NxPythonProject", + "title": "", + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Project name", + "$default": { + "$source": "argv", + "index": 0 + }, + "x-prompt": "What name would you like to use?" + }, + "packageName": { + "type": "string", + "description": "Python package name", + "x-prompt": "What package name would you like to use?" + }, + "publishable": { + "type": "boolean", + "description": "Project is publishable", + "default": true + }, + "type": { + "type": "string", + "enum": ["application", "library"], + "description": "Project type application or library", + "default": "application", + "x-prompt": { + "message": "What project type would you like to use?", + "type": "list", + "items": [ + { + "value": "application", + "label": "Application" + }, + { + "value": "library", + "label": "Library" + } + ] + } + }, + "description": { + "type": "string", + "description": "Project short description", + "x-prompt": "What description would you like to use?" + }, + "tags": { + "type": "string", + "description": "Add tags to the project", + "alias": "t" + }, + "directory": { + "type": "string", + "description": "The directory where the project is placed", + "alias": "d" + }, + "customSource": { + "type": "boolean", + "description": "Specifies if the project uses custom PyPi registry", + "default": false + }, + "sourceName": { + "type": "string", + "description": "Source name, required if the flag 'customSource' is true" + }, + "sourceUrl": { + "type": "string", + "description": "Source URL, required if the flag 'customSource' is true" + }, + "sourceSecondary": { + "type": "boolean", + "description": "Source secondary flag, required if the flag 'customSource' is true", + "default": true + } + }, + "required": ["name", "packageName", "type"] +} diff --git a/packages/nx-python/src/graph/dependency-graph.spec.ts b/packages/nx-python/src/graph/dependency-graph.spec.ts new file mode 100644 index 0000000..ce4cddb --- /dev/null +++ b/packages/nx-python/src/graph/dependency-graph.spec.ts @@ -0,0 +1,115 @@ +import { processProjectGraph } from './dependency-graph'; +import fsMock from 'mock-fs'; +import { ProjectGraphBuilder } from '@nrwl/devkit'; + +describe('nx-python dependency graph', () => { + afterEach(() => { + fsMock.restore(); + }); + + it('should progress the dependency graph', async () => { + fsMock({ + 'apps/app1/pyproject.toml': `[tool.poetry] +name = "app1" +version = "1.0.0" + [tool.poetry.dependencies] + python = "^3.8" + dep1 = { path = "../../libs/dep1" }`, + 'libs/dep1/pyproject.toml': `[tool.poetry] + name = "dep1" + version = "1.0.0" + [tool.poetry.dependencies] + python = "^3.8"`, + 'libs/dep2/pyproject.toml': `[tool.poetry] + name = "dep2" + version = "1.0.0" + [tool.poetry.dependencies] + python = "^3.8"`, + }); + + const mockBuilder = new ProjectGraphBuilder(null); + + mockBuilder.addNode({ + name: 'app1', + type: 'app', + data: {}, + }); + + mockBuilder.addNode({ + name: 'dep1', + type: 'lib', + data: {}, + }); + + const result = processProjectGraph(mockBuilder.graph, { + fileMap: {}, + filesToProcess: {}, + workspace: { + projects: { + app1: { + root: 'apps/app1', + targets: {}, + }, + dep1: { + root: 'libs/dep1', + targets: {}, + }, + dep2: { + root: 'libs/dep2', + targets: {}, + }, + dep3: { + root: 'libs/dep3', + targets: {}, + }, + }, + version: 2, + npmScope: 'test', + }, + }); + + expect(result).toStrictEqual({ + dependencies: { + app1: [ + { + source: 'app1', + target: 'dep1', + type: 'implicit', + }, + ], + dep1: [], + }, + externalNodes: {}, + nodes: { + app1: { + name: 'app1', + type: 'app', + data: {}, + }, + dep1: { + name: 'dep1', + type: 'lib', + data: {}, + }, + }, + }); + }); + + it('should progress the dependency graph for an empty project', async () => { + const result = processProjectGraph(null, { + fileMap: {}, + filesToProcess: {}, + workspace: { + projects: {}, + version: 2, + npmScope: 'test', + }, + }); + + expect(result).toStrictEqual({ + dependencies: {}, + externalNodes: {}, + nodes: {}, + }); + }); +}); diff --git a/packages/nx-python/src/graph/dependency-graph.ts b/packages/nx-python/src/graph/dependency-graph.ts new file mode 100644 index 0000000..74d8f5f --- /dev/null +++ b/packages/nx-python/src/graph/dependency-graph.ts @@ -0,0 +1,112 @@ +import { + ProjectGraphBuilder, + ProjectGraph, + ProjectGraphProcessorContext, + joinPathFragments, + Workspace, + WorkspaceJsonConfiguration, +} from '@nrwl/devkit'; +import { readFileSync, existsSync } from 'fs'; +import { parse } from '@iarna/toml'; +import path from 'path'; + +export const getDependents = ( + projectName: string, + workspace: Workspace | WorkspaceJsonConfiguration, + cwd: string = process.cwd() +): string[] => { + const deps: string[] = []; + + const { root } = workspace.projects[projectName]; + + for (const project in workspace.projects) { + if (checkProjectIsDependent(workspace, project, root, cwd)) { + deps.push(project) + } + } + + return deps; +}; + +export const getDependencies = ( + projectName: string, + workspace: Workspace | WorkspaceJsonConfiguration, + cwd: string = process.cwd() +): string[] => { + const projectData = workspace.projects[projectName]; + const pyprojectToml = joinPathFragments(projectData.root, 'pyproject.toml'); + + const deps = []; + + if (existsSync(pyprojectToml)) { + const tomlData = getPyprojectData(pyprojectToml); + + for (const dep in tomlData.tool.poetry.dependencies) { + const depData = tomlData.tool.poetry.dependencies[dep]; + + if (depData instanceof Object && depData.path) { + const depAbsPath = path.resolve(projectData.root, depData.path); + const depProjectName = Object.keys(workspace.projects).find( + (proj) => + workspace.projects[proj].root === path.relative(cwd, depAbsPath) + ); + + deps.push(depProjectName); + } + } + } + + return deps; +}; + +export const processProjectGraph = ( + graph: ProjectGraph, + context: ProjectGraphProcessorContext +) => { + const builder = new ProjectGraphBuilder(graph); + + for (const project in context.workspace.projects) { + const deps = getDependencies(project, context.workspace); + + deps.forEach((dep) => + builder.addImplicitDependency(project, dep) + ); + } + + return builder.graph; +}; + +const getPyprojectData = (pyprojectToml: string) => { + return parse( + readFileSync(pyprojectToml).toString('utf-8') + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ) as any; +}; + +const checkProjectIsDependent = ( + workspace: Workspace | WorkspaceJsonConfiguration, + project: string, + root: string, + cwd: string +): boolean => { + const projectData = workspace.projects[project]; + const pyprojectToml = joinPathFragments(projectData.root, 'pyproject.toml'); + + if (existsSync(pyprojectToml)) { + const tomlData = getPyprojectData(pyprojectToml); + + for (const dep in tomlData.tool.poetry.dependencies) { + const depData = tomlData.tool.poetry.dependencies[dep]; + + if (depData instanceof Object && depData.path) { + const depAbsPath = path.resolve(projectData.root, depData.path); + + if (root === path.relative(cwd, depAbsPath)) { + return true; + } + } + } + } + + return false; +}; diff --git a/packages/nx-python/src/index.spec.ts b/packages/nx-python/src/index.spec.ts new file mode 100644 index 0000000..adcf081 --- /dev/null +++ b/packages/nx-python/src/index.spec.ts @@ -0,0 +1,8 @@ +import * as nxPython from './index'; + +describe('nx-python index', () => { + it('should have processProjectGraph', async () => { + expect(nxPython.processProjectGraph).toBeTruthy() + expect(nxPython.processProjectGraph instanceof Function).toBeTruthy() + }); +}); diff --git a/packages/nx-python/src/index.ts b/packages/nx-python/src/index.ts new file mode 100644 index 0000000..7940024 --- /dev/null +++ b/packages/nx-python/src/index.ts @@ -0,0 +1 @@ +export * from './graph/dependency-graph' diff --git a/packages/nx-python/src/utils/mocks/child_process.mock.ts b/packages/nx-python/src/utils/mocks/child_process.mock.ts new file mode 100644 index 0000000..ec26d28 --- /dev/null +++ b/packages/nx-python/src/utils/mocks/child_process.mock.ts @@ -0,0 +1,7 @@ +export const execSyncMock = jest.fn() + +jest.mock("child_process", () => { + return { + execSync: execSyncMock + }; +}); diff --git a/packages/nx-python/src/utils/mocks/uuid.mock.ts b/packages/nx-python/src/utils/mocks/uuid.mock.ts new file mode 100644 index 0000000..d11fb6b --- /dev/null +++ b/packages/nx-python/src/utils/mocks/uuid.mock.ts @@ -0,0 +1,7 @@ +export const uuidMock = jest.fn() + +jest.mock("uuid", () => { + return { + v4: uuidMock + }; +}); diff --git a/lucasvieira/packages/nx-python/tsconfig.json b/packages/nx-python/tsconfig.json similarity index 100% rename from lucasvieira/packages/nx-python/tsconfig.json rename to packages/nx-python/tsconfig.json diff --git a/lucasvieira/packages/nx-python/tsconfig.lib.json b/packages/nx-python/tsconfig.lib.json similarity index 68% rename from lucasvieira/packages/nx-python/tsconfig.lib.json rename to packages/nx-python/tsconfig.lib.json index 6efdbee..78faa48 100644 --- a/lucasvieira/packages/nx-python/tsconfig.lib.json +++ b/packages/nx-python/tsconfig.lib.json @@ -4,8 +4,9 @@ "module": "commonjs", "outDir": "../../dist/out-tsc", "declaration": true, + "esModuleInterop": true, "types": ["node"] }, - "exclude": ["**/*.spec.ts", "**/*.test.ts"], + "exclude": ["**/*.spec.ts", "**/*.test.ts", "**/*.mock.ts"], "include": ["**/*.ts"] } diff --git a/lucasvieira/packages/nx-python/tsconfig.spec.json b/packages/nx-python/tsconfig.spec.json similarity index 92% rename from lucasvieira/packages/nx-python/tsconfig.spec.json rename to packages/nx-python/tsconfig.spec.json index 67f149c..8353952 100644 --- a/lucasvieira/packages/nx-python/tsconfig.spec.json +++ b/packages/nx-python/tsconfig.spec.json @@ -3,6 +3,7 @@ "compilerOptions": { "outDir": "../../dist/out-tsc", "module": "commonjs", + "esModuleInterop": true, "types": ["jest", "node"] }, "include": [ diff --git a/lucasvieira/tools/generators/.gitkeep b/tools/generators/.gitkeep similarity index 100% rename from lucasvieira/tools/generators/.gitkeep rename to tools/generators/.gitkeep diff --git a/lucasvieira/tools/tsconfig.tools.json b/tools/tsconfig.tools.json similarity index 100% rename from lucasvieira/tools/tsconfig.tools.json rename to tools/tsconfig.tools.json diff --git a/lucasvieira/tsconfig.base.json b/tsconfig.base.json similarity index 87% rename from lucasvieira/tsconfig.base.json rename to tsconfig.base.json index e46e441..ff19a84 100644 --- a/lucasvieira/tsconfig.base.json +++ b/tsconfig.base.json @@ -15,7 +15,7 @@ "skipDefaultLibCheck": true, "baseUrl": ".", "paths": { - "lucasvieira/nx-python": ["packages/nx-python/src/index.ts"] + "@nxlv/python": ["packages/nx-python/src/index.ts"] } }, "exclude": ["node_modules", "tmp"] diff --git a/lucasvieira/workspace.json b/workspace.json similarity index 100% rename from lucasvieira/workspace.json rename to workspace.json