From 710ffa66253d2caf7ecdb1bd9a606c4d0af0a01f Mon Sep 17 00:00:00 2001 From: Charlie Greenman Date: Sun, 22 Jan 2023 20:55:29 +0200 Subject: [PATCH] first commit. --- .eslintignore | 3 + .eslintrc | 10 + .github/workflows/pr-devops.yml | 21 ++ .gitignore | 5 + .npmignore | 8 + .prettierrc | 6 + .vscode/settings.json | 8 + LICENSE.md | 21 ++ README.md | 102 +++++++ jest.config.js | 15 + package.json | 99 +++++++ src/index.ts | 27 ++ src/replace.spec.ts | 37 +++ src/replace.ts | 19 ++ src/rz/angular/add-sibling-html.ts | 22 ++ src/rz/angular/angular-effects.ts | 39 +++ .../component/component-effects.spec.ts | 35 +++ .../effects/component/component-effects.ts | 47 ++++ src/rz/angular/effects/component/index.ts | 0 .../effects/component/sample.module.ts | 7 + .../component/snapshots/index-output.ts.snap | 1 + .../snapshots/module-original.ts.snap | 7 + .../component/snapshots/module-output.ts.snap | 10 + .../angular/effects/graphql/graphql.spec.ts | 27 ++ src/rz/angular/effects/graphql/graphql.ts | 7 + src/rz/angular/effects/graphql/index.ts | 0 .../graphql/snapshots/index-output.ts.snap | 1 + src/rz/angular/effects/interface/index.ts | 0 .../effects/interface/interface.spec.ts | 26 ++ src/rz/angular/effects/interface/interface.ts | 7 + .../interface/snapshots/index-output.ts.snap | 1 + .../effects/ngrx/effects/ngrx-effects.spec.ts | 32 +++ .../effects/ngrx/effects/ngrx-effects.ts | 42 +++ .../test-ngrx-effects-output.module.ts.snap | 13 + .../test-ngrx-effects.module.ts.snap | 10 + .../effects/ngrx/effects/test.module.ts | 10 + .../effects/ngrx/facade/ngrx-facade.spec.ts | 32 +++ .../effects/ngrx/facade/ngrx-facade.ts | 36 +++ .../ngrx-facade-output.module.ts.snap | 11 + .../snapshots/ngrx-facade.module.ts.snap | 10 + .../effects/ngrx/facade/test.module.ts | 10 + .../effects/ngrx/reducer/ngrx-reducer.spec.ts | 33 +++ .../effects/ngrx/reducer/ngrx-reducer.ts | 37 +++ .../ngrx-reducer-output.module.ts.snap | 12 + .../snapshots/ngrx-reducer.module.ts.snap | 10 + .../effects/ngrx/reducer/test.module.ts | 10 + src/rz/angular/effects/service/index.ts | 0 .../angular/effects/service/service.spec.ts | 26 ++ src/rz/angular/effects/service/service.ts | 7 + .../service/snapshots/index-output.ts.snap | 1 + .../effects/standalone-component/index.ts | 0 .../snapshots/index-output.ts.snap | 1 + .../standalone-component.ts | 7 + .../standlone-component.spec.ts | 27 ++ src/rz/angular/index.ts | 2 + .../insert-into-html-tag.ts | 22 ++ .../angular/interfaces/edit-html.interface.ts | 15 + src/rz/angular/morph-angular-html.spec.ts | 66 +++++ src/rz/angular/morph-angular-html.ts | 59 ++++ .../data-table-output.component.html.snap | 76 ++++++ .../data-table-output.component.ts.snap | 0 .../snapshots/data-table.component.html.snap | 48 ++++ .../snapshots/data-table.component.ts.snap | 0 ...sert-into-html-tag-header-output.html.snap | 19 ++ .../insert-into-html-tag-header.html.snap | 1 + .../insert-into-html-tag.html-output.snap | 1 + .../insert-into-html-tag.html.snap | 1 + .../technologies/angular-technologies.ts | 12 + src/rz/angular/types/types.ts | 68 +++++ src/rz/angular/update-html-tag.ts | 12 + src/rz/global-variables/colors/colors.spec.ts | 22 ++ src/rz/global-variables/colors/colors.ts | 78 ++++++ src/rz/global-variables/index.ts | 8 + .../interfaces/global-variables.interface.ts | 10 + src/rz/global-variables/interfaces/index.ts | 1 + src/rz/global-variables/names.spec.ts | 39 +++ src/rz/global-variables/names.ts | 79 ++++++ .../variable-dependencies.ts | 141 ++++++++++ .../variables-to-ignore.ts | 116 ++++++++ src/rz/html/append-html/append-html.spec.ts | 42 +++ src/rz/html/append-html/append-html.ts | 21 ++ .../append-html-into-element-output.html.snap | 4 + .../append-html-into-element.html.snap | 3 + .../snapshots/append-html-output.html.snap | 2 + .../snapshots/append-html.html.snap | 1 + .../delete-html-element.spec.ts | 23 ++ .../delete-html-element.ts | 10 + .../delete-html-element-output.html.snap | 0 .../snapshots/delete-html-element.html.snap | 1 + src/rz/html/prepend-html/prepend-html.spec.ts | 42 +++ src/rz/html/prepend-html/prepend-html.ts | 21 ++ .../prepend-html-into-element-ouput.html.snap | 4 + .../prepend-html-into-element.html.snap | 3 + .../snapshots/prepend-html-output.html.snap | 2 + .../snapshots/prepend-html.html.snap | 1 + src/rz/json/edit-json/edit-json.spec.ts | 46 ++++ src/rz/json/edit-json/edit-json.ts | 31 +++ .../json/interfaces/json-morph.interface.ts | 10 + src/rz/json/json-morph.ts | 18 ++ .../nested-package-output.json.snap | 61 +++++ .../nested-package.json.snap | 59 ++++ .../package-json/package-output.json.snap | 57 ++++ .../snapshots/package-json/package.json.snap | 56 ++++ .../json/snapshots/project-output.json.snap | 100 +++++++ src/rz/json/snapshots/project.json.snap | 94 +++++++ .../morph/community-paths/community-paths.ts | 50 ++++ src/rz/morph/index.ts | 3 + src/rz/morph/interfaces/morph.interface.ts | 26 ++ src/rz/morph/morph.spec.ts | 155 +++++++++++ src/rz/morph/morph.ts | 58 ++++ src/rz/params/html-params/html-params.ts | 119 ++++++++ src/rz/params/index.ts | 1 + src/rz/params/json-params/json-params.ts | 52 ++++ src/rz/params/params.ts | 32 +++ src/rz/params/scss-params/scss-params.ts | 34 +++ .../typescript-params/typescript-params.ts | 256 ++++++++++++++++++ src/rz/react/index.ts | 1 + src/rz/react/types/react-types.ts | 22 ++ .../scss/interfaces/morph-scss.interface.ts | 10 + src/rz/scss/morph-scss.spec.ts | 47 ++++ src/rz/scss/morph-scss.ts | 60 ++++ .../add-import-output.scss.snap | 5 + .../add-import-output/add-import.scss.snap | 4 + src/rz/scss/snapshots/themes-output.scss.snap | 8 + src/rz/scss/snapshots/themes.scss.snap | 4 + .../add-class-method/add-class-method.spec.ts | 28 ++ .../add-class-method/add-class-method.ts | 19 ++ .../add-constructor-method.spec.ts | 45 +++ .../add-constructor-method.ts | 14 + ...-constructor-method-no-constructor.ts.snap | 10 + .../add-constructor-method-output.ts.snap | 13 + .../add-constructor-method.ts.snap | 13 + .../typescript/add-export/add-export.spec.ts | 26 ++ src/rz/typescript/add-export/add-export.ts | 10 + .../snapshots/add-export-output.ts.snap | 1 + .../add-export/snapshots/add-export.ts.snap | 0 .../add-function/add-function.spec.ts | 32 +++ .../typescript/add-function/add-function.ts | 13 + .../snapshots/add-function-output.ts.snap | 11 + .../snapshots/add-function.ts.snap | 2 + .../add-import-to-existing.spec.ts | 26 ++ .../add-import-to-existing.ts | 12 + .../add-variable-declaration-statement.ts | 13 + .../ngmodule-imports.spec.ts | 28 ++ .../add-ngmodule-import/ngmodule-imports.ts | 15 + .../add-ngmodule-item.spec.ts | 74 +++++ .../add-ngmodule-item/add-ngmodule-item.ts | 72 +++++ .../add-ngmodule-export-output.ts.snap | 9 + .../add-ngmodule-export.ts.snap | 8 + .../add-spec-ngmodule-spec-output.ts.snap | 25 ++ .../add-spec-ngmodule-spec.ts.snap | 24 ++ .../interfaces/edit-typescript.interface.ts | 14 + src/rz/typescript/morph-typescript.spec.ts | 77 ++++++ src/rz/typescript/morph-typescript.ts | 132 +++++++++ .../add-class-method-output.ts.snap | 16 ++ .../add-class-method/add-class-method.ts.snap | 12 + .../add-import-to-existing-output.ts.snap | 10 + .../add-import-to-existing.ts.snap | 10 + ...iable-declaration-statement-output.ts.snap | 20 ++ ...add-variable-declaration-statement.ts.snap | 18 ++ .../add-ngmodule-import-output-spec.ts.snap | 33 +++ .../add-ngmodule-import-spec.ts.snap | 31 +++ .../add-ngmodule-import-output.ts.snap | 35 +++ .../add-ngmodule-import.ts.snap | 33 +++ .../add-ngmodule-provider-output.ts.snap | 19 ++ .../add-ngmodule-provider.ts.snap | 8 + .../data-table-output.component.ts.snap | 27 ++ .../snapshots/data-table.component.ts.snap | 24 ++ .../edit-import/edit-import-output.ts.snap | 13 + .../snapshots/edit-import/edit-import.ts.snap | 13 + .../header-component-output.ts.snap | 25 ++ .../header-component/header-component.ts.snap | 24 ++ .../variable-statement-output-spec.snap.ts | 4 + .../variable-statement-spec.snap.ts | 3 + .../add-to-variable-object.spec.ts | 22 ++ .../variable-statement/variable-statement.ts | 18 ++ src/rz/utils/add-export/add-export.spec.ts | 32 +++ src/rz/utils/add-export/add-export.ts | 58 ++++ src/rz/utils/add-export/index.ts | 1 + .../determine-paramater.spec.ts | 30 ++ .../determine-parameter.ts | 12 + .../determine-type/determine-type.spec.ts | 30 ++ src/rz/utils/determine-type/determine-type.ts | 12 + src/rz/utils/directories/directories.spec.ts | 41 +++ src/rz/utils/directories/directories.ts | 34 +++ .../directories/test-directories/README.md | 1 + .../test-directory-one/.gitkeep | 0 .../test-directory-three/.gitkeep | 0 .../test-directory-two/.gitkeep | 0 src/rz/utils/find-module-file/app.module.ts | 0 .../find-module-file/find-module-file.spec.ts | 11 + .../find-module-file/find-module-file.ts | 14 + src/rz/utils/git-utils/git-utils.spec.ts | 15 + src/rz/utils/git-utils/git-utils.ts | 14 + src/rz/utils/index.ts | 8 + .../utils/interfaces/template-parameters.ts | 15 + src/rz/utils/package-json/package-json.ts | 91 +++++++ src/rz/utils/razroo-path/razroo-path.spec.ts | 21 ++ src/rz/utils/razroo-path/razroo-path.ts | 13 + .../replace-template-variables.spec.ts | 31 +++ .../replace-template-variables.ts | 7 + src/tsconfig.json | 16 ++ src/tsconfig.lib.json | 11 + src/tsconfig.spec.json | 21 ++ tsconfig.base.json | 19 ++ tsconfig.json | 71 +++++ 206 files changed, 5363 insertions(+) create mode 100644 .eslintignore create mode 100644 .eslintrc create mode 100644 .github/workflows/pr-devops.yml create mode 100644 .gitignore create mode 100644 .npmignore create mode 100644 .prettierrc create mode 100644 .vscode/settings.json create mode 100644 LICENSE.md create mode 100644 README.md create mode 100644 jest.config.js create mode 100644 package.json create mode 100644 src/index.ts create mode 100644 src/replace.spec.ts create mode 100644 src/replace.ts create mode 100644 src/rz/angular/add-sibling-html.ts create mode 100644 src/rz/angular/angular-effects.ts create mode 100644 src/rz/angular/effects/component/component-effects.spec.ts create mode 100644 src/rz/angular/effects/component/component-effects.ts create mode 100644 src/rz/angular/effects/component/index.ts create mode 100644 src/rz/angular/effects/component/sample.module.ts create mode 100644 src/rz/angular/effects/component/snapshots/index-output.ts.snap create mode 100644 src/rz/angular/effects/component/snapshots/module-original.ts.snap create mode 100644 src/rz/angular/effects/component/snapshots/module-output.ts.snap create mode 100644 src/rz/angular/effects/graphql/graphql.spec.ts create mode 100644 src/rz/angular/effects/graphql/graphql.ts create mode 100644 src/rz/angular/effects/graphql/index.ts create mode 100644 src/rz/angular/effects/graphql/snapshots/index-output.ts.snap create mode 100644 src/rz/angular/effects/interface/index.ts create mode 100644 src/rz/angular/effects/interface/interface.spec.ts create mode 100644 src/rz/angular/effects/interface/interface.ts create mode 100644 src/rz/angular/effects/interface/snapshots/index-output.ts.snap create mode 100644 src/rz/angular/effects/ngrx/effects/ngrx-effects.spec.ts create mode 100644 src/rz/angular/effects/ngrx/effects/ngrx-effects.ts create mode 100644 src/rz/angular/effects/ngrx/effects/snapshots/test-ngrx-effects-output.module.ts.snap create mode 100644 src/rz/angular/effects/ngrx/effects/snapshots/test-ngrx-effects.module.ts.snap create mode 100644 src/rz/angular/effects/ngrx/effects/test.module.ts create mode 100644 src/rz/angular/effects/ngrx/facade/ngrx-facade.spec.ts create mode 100644 src/rz/angular/effects/ngrx/facade/ngrx-facade.ts create mode 100644 src/rz/angular/effects/ngrx/facade/snapshots/ngrx-facade-output.module.ts.snap create mode 100644 src/rz/angular/effects/ngrx/facade/snapshots/ngrx-facade.module.ts.snap create mode 100644 src/rz/angular/effects/ngrx/facade/test.module.ts create mode 100644 src/rz/angular/effects/ngrx/reducer/ngrx-reducer.spec.ts create mode 100644 src/rz/angular/effects/ngrx/reducer/ngrx-reducer.ts create mode 100644 src/rz/angular/effects/ngrx/reducer/snapshots/ngrx-reducer-output.module.ts.snap create mode 100644 src/rz/angular/effects/ngrx/reducer/snapshots/ngrx-reducer.module.ts.snap create mode 100644 src/rz/angular/effects/ngrx/reducer/test.module.ts create mode 100644 src/rz/angular/effects/service/index.ts create mode 100644 src/rz/angular/effects/service/service.spec.ts create mode 100644 src/rz/angular/effects/service/service.ts create mode 100644 src/rz/angular/effects/service/snapshots/index-output.ts.snap create mode 100644 src/rz/angular/effects/standalone-component/index.ts create mode 100644 src/rz/angular/effects/standalone-component/snapshots/index-output.ts.snap create mode 100644 src/rz/angular/effects/standalone-component/standalone-component.ts create mode 100644 src/rz/angular/effects/standalone-component/standlone-component.spec.ts create mode 100644 src/rz/angular/index.ts create mode 100644 src/rz/angular/insert-into-html-tag/insert-into-html-tag.ts create mode 100644 src/rz/angular/interfaces/edit-html.interface.ts create mode 100644 src/rz/angular/morph-angular-html.spec.ts create mode 100644 src/rz/angular/morph-angular-html.ts create mode 100644 src/rz/angular/snapshots/data-table-output.component.html.snap create mode 100644 src/rz/angular/snapshots/data-table-output.component.ts.snap create mode 100644 src/rz/angular/snapshots/data-table.component.html.snap create mode 100644 src/rz/angular/snapshots/data-table.component.ts.snap create mode 100644 src/rz/angular/snapshots/insert-into-html-tag/insert-into-html-tag-header-output.html.snap create mode 100644 src/rz/angular/snapshots/insert-into-html-tag/insert-into-html-tag-header.html.snap create mode 100644 src/rz/angular/snapshots/insert-into-html-tag/insert-into-html-tag.html-output.snap create mode 100644 src/rz/angular/snapshots/insert-into-html-tag/insert-into-html-tag.html.snap create mode 100644 src/rz/angular/technologies/angular-technologies.ts create mode 100644 src/rz/angular/types/types.ts create mode 100644 src/rz/angular/update-html-tag.ts create mode 100644 src/rz/global-variables/colors/colors.spec.ts create mode 100644 src/rz/global-variables/colors/colors.ts create mode 100644 src/rz/global-variables/index.ts create mode 100644 src/rz/global-variables/interfaces/global-variables.interface.ts create mode 100644 src/rz/global-variables/interfaces/index.ts create mode 100644 src/rz/global-variables/names.spec.ts create mode 100644 src/rz/global-variables/names.ts create mode 100644 src/rz/global-variables/variable-dependencies/variable-dependencies.ts create mode 100644 src/rz/global-variables/variables-to-ignore/variables-to-ignore.ts create mode 100644 src/rz/html/append-html/append-html.spec.ts create mode 100644 src/rz/html/append-html/append-html.ts create mode 100644 src/rz/html/append-html/snapshots/append-html-into-element/append-html-into-element-output.html.snap create mode 100644 src/rz/html/append-html/snapshots/append-html-into-element/append-html-into-element.html.snap create mode 100644 src/rz/html/append-html/snapshots/append-html-output.html.snap create mode 100644 src/rz/html/append-html/snapshots/append-html.html.snap create mode 100644 src/rz/html/delete-html-element/delete-html-element.spec.ts create mode 100644 src/rz/html/delete-html-element/delete-html-element.ts create mode 100644 src/rz/html/delete-html-element/snapshots/delete-html-element-output.html.snap create mode 100644 src/rz/html/delete-html-element/snapshots/delete-html-element.html.snap create mode 100644 src/rz/html/prepend-html/prepend-html.spec.ts create mode 100644 src/rz/html/prepend-html/prepend-html.ts create mode 100644 src/rz/html/prepend-html/snapshots/prepend-html-into-element/prepend-html-into-element-ouput.html.snap create mode 100644 src/rz/html/prepend-html/snapshots/prepend-html-into-element/prepend-html-into-element.html.snap create mode 100644 src/rz/html/prepend-html/snapshots/prepend-html-output.html.snap create mode 100644 src/rz/html/prepend-html/snapshots/prepend-html.html.snap create mode 100644 src/rz/json/edit-json/edit-json.spec.ts create mode 100644 src/rz/json/edit-json/edit-json.ts create mode 100644 src/rz/json/interfaces/json-morph.interface.ts create mode 100644 src/rz/json/json-morph.ts create mode 100644 src/rz/json/snapshots/nested-package-json/nested-package-output.json.snap create mode 100644 src/rz/json/snapshots/nested-package-json/nested-package.json.snap create mode 100644 src/rz/json/snapshots/package-json/package-output.json.snap create mode 100644 src/rz/json/snapshots/package-json/package.json.snap create mode 100644 src/rz/json/snapshots/project-output.json.snap create mode 100644 src/rz/json/snapshots/project.json.snap create mode 100644 src/rz/morph/community-paths/community-paths.ts create mode 100644 src/rz/morph/index.ts create mode 100644 src/rz/morph/interfaces/morph.interface.ts create mode 100644 src/rz/morph/morph.spec.ts create mode 100644 src/rz/morph/morph.ts create mode 100644 src/rz/params/html-params/html-params.ts create mode 100644 src/rz/params/index.ts create mode 100644 src/rz/params/json-params/json-params.ts create mode 100644 src/rz/params/params.ts create mode 100644 src/rz/params/scss-params/scss-params.ts create mode 100644 src/rz/params/typescript-params/typescript-params.ts create mode 100644 src/rz/react/index.ts create mode 100644 src/rz/react/types/react-types.ts create mode 100644 src/rz/scss/interfaces/morph-scss.interface.ts create mode 100644 src/rz/scss/morph-scss.spec.ts create mode 100644 src/rz/scss/morph-scss.ts create mode 100644 src/rz/scss/snapshots/add-import-output/add-import-output.scss.snap create mode 100644 src/rz/scss/snapshots/add-import-output/add-import.scss.snap create mode 100644 src/rz/scss/snapshots/themes-output.scss.snap create mode 100644 src/rz/scss/snapshots/themes.scss.snap create mode 100644 src/rz/typescript/add-class-method/add-class-method.spec.ts create mode 100644 src/rz/typescript/add-class-method/add-class-method.ts create mode 100644 src/rz/typescript/add-constructor-method/add-constructor-method.spec.ts create mode 100644 src/rz/typescript/add-constructor-method/add-constructor-method.ts create mode 100644 src/rz/typescript/add-constructor-method/snapshots/add-constructor-method/add-constructor-method-no-constructor.ts.snap create mode 100644 src/rz/typescript/add-constructor-method/snapshots/add-constructor-method/add-constructor-method-output.ts.snap create mode 100644 src/rz/typescript/add-constructor-method/snapshots/add-constructor-method/add-constructor-method.ts.snap create mode 100644 src/rz/typescript/add-export/add-export.spec.ts create mode 100644 src/rz/typescript/add-export/add-export.ts create mode 100644 src/rz/typescript/add-export/snapshots/add-export-output.ts.snap create mode 100644 src/rz/typescript/add-export/snapshots/add-export.ts.snap create mode 100644 src/rz/typescript/add-function/add-function.spec.ts create mode 100644 src/rz/typescript/add-function/add-function.ts create mode 100644 src/rz/typescript/add-function/snapshots/add-function-output.ts.snap create mode 100644 src/rz/typescript/add-function/snapshots/add-function.ts.snap create mode 100644 src/rz/typescript/add-import-to-existing/add-import-to-existing.spec.ts create mode 100644 src/rz/typescript/add-import-to-existing/add-import-to-existing.ts create mode 100644 src/rz/typescript/add-variable-declaration-statement/add-variable-declaration-statement.ts create mode 100644 src/rz/typescript/angular-typescript/add-ngmodule-import/ngmodule-imports.spec.ts create mode 100644 src/rz/typescript/angular-typescript/add-ngmodule-import/ngmodule-imports.ts create mode 100644 src/rz/typescript/angular-typescript/add-ngmodule-item/add-ngmodule-item.spec.ts create mode 100644 src/rz/typescript/angular-typescript/add-ngmodule-item/add-ngmodule-item.ts create mode 100644 src/rz/typescript/angular-typescript/snapshots/add-ngmodule-export/add-ngmodule-export-output.ts.snap create mode 100644 src/rz/typescript/angular-typescript/snapshots/add-ngmodule-export/add-ngmodule-export.ts.snap create mode 100644 src/rz/typescript/angular-typescript/snapshots/add-spec-ngmodule-spec/add-spec-ngmodule-spec-output.ts.snap create mode 100644 src/rz/typescript/angular-typescript/snapshots/add-spec-ngmodule-spec/add-spec-ngmodule-spec.ts.snap create mode 100644 src/rz/typescript/interfaces/edit-typescript.interface.ts create mode 100644 src/rz/typescript/morph-typescript.spec.ts create mode 100644 src/rz/typescript/morph-typescript.ts create mode 100644 src/rz/typescript/snapshots/add-class-method/add-class-method-output.ts.snap create mode 100644 src/rz/typescript/snapshots/add-class-method/add-class-method.ts.snap create mode 100644 src/rz/typescript/snapshots/add-import-to-existing/add-import-to-existing-output.ts.snap create mode 100644 src/rz/typescript/snapshots/add-import-to-existing/add-import-to-existing.ts.snap create mode 100644 src/rz/typescript/snapshots/add-variable-declaration-statement/add-variable-declaration-statement-output.ts.snap create mode 100644 src/rz/typescript/snapshots/add-variable-declaration-statement/add-variable-declaration-statement.ts.snap create mode 100644 src/rz/typescript/snapshots/angular-typescript/add-ngmodule-import-spec/add-ngmodule-import-output-spec.ts.snap create mode 100644 src/rz/typescript/snapshots/angular-typescript/add-ngmodule-import-spec/add-ngmodule-import-spec.ts.snap create mode 100644 src/rz/typescript/snapshots/angular-typescript/add-ngmodule-import/add-ngmodule-import-output.ts.snap create mode 100644 src/rz/typescript/snapshots/angular-typescript/add-ngmodule-import/add-ngmodule-import.ts.snap create mode 100644 src/rz/typescript/snapshots/angular-typescript/add-ngmodule-provider/add-ngmodule-provider-output.ts.snap create mode 100644 src/rz/typescript/snapshots/angular-typescript/add-ngmodule-provider/add-ngmodule-provider.ts.snap create mode 100644 src/rz/typescript/snapshots/data-table-output.component.ts.snap create mode 100644 src/rz/typescript/snapshots/data-table.component.ts.snap create mode 100644 src/rz/typescript/snapshots/edit-import/edit-import-output.ts.snap create mode 100644 src/rz/typescript/snapshots/edit-import/edit-import.ts.snap create mode 100644 src/rz/typescript/snapshots/header-component/header-component-output.ts.snap create mode 100644 src/rz/typescript/snapshots/header-component/header-component.ts.snap create mode 100644 src/rz/typescript/snapshots/variable-statement/variable-statement-output-spec.snap.ts create mode 100644 src/rz/typescript/snapshots/variable-statement/variable-statement-spec.snap.ts create mode 100644 src/rz/typescript/variable-statement/add-to-variable-object.spec.ts create mode 100644 src/rz/typescript/variable-statement/variable-statement.ts create mode 100644 src/rz/utils/add-export/add-export.spec.ts create mode 100644 src/rz/utils/add-export/add-export.ts create mode 100644 src/rz/utils/add-export/index.ts create mode 100644 src/rz/utils/determine-parameter/determine-paramater.spec.ts create mode 100644 src/rz/utils/determine-parameter/determine-parameter.ts create mode 100644 src/rz/utils/determine-type/determine-type.spec.ts create mode 100644 src/rz/utils/determine-type/determine-type.ts create mode 100644 src/rz/utils/directories/directories.spec.ts create mode 100644 src/rz/utils/directories/directories.ts create mode 100644 src/rz/utils/directories/test-directories/README.md create mode 100644 src/rz/utils/directories/test-directories/test-directory-one/.gitkeep create mode 100644 src/rz/utils/directories/test-directories/test-directory-one/test-directory-three/.gitkeep create mode 100644 src/rz/utils/directories/test-directories/test-directory-two/.gitkeep create mode 100644 src/rz/utils/find-module-file/app.module.ts create mode 100644 src/rz/utils/find-module-file/find-module-file.spec.ts create mode 100644 src/rz/utils/find-module-file/find-module-file.ts create mode 100644 src/rz/utils/git-utils/git-utils.spec.ts create mode 100644 src/rz/utils/git-utils/git-utils.ts create mode 100644 src/rz/utils/index.ts create mode 100644 src/rz/utils/interfaces/template-parameters.ts create mode 100644 src/rz/utils/package-json/package-json.ts create mode 100644 src/rz/utils/razroo-path/razroo-path.spec.ts create mode 100644 src/rz/utils/razroo-path/razroo-path.ts create mode 100644 src/rz/utils/replace-template-variables/replace-template-variables.spec.ts create mode 100644 src/rz/utils/replace-template-variables/replace-template-variables.ts create mode 100644 src/tsconfig.json create mode 100644 src/tsconfig.lib.json create mode 100644 src/tsconfig.spec.json create mode 100644 tsconfig.base.json create mode 100644 tsconfig.json diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..5a19e8a --- /dev/null +++ b/.eslintignore @@ -0,0 +1,3 @@ +node_modules +dist +coverage \ No newline at end of file diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..d679ba6 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,10 @@ +{ + "root": true, + "parser": "@typescript-eslint/parser", + "plugins": ["@typescript-eslint", "jest"], + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:jest/recommended" + ] +} diff --git a/.github/workflows/pr-devops.yml b/.github/workflows/pr-devops.yml new file mode 100644 index 0000000..e2de146 --- /dev/null +++ b/.github/workflows/pr-devops.yml @@ -0,0 +1,21 @@ +name: Run Unit Tests + +on: + pull_request: + branches: [ main ] + workflow_dispatch: +jobs: + DevOpsPRUnitTests: + runs-on: ubuntu-20.04 + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Login to DockerHub Registry + run: echo ${{ secrets.DOCKERHUB_PASSWORD }} | docker login -u ${{ secrets.DOCKERHUB_USERNAME }} --password-stdin + - name: Build Private Dependencies And Test + run: | + sudo apt install -y jq + echo "//npm.pkg.github.com/:_authToken=${{ secrets.GitHubToken }}" >> ~/.npmrc + npm install + npm run build + npm test -- -u \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fbf86d7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +node_modules +dist +lib +.env +.DS_Store \ No newline at end of file diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..15909e2 --- /dev/null +++ b/.npmignore @@ -0,0 +1,8 @@ +.vscode +node_modules +.env +.idea +lib +build +package-lock.json +yarn-lock diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..e6936ab --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "printWidth": 120, + "trailingComma": "all", + "singleQuote": true, + "tabWidth": 2 +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..982c261 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "terminal.integrated.shellArgs.linux": [ + "-i" + ], + "terminal.integrated.shellArgs.windows": [ + "-NoProfile" + ] +} diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..dce86d2 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2013-present Pagar.me Pagamentos S/A + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..e6c73c4 --- /dev/null +++ b/README.md @@ -0,0 +1,102 @@ +# Razzle +Razzle is an extensible, easy to understand, easy contribute, easy to use Codemod library. Razzle-dazzle your codebase like you've never before. + +# Novelty behind Razzle library + +What the Razzle library does that is unique, is that it creates a unified interface across different programming languages. It uses a simple object to make modifications. e.g. + +### Typescript +``` +{ + "import": '{ NgModule }', + "path": '@angular/core' +} +``` + +### SCSS +``` +{ + "import": '', + "path": 'libs/common/styles/razroo-styles.scss' +} +``` + +## Assumption Made +Razzle is always used in an editing circumstance. There is no need to speicify that file is being editied. Razzle will update the string + +## Benefits of such +1. This will be consumer facing. By allowing a singular object, we can simplify the frontend shown to users in the content management system. +2. Developers don't have to dig into specifics for each programming language. They can simply keep an eye on the top level interface. + +# Further reading via Razroo blog(soon to be it's own documentation site) + +blog.razroo.com + + +# APIS Available + +## Typescript + +1. `import` +2. `editImport` +3. `classDeclaration` +4. `classMethod` +5. `addNgModuleImport` +6. `addNgModuleProvider` +7. `addNgModuleImportToSpec` +8. `addToVariableObject` +9. `addConstructorMethod` +``` +{ + nodeType: 'addConstructorMethod', + codeBlock: 'userService', + type: 'UserService' +} +``` +10. addVariableDeclarationStatement +``` +{ + nodeType: 'addVariableDeclarationStatement', + codeBlock: 'gtag', + type: 'Function' +} +``` + +## JSON + +1. `editJson` +2. `addJsonKeyValue` + +## SCSS + +1. `addScssBlock` +2. `import` + + +## Angular HTML + HTML + +1. `editHtmlTag` +``` +{ + "nodeType": "editHtmlTag", + "codeBlock": "matSort", + "tagNameToModify": "mat-table" +} +``` +2. `addSiblingHtml` +``` +{ + "nodeType": "addSiblingHtml", + "codeBlock": "", + "tagNameToInsert": "script", + "siblingTagName": "link" +}, +``` +3. `insertIntoHtmlTag` +``` +{ + "nodeType": "insertIntoHtmlTag", + "codeBlock": "
", + "tagNameToInsertInto": "mat-toolbar" +} +``` diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..beeab6b --- /dev/null +++ b/jest.config.js @@ -0,0 +1,15 @@ +module.exports = { + preset: 'ts-jest/presets/default-esm', + testEnvironment: 'node', + transform: { + '^.+\\.(t|j)s$': 'ts-jest' + }, + globals: { + 'ts-jest': { + diagnostics: false, + useESM: true + } + }, + testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.(t|j)s$', + moduleFileExtensions: ['ts', 'js', 'json', 'node'], +}; diff --git a/package.json b/package.json new file mode 100644 index 0000000..cafc129 --- /dev/null +++ b/package.json @@ -0,0 +1,99 @@ +{ + "name": "@razroo/razzle", + "version": "1.0.0", + "description": "Razzle is an extensible, easy to understand, easy contribute, easy to use Codemod library.", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "scripts": { + "build": "tsc", + "format": "prettier --write \"src/**/*.(js|ts)\"", + "lint": "eslint src --ext .js,.ts", + "lint:fix": "eslint src --fix --ext .js,.ts", + "test": "jest --config jest.config.js", + "publish": "npm publish" + }, + "publishConfig": { + "registry": "https://npm.pkg.github.com" + }, + "repository": { + "type": "git", + "url": "https://github.com/razroo/razzle" + }, + "keywords": [ + "razroo", + "codemod" + ], + "author": "Razroo", + "license": "RAZROO", + "bugs": { + "url": "https://github.com/razroo/razzle/issues" + }, + "homepage": "https://github.com/razroo/razzle#readme", + "devDependencies": { + "@angular-devkit/build-angular": "~13.0.0", + "@angular-devkit/build-optimizer": "~0.1300.0", + "@angular-devkit/build-webpack": "~0.1300.0", + "@angular-devkit/schematics": "~13.0.0", + "@angular-eslint/eslint-plugin": "~13.0.1", + "@angular-eslint/eslint-plugin-template": "~13.0.1", + "@angular-eslint/template-parser": "~13.0.1", + "@angular/cli": "~13.0.0", + "@angular/common": "^13.0.0", + "@angular/compiler": "^13.0.0", + "@angular/compiler-cli": "^13.0.0", + "@angular/core": "^13.0.0", + "@angular/forms": "^13.0.0", + "@angular/platform-browser": "^13.0.0", + "@angular/platform-browser-dynamic": "^13.0.0", + "@angular/router": "^13.0.0", + "@angular/service-worker": "^13.0.0", + "@angular/upgrade": "^13.0.0", + "@angular-devkit/architect": "~0.1300.0", + "@angular-devkit/core": "~13.0.0", + "@types/jest": "^27.5.2", + "@types/tinycolor2": "^1.4.3", + "@typescript-eslint/eslint-plugin": "^4.5.0", + "@typescript-eslint/parser": "^4.5.0", + "babel-jest": "^27.4.6", + "eslint": "^7.11.0", + "eslint-plugin-jest": "^24.1.0", + "jest": "^27.0.7", + "ts-jest": "^27.1.5", + "tsup": "^6.2.3", + "typescript": "^4.0.3", + "yargs-parser": "20.0.0" + }, + "dependencies": { + "@ctrl/tinycolor": "^3.4.1", + "chalk": "4.1.0", + "ejs": "^3.1.5", + "enquirer": "~2.3.6", + "fast-glob": "3.2.7", + "glob": "^8.0.3", + "hast-util-from-parse5": "6.0.1", + "hast-util-to-html": "7.1.3", + "ignore": "^5.0.4", + "json-pointer": "^0.6.2", + "json-to-ast": "^2.1.0", + "jsonc-parser": "3.0.0", + "jsonpath-plus": "^6.0.1", + "parse5": "^6.0.1", + "path": "^0.12.7", + "prettier": "^2.3.0", + "query-ast": "^1.0.4", + "rxjs": "^6.5.4", + "rxjs-for-await": "0.0.2", + "scss-parser": "^1.0.5", + "semver": "7.3.4", + "tmp": "~0.2.1", + "to-vfile": "^7.2.3", + "ts-morph": "^15.1.0", + "tslib": "^2.3.0", + "unist-util-visit": "2.0.3", + "unist-util-visit-parents": "3.1.1", + "yargs-parser": "20.0.0" + }, + "files": [ + "dist/**/*" + ] +} diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..eaec4b8 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,27 @@ +// color related items +export { getPalette, createColorVariables } from './rz/global-variables'; + +// export { Blah as default } from './lib' +export { replaceTagParameters, replaceCurlyBrace } from './replace'; + +// general utils +export { getVersionAndNameString, determineType, determineFilePathParameter, readPackageJson, extractProjectName, + getAllDirectoriesFromVsCodeFolder, getProjectDependencies, determineLanguagesUsed, replaceCodeModEditsTemplateVariables } from './rz/utils'; + +// export typescript related edits +export { EditInput, EditFile, morphCode, effects, communityPaths, types } from './rz/morph'; + +// export typescript related edits +export { getCodeModParams, getCodeModOptions } from './rz/params'; + +// global variables +export { names, codeCmsVariablesToIgnore, powerUpVariables, powerUpVariablesFlatData } from './rz/global-variables'; + +// global variables interfaces +export { CodeCmsVariablesToIgnore, PowerUpVariables } from './rz/global-variables/interfaces'; + +// types for angular code generation side-effects +export { angularTypes, AngularTypeNames, defaultAngularTechnologies } from './rz/angular'; + +// types react code generation side-effects +export { reactTypes, ReactTypeNames } from './rz/react'; \ No newline at end of file diff --git a/src/replace.spec.ts b/src/replace.spec.ts new file mode 100644 index 0000000..7facb55 --- /dev/null +++ b/src/replace.spec.ts @@ -0,0 +1,37 @@ +import { replaceCurlyBrace } from './replace'; + +describe('replaceCurlyBrace', () => { + it('should replaceCurlyBrace with respective parameters', () => { + const mockParameters = { + name: 'test', + selector: 'test-two', + className: '' + }; + const mockFileStringWithCurlyBrace = 'src/{selector}/{name}.component.html'; + + const result = replaceCurlyBrace(mockParameters, mockFileStringWithCurlyBrace); + const expected = 'src/test-two/test.component.html'; + + expect(result).toEqual(expected); + }); + + it('should replace with curly braces if curly brace values shows up more than once', () => { + const mockParameters = { + nameFilePath:"libs/common/ui/src/lib/{name}-dialog", + name:"blue", + projectName:"razroo-angular-starter", + className:"Blue", + propertyName:"blue", + constantName:"BLUE", + fileName:"blue", + titleName:"Blue" + }; + const mockFileStringWithCurlyBrace = '{nameFilePath}/{name}-dialog.component.ts'; + + const result = replaceCurlyBrace(mockParameters, mockFileStringWithCurlyBrace); + const expected = 'libs/common/ui/src/lib/blue-dialog/blue-dialog.component.ts'; + + expect(result).toEqual(expected); + }) + +}); \ No newline at end of file diff --git a/src/replace.ts b/src/replace.ts new file mode 100644 index 0000000..2c64d24 --- /dev/null +++ b/src/replace.ts @@ -0,0 +1,19 @@ +export const replaceTagParameters = ( + parameters: Record, + toReplace: string + ): string => { + for (const [key, value] of Object.entries(parameters)) { + const keyString = `<%= ${key} %>`; + const keyStringRegex = new RegExp(keyString, 'g'); + toReplace = toReplace.replace(keyStringRegex, value); + } + return toReplace; + }; + + export const replaceCurlyBrace = (mockParameters: object, mockFileStringWithCurlyBrace: string): string => { + let result = mockFileStringWithCurlyBrace; + for(let key in mockParameters) { + result = result.replaceAll('{' + key + '}', mockParameters[key]); + } + return result; +} \ No newline at end of file diff --git a/src/rz/angular/add-sibling-html.ts b/src/rz/angular/add-sibling-html.ts new file mode 100644 index 0000000..12ecebe --- /dev/null +++ b/src/rz/angular/add-sibling-html.ts @@ -0,0 +1,22 @@ +import visitParents = require('unist-util-visit-parents'); +import visit = require('unist-util-visit'); +import { createUnifiedTree } from './morph-angular-html'; +import { EditHtmlFile } from './interfaces/edit-html.interface'; + +// takes html tree of code we want to insert +// and combines with html tree we want to insert into +function combineTrees(fileToModifyTree: any, codeToAddTree: any, tagNameToInsert: string, siblingTagName: string ) { + visit(codeToAddTree, {type: 'element', tagName: tagNameToInsert}, (matPaginator: any) => { + visitParents(fileToModifyTree, {type: 'element', tagName: siblingTagName}, (_node, ancestors) => { + (ancestors[ancestors.length -1] as any).children.push(matPaginator); + }); + }); + return fileToModifyTree; +} + + // this is a unique function + // eslint-disable-next-line @typescript-eslint/no-explicit-any +export function insertCodeAfterElement(editFile: EditHtmlFile, fileToBeAddedToTree: any): any { + const stringToAddTree = createUnifiedTree(editFile.codeBlock as string); + return combineTrees(fileToBeAddedToTree, stringToAddTree, editFile.tagNameToInsert, editFile.siblingTagName); +} \ No newline at end of file diff --git a/src/rz/angular/angular-effects.ts b/src/rz/angular/angular-effects.ts new file mode 100644 index 0000000..2210943 --- /dev/null +++ b/src/rz/angular/angular-effects.ts @@ -0,0 +1,39 @@ +import { Parameters } from "../morph"; +import { addClassToDeclarationsAndImports } from "./effects/component/component-effects"; +import { exportGraphqlFile } from "./effects/graphql/graphql"; +import { exportInterfaceFile } from "./effects/interface/interface"; +import { addEffectToNgModule } from "./effects/ngrx/effects/ngrx-effects"; +import { addFacadeToNgModule } from "./effects/ngrx/facade/ngrx-facade"; +import { addReducerToNgModule } from "./effects/ngrx/reducer/ngrx-reducer"; +import { exportServiceFile } from "./effects/service/service"; +import { exportComponentFile } from "./effects/standalone-component/standalone-component"; +import { AngularType, AngularTypeNames, AngularOptionalType } from "./types/types"; + +export function angularEffects(filePathWithName: string, type: AngularTypeNames, parameters: Parameters, optionalTypes: AngularOptionalType[]): void { + switch (type) { + case AngularTypeNames.Component: + addClassToDeclarationsAndImports(filePathWithName, parameters.className, optionalTypes); + break; + case AngularTypeNames.StandaloneComponent: + exportComponentFile(filePathWithName) + break; + case AngularTypeNames.Service: + exportServiceFile(filePathWithName); + break; + case AngularTypeNames.Interface: + exportInterfaceFile(filePathWithName) + break; + case AngularTypeNames.Graphql: + exportGraphqlFile(filePathWithName) + break; + case AngularTypeNames.NgrxEffects: + addEffectToNgModule(filePathWithName, parameters.className) + break; + case AngularTypeNames.NgrxFacade: + addFacadeToNgModule(filePathWithName, parameters.className) + break; + case AngularTypeNames.NgrxReducer: + addReducerToNgModule(filePathWithName, parameters.className, parameters.constantName) + break; + } +} \ No newline at end of file diff --git a/src/rz/angular/effects/component/component-effects.spec.ts b/src/rz/angular/effects/component/component-effects.spec.ts new file mode 100644 index 0000000..419c181 --- /dev/null +++ b/src/rz/angular/effects/component/component-effects.spec.ts @@ -0,0 +1,35 @@ +import { readFileSync, writeFileSync } from 'fs'; +import { TemplateInputParameter } from '../../../utils/interfaces/template-parameters'; +import { effects, Parameters } from "../../../morph"; + +describe('Angular Component Effects', () => { + afterEach(() => { + const moduleOriginal = readFileSync('src/rz/angular/effects/component/snapshots/module-original.ts.snap').toString(); + writeFileSync('src/rz/angular/effects/component/index.ts', ''); + writeFileSync('src/rz/angular/effects/component/sample.module.ts', moduleOriginal); + }); + it('should update the closest app module file with an ngmodule export, declaration, import component, and export via optional type', () => { + const mockFilePath = 'src/rz/angular/effects/component/sample.component.ts'; + const parameters: Parameters = { + className: 'Sample' + }; + const mockTemplateInputParameter: TemplateInputParameter = { + defaultValue: 'libs/{name}-dialog', + description: 'File path for name file(s)', + inputType: 'text', + name: 'nameFilePath', + optionalTypes: [{name: 'isExported', selected: true}], + paramType: 'filePath', + type: 'component' + } + effects(mockFilePath, mockTemplateInputParameter, 'angular', JSON.stringify(parameters)); + + const indexResult = readFileSync('src/rz/angular/effects/component/index.ts').toString(); + const indexExpected = readFileSync('src/rz/angular/effects/component/snapshots/index-output.ts.snap').toString(); + expect(indexResult).toEqual(indexExpected); + + const moduleResult = readFileSync('src/rz/angular/effects/component/sample.module.ts').toString(); + const moduleExpected = readFileSync('src/rz/angular/effects/component/snapshots/module-output.ts.snap').toString(); + expect(moduleResult).toEqual(moduleExpected); + }) +}); \ No newline at end of file diff --git a/src/rz/angular/effects/component/component-effects.ts b/src/rz/angular/effects/component/component-effects.ts new file mode 100644 index 0000000..09c4385 --- /dev/null +++ b/src/rz/angular/effects/component/component-effects.ts @@ -0,0 +1,47 @@ +import { EditInput } from 'src/rz/morph/interfaces/morph.interface'; +import { readFileSync, writeFileSync } from 'fs'; +import { findClosestModuleFile } from "../../../utils"; +import { createRelativePath, exportTsFiles, isTsFile } from "../../../utils/add-export"; +import { morphTypescript } from '../../../typescript/morph-typescript'; +import { GlobalAngularOptionNames, AngularOptionalType } from '../../types/types'; +import { names } from '../../../global-variables'; + +export function addClassToDeclarationsAndImports(filePathWithName: string, className: string, optionalTypes: AngularOptionalType[]): void { + if(isTsFile(filePathWithName)) { + const moduleTsFile = findClosestModuleFile(filePathWithName); + const componentFileName = filePathWithName.split('/').pop(); + const componentName = componentFileName.split('.')[0]; + const componentClassName = `${names(componentName).className}Component`; + const importPath = createRelativePath(filePathWithName, moduleTsFile); + const fileName = moduleTsFile.split('/').pop(); + const fileToBeAddedTo = readFileSync(moduleTsFile, 'utf-8').toString(); + // const componentName = `${className}Component`; + const editInput: EditInput = { + fileType: 'ts', + fileName: fileName, + filePath: moduleTsFile, + fileToBeAddedTo: fileToBeAddedTo, + edits: [ + { + nodeType: 'addNgModuleExport', + codeBlock: componentClassName, + }, + { + nodeType: 'addNgModuleDeclaration', + codeBlock: componentClassName, + }, + { + nodeType: 'import', + codeBlock: `{ ${componentClassName} }`, + path: importPath, + } + ] + }; + if(optionalTypes?.find(option => option.name === GlobalAngularOptionNames.IsExported).selected){ + exportTsFiles(filePathWithName); + } + const updatedFileToBeAddedTo = morphTypescript(editInput); + // TODO extract write file logic from morphCode logic + writeFileSync(moduleTsFile, updatedFileToBeAddedTo); + } + } \ No newline at end of file diff --git a/src/rz/angular/effects/component/index.ts b/src/rz/angular/effects/component/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/rz/angular/effects/component/sample.module.ts b/src/rz/angular/effects/component/sample.module.ts new file mode 100644 index 0000000..de31813 --- /dev/null +++ b/src/rz/angular/effects/component/sample.module.ts @@ -0,0 +1,7 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +@NgModule({ + imports: [CommonModule], +}) +export class CommonUiModule {} diff --git a/src/rz/angular/effects/component/snapshots/index-output.ts.snap b/src/rz/angular/effects/component/snapshots/index-output.ts.snap new file mode 100644 index 0000000..7f06fa2 --- /dev/null +++ b/src/rz/angular/effects/component/snapshots/index-output.ts.snap @@ -0,0 +1 @@ +export * from "./sample.component"; diff --git a/src/rz/angular/effects/component/snapshots/module-original.ts.snap b/src/rz/angular/effects/component/snapshots/module-original.ts.snap new file mode 100644 index 0000000..de31813 --- /dev/null +++ b/src/rz/angular/effects/component/snapshots/module-original.ts.snap @@ -0,0 +1,7 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +@NgModule({ + imports: [CommonModule], +}) +export class CommonUiModule {} diff --git a/src/rz/angular/effects/component/snapshots/module-output.ts.snap b/src/rz/angular/effects/component/snapshots/module-output.ts.snap new file mode 100644 index 0000000..bb9ac1f --- /dev/null +++ b/src/rz/angular/effects/component/snapshots/module-output.ts.snap @@ -0,0 +1,10 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { SampleComponent } from "./sample.component"; + +@NgModule({ + imports: [CommonModule], + exports: [SampleComponent], + declarations: [SampleComponent] +}) +export class CommonUiModule { } diff --git a/src/rz/angular/effects/graphql/graphql.spec.ts b/src/rz/angular/effects/graphql/graphql.spec.ts new file mode 100644 index 0000000..4997120 --- /dev/null +++ b/src/rz/angular/effects/graphql/graphql.spec.ts @@ -0,0 +1,27 @@ +import { TemplateInputParameter } from './../../../utils/interfaces/template-parameters'; +import { writeFileSync, readFileSync } from 'fs'; +import { effects } from "../../../morph"; +import { AngularTypeNames } from "../../types/types"; + +describe('exportGraphqlFile', () => { + afterEach(() => { + writeFileSync('src/rz/angular/effects/graphql/index.ts', ''); + }); + it('should export graphql file', () => { + const mockFilePath = 'src/rz/angular/effects/graphql/user.mutations.ts'; + const mockTemplateInputParameter: TemplateInputParameter = { + defaultValue: 'libs/{name}-dialog', + description: 'File path for name file(s)', + inputType: 'text', + name: 'nameFilePath', + optionalTypes: [{name: 'isExported', selected: true}], + paramType: 'filePath', + type: AngularTypeNames.Graphql + }; + + effects(mockFilePath, mockTemplateInputParameter, 'angular'); + const result = readFileSync('src/rz/angular/effects/graphql/index.ts').toString(); + const expected = readFileSync('src/rz/angular/effects/graphql/snapshots/index-output.ts.snap').toString(); + expect(result).toEqual(expected); + }); +}); \ No newline at end of file diff --git a/src/rz/angular/effects/graphql/graphql.ts b/src/rz/angular/effects/graphql/graphql.ts new file mode 100644 index 0000000..774f592 --- /dev/null +++ b/src/rz/angular/effects/graphql/graphql.ts @@ -0,0 +1,7 @@ +import { exportTsFiles, isTsFile } from "../../../utils/add-export"; + +export function exportGraphqlFile(filePathWithName: string): void { + if (isTsFile(filePathWithName)) { + exportTsFiles(filePathWithName); + } +} \ No newline at end of file diff --git a/src/rz/angular/effects/graphql/index.ts b/src/rz/angular/effects/graphql/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/rz/angular/effects/graphql/snapshots/index-output.ts.snap b/src/rz/angular/effects/graphql/snapshots/index-output.ts.snap new file mode 100644 index 0000000..32fe8b9 --- /dev/null +++ b/src/rz/angular/effects/graphql/snapshots/index-output.ts.snap @@ -0,0 +1 @@ +export * from "./user.mutations"; diff --git a/src/rz/angular/effects/interface/index.ts b/src/rz/angular/effects/interface/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/rz/angular/effects/interface/interface.spec.ts b/src/rz/angular/effects/interface/interface.spec.ts new file mode 100644 index 0000000..9fda629 --- /dev/null +++ b/src/rz/angular/effects/interface/interface.spec.ts @@ -0,0 +1,26 @@ +import { TemplateInputParameter } from './../../../utils/interfaces/template-parameters'; +import { writeFileSync, readFileSync } from 'fs'; +import { effects } from "../../../morph"; +import { AngularTypeNames } from "../../types/types"; + +describe('exportInterfaceFile', () => { + afterEach(() => { + writeFileSync('src/rz/angular/effects/interface/index.ts', ''); + }); + it('should export service file', () => { + const mockFilePath = 'src/rz/angular/effects/interface/user.interface.ts'; + const mockTemplateInputParameter: TemplateInputParameter = { + defaultValue: 'libs/{name}-dialog', + description: 'File path for name file(s)', + inputType: 'text', + name: 'nameFilePath', + optionalTypes: [{name: 'isExported', selected: true}], + paramType: 'filePath', + type: AngularTypeNames.Service + }; + effects(mockFilePath, mockTemplateInputParameter, 'angular'); + const result = readFileSync('src/rz/angular/effects/interface/index.ts').toString(); + const expected = readFileSync('src/rz/angular/effects/interface/snapshots/index-output.ts.snap').toString(); + expect(result).toEqual(expected); + }); +}); \ No newline at end of file diff --git a/src/rz/angular/effects/interface/interface.ts b/src/rz/angular/effects/interface/interface.ts new file mode 100644 index 0000000..49452d2 --- /dev/null +++ b/src/rz/angular/effects/interface/interface.ts @@ -0,0 +1,7 @@ +import { exportTsFiles, isTsFile } from "../../../utils/add-export"; + +export function exportInterfaceFile(filePathWithName: string): void { + if (isTsFile(filePathWithName)) { + exportTsFiles(filePathWithName); + } +} \ No newline at end of file diff --git a/src/rz/angular/effects/interface/snapshots/index-output.ts.snap b/src/rz/angular/effects/interface/snapshots/index-output.ts.snap new file mode 100644 index 0000000..c17f532 --- /dev/null +++ b/src/rz/angular/effects/interface/snapshots/index-output.ts.snap @@ -0,0 +1 @@ +export * from "./user.interface"; diff --git a/src/rz/angular/effects/ngrx/effects/ngrx-effects.spec.ts b/src/rz/angular/effects/ngrx/effects/ngrx-effects.spec.ts new file mode 100644 index 0000000..d0b2874 --- /dev/null +++ b/src/rz/angular/effects/ngrx/effects/ngrx-effects.spec.ts @@ -0,0 +1,32 @@ +import { TemplateInputParameter } from './../../../../utils/interfaces/template-parameters'; +import { AngularTypeNames } from './../../../types/types'; +import { effects, Parameters } from "../../../../../../src/rz/morph"; +import { writeFileSync, readFileSync } from 'fs'; + +describe('addEffectToNgModule', () => { + afterEach(() => { + const moduleToUseToReset = readFileSync('src/rz/angular/effects/ngrx/effects/snapshots/test-ngrx-effects.module.ts.snap').toString(); + writeFileSync('src/rz/angular/effects/ngrx/effects/test.module.ts', moduleToUseToReset); + }); + it('should update the closest app module file with the proper text', () => { + const mockFilePath = 'src/rz/angular/effects/ngrx/effects/snapshots/sample.effect.ts'; + const parameters: Parameters = { + className: 'Test' + }; + const parametersString = JSON.stringify(parameters); + const mockTemplateInputParameter: TemplateInputParameter = { + defaultValue: 'libs/{name}-dialog', + description: 'File path for name file(s)', + inputType: 'text', + name: 'nameFilePath', + optionalTypes: [{name: 'isExported', selected: true}], + paramType: 'filePath', + type: AngularTypeNames.NgrxEffects + } + effects(mockFilePath, mockTemplateInputParameter, 'angular', parametersString); + const result = readFileSync('src/rz/angular/effects/ngrx/effects/test.module.ts').toString(); + const expected = readFileSync('src/rz/angular/effects/ngrx/effects/snapshots/test-ngrx-effects-output.module.ts.snap').toString(); + + expect(result).toEqual(expected); + }) +}); \ No newline at end of file diff --git a/src/rz/angular/effects/ngrx/effects/ngrx-effects.ts b/src/rz/angular/effects/ngrx/effects/ngrx-effects.ts new file mode 100644 index 0000000..488d03f --- /dev/null +++ b/src/rz/angular/effects/ngrx/effects/ngrx-effects.ts @@ -0,0 +1,42 @@ +import { EditInput } from 'src/rz/morph/interfaces/morph.interface'; +import { findClosestModuleFile } from "../../../../utils"; +import { createRelativePath, isTsFile } from "../../../../utils/add-export"; +import { readFileSync, writeFileSync } from 'fs'; +import { morphTypescript } from '../../../../typescript/morph-typescript'; + +export function addEffectToNgModule(filePathWithName: string, className: string): void { + // make sure that spec is not taken + if(isTsFile(filePathWithName)) { + const moduleTsFile = findClosestModuleFile(filePathWithName); + const importPath = createRelativePath(filePathWithName, moduleTsFile); + const fileName = moduleTsFile.split('.').pop(); + const fileToBeAddedTo = readFileSync(moduleTsFile, 'utf-8').toString(); + const EffectsName = `${className}Effects`; + const moduleName = `EffectsModule.forFeature([${EffectsName}])` + const editInput: EditInput = { + fileType: 'ts', + fileName: fileName, + filePath: moduleTsFile, + fileToBeAddedTo: fileToBeAddedTo, + edits: [ + { + nodeType: 'import', + codeBlock: `{ EffectsModule }`, + path: '@ngrx/effects', + }, + { + nodeType: 'import', + codeBlock: `{ ${EffectsName} }`, + path: importPath, + }, + { + nodeType: 'addNgModuleImport', + codeBlock: moduleName, + } + ] + }; + const updatedFileToBeAddedTo = morphTypescript(editInput); + // TODO extract write file logic from morphCode logic + writeFileSync(moduleTsFile, updatedFileToBeAddedTo); + } +} \ No newline at end of file diff --git a/src/rz/angular/effects/ngrx/effects/snapshots/test-ngrx-effects-output.module.ts.snap b/src/rz/angular/effects/ngrx/effects/snapshots/test-ngrx-effects-output.module.ts.snap new file mode 100644 index 0000000..3c243c6 --- /dev/null +++ b/src/rz/angular/effects/ngrx/effects/snapshots/test-ngrx-effects-output.module.ts.snap @@ -0,0 +1,13 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { EffectsModule } from "@ngrx/effects"; +import { TestEffects } from "./snapshots/sample.effect"; + +@NgModule({ + imports: [ + CommonModule, + EffectsModule.forFeature([TestEffects]) + ], + providers: [], +}) +export class DataAccessBooksModule { } diff --git a/src/rz/angular/effects/ngrx/effects/snapshots/test-ngrx-effects.module.ts.snap b/src/rz/angular/effects/ngrx/effects/snapshots/test-ngrx-effects.module.ts.snap new file mode 100644 index 0000000..0b3abdf --- /dev/null +++ b/src/rz/angular/effects/ngrx/effects/snapshots/test-ngrx-effects.module.ts.snap @@ -0,0 +1,10 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +@NgModule({ + imports: [ + CommonModule, + ], + providers: [], +}) +export class DataAccessBooksModule {} diff --git a/src/rz/angular/effects/ngrx/effects/test.module.ts b/src/rz/angular/effects/ngrx/effects/test.module.ts new file mode 100644 index 0000000..0b3abdf --- /dev/null +++ b/src/rz/angular/effects/ngrx/effects/test.module.ts @@ -0,0 +1,10 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +@NgModule({ + imports: [ + CommonModule, + ], + providers: [], +}) +export class DataAccessBooksModule {} diff --git a/src/rz/angular/effects/ngrx/facade/ngrx-facade.spec.ts b/src/rz/angular/effects/ngrx/facade/ngrx-facade.spec.ts new file mode 100644 index 0000000..1b3b159 --- /dev/null +++ b/src/rz/angular/effects/ngrx/facade/ngrx-facade.spec.ts @@ -0,0 +1,32 @@ +import { TemplateInputParameter } from './../../../../utils/interfaces/template-parameters'; +import { AngularTypeNames } from './../../../types/types'; +import { effects, Parameters } from "../../../../../../src/rz/morph"; +import { writeFileSync, readFileSync } from 'fs'; + +describe('addFacadeToNgModule', () => { + afterEach(() => { + const moduleToUseToReset = readFileSync('src/rz/angular/effects/ngrx/facade/snapshots/ngrx-facade.module.ts.snap').toString(); + writeFileSync('src/rz/angular/effects/ngrx/facade/test.module.ts', moduleToUseToReset); + }); + it('should update the closest app module file with ngrx facade codemods', () => { + const mockFilePath = 'src/rz/angular/effects/ngrx/facade/snapshots/sample.facade.ts'; + const parameters: Parameters = { + className: 'Test' + }; + const parametersString = JSON.stringify(parameters); + const mockTemplateInputParameter: TemplateInputParameter = { + defaultValue: 'libs/{name}-dialog', + description: 'File path for name file(s)', + inputType: 'text', + name: 'nameFilePath', + optionalTypes: [{name: 'isExported', selected: true}], + paramType: 'filePath', + type: AngularTypeNames.NgrxFacade + } + effects(mockFilePath, mockTemplateInputParameter, 'angular', parametersString); + const result = readFileSync('src/rz/angular/effects/ngrx/facade/test.module.ts').toString(); + const expected = readFileSync('src/rz/angular/effects/ngrx/facade/snapshots/ngrx-facade-output.module.ts.snap').toString(); + + expect(result).toEqual(expected); + }) +}); \ No newline at end of file diff --git a/src/rz/angular/effects/ngrx/facade/ngrx-facade.ts b/src/rz/angular/effects/ngrx/facade/ngrx-facade.ts new file mode 100644 index 0000000..428f66d --- /dev/null +++ b/src/rz/angular/effects/ngrx/facade/ngrx-facade.ts @@ -0,0 +1,36 @@ +import { EditInput } from 'src/rz/morph/interfaces/morph.interface'; +import { findClosestModuleFile } from "../../../../utils"; +import { createRelativePath, isTsFile } from "../../../../utils/add-export"; +import { readFileSync, writeFileSync } from 'fs'; +import { morphTypescript } from '../../../../typescript/morph-typescript'; + +export function addFacadeToNgModule(filePathWithName: string, className: string): void { + // make sure that spec is not taken + if(isTsFile(filePathWithName)) { + const moduleTsFile = findClosestModuleFile(filePathWithName); + const importPath = createRelativePath(filePathWithName, moduleTsFile); + const fileName = moduleTsFile.split('.').pop(); + const fileToBeAddedTo = readFileSync(moduleTsFile, 'utf-8').toString(); + const FacadeName = `${className}Facade`; + const editInput: EditInput = { + fileType: 'ts', + fileName: fileName, + filePath: moduleTsFile, + fileToBeAddedTo: fileToBeAddedTo, + edits: [ + { + nodeType: 'import', + codeBlock: `{ ${FacadeName} }`, + path: importPath, + }, + { + nodeType: 'addNgModuleProvider', + codeBlock: FacadeName, + } + ] + }; + const updatedFileToBeAddedTo = morphTypescript(editInput); + // TODO extract write file logic from morphCode logic + writeFileSync(moduleTsFile, updatedFileToBeAddedTo); + } +} \ No newline at end of file diff --git a/src/rz/angular/effects/ngrx/facade/snapshots/ngrx-facade-output.module.ts.snap b/src/rz/angular/effects/ngrx/facade/snapshots/ngrx-facade-output.module.ts.snap new file mode 100644 index 0000000..88486f8 --- /dev/null +++ b/src/rz/angular/effects/ngrx/facade/snapshots/ngrx-facade-output.module.ts.snap @@ -0,0 +1,11 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { TestFacade } from "./snapshots/sample.facade"; + +@NgModule({ + imports: [ + CommonModule, + ], + providers: [TestFacade], +}) +export class DataAccessBooksModule { } diff --git a/src/rz/angular/effects/ngrx/facade/snapshots/ngrx-facade.module.ts.snap b/src/rz/angular/effects/ngrx/facade/snapshots/ngrx-facade.module.ts.snap new file mode 100644 index 0000000..0b3abdf --- /dev/null +++ b/src/rz/angular/effects/ngrx/facade/snapshots/ngrx-facade.module.ts.snap @@ -0,0 +1,10 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +@NgModule({ + imports: [ + CommonModule, + ], + providers: [], +}) +export class DataAccessBooksModule {} diff --git a/src/rz/angular/effects/ngrx/facade/test.module.ts b/src/rz/angular/effects/ngrx/facade/test.module.ts new file mode 100644 index 0000000..0b3abdf --- /dev/null +++ b/src/rz/angular/effects/ngrx/facade/test.module.ts @@ -0,0 +1,10 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +@NgModule({ + imports: [ + CommonModule, + ], + providers: [], +}) +export class DataAccessBooksModule {} diff --git a/src/rz/angular/effects/ngrx/reducer/ngrx-reducer.spec.ts b/src/rz/angular/effects/ngrx/reducer/ngrx-reducer.spec.ts new file mode 100644 index 0000000..7843f12 --- /dev/null +++ b/src/rz/angular/effects/ngrx/reducer/ngrx-reducer.spec.ts @@ -0,0 +1,33 @@ +import { TemplateInputParameter } from './../../../../utils/interfaces/template-parameters'; +import { AngularTypeNames } from '../../../types/types'; +import { effects, Parameters } from "../../../../morph"; +import { writeFileSync, readFileSync } from 'fs'; + +describe('addReducerToNgModule', () => { + afterEach(() => { + const moduleToUseToReset = readFileSync('src/rz/angular/effects/ngrx/reducer/snapshots/ngrx-reducer.module.ts.snap').toString(); + writeFileSync('src/rz/angular/effects/ngrx/reducer/test.module.ts', moduleToUseToReset); + }); + it('should update the closest app module file with ngrx reducer codemods', () => { + const mockFilePath = 'src/rz/angular/effects/ngrx/reducer/snapshots/sample.reducer.ts'; + const parameters: Parameters = { + className: 'Test', + constantName: 'TEST' + }; + const mockTemplateInputParameter: TemplateInputParameter = { + defaultValue: 'libs/{name}-dialog', + description: 'File path for name file(s)', + inputType: 'text', + name: 'nameFilePath', + optionalTypes: [{name: 'isExported', selected: true}], + paramType: 'filePath', + type: AngularTypeNames.NgrxReducer + } + const parametersString = JSON.stringify(parameters); + effects(mockFilePath, mockTemplateInputParameter, 'angular', parametersString); + const result = readFileSync('src/rz/angular/effects/ngrx/reducer/test.module.ts').toString(); + const expected = readFileSync('src/rz/angular/effects/ngrx/reducer/snapshots/ngrx-reducer-output.module.ts.snap').toString(); + + expect(result).toEqual(expected); + }) +}); \ No newline at end of file diff --git a/src/rz/angular/effects/ngrx/reducer/ngrx-reducer.ts b/src/rz/angular/effects/ngrx/reducer/ngrx-reducer.ts new file mode 100644 index 0000000..5cda66c --- /dev/null +++ b/src/rz/angular/effects/ngrx/reducer/ngrx-reducer.ts @@ -0,0 +1,37 @@ +import { EditInput } from 'src/rz/morph/interfaces/morph.interface'; +import { findClosestModuleFile } from "../../../../utils"; +import { createRelativePath, isTsFile } from "../../../../utils/add-export"; +import { readFileSync, writeFileSync } from 'fs'; +import { morphTypescript } from '../../../../typescript/morph-typescript'; + +export function addReducerToNgModule(filePathWithName: string, className: string, constantName: string): void { + // make sure that spec is not taken + if(isTsFile(filePathWithName)) { + const moduleTsFile = findClosestModuleFile(filePathWithName); + const importPath = createRelativePath(filePathWithName, moduleTsFile); + const fileName = moduleTsFile.split('.').pop(); + const fileToBeAddedTo = readFileSync(moduleTsFile, 'utf-8').toString(); + const ReducerName = `from${className}`; + const moduleName = `StoreModule.forFeature(${ReducerName}.${constantName}_FEATURE_KEY, ${ReducerName}.reducer)`; + const editInput: EditInput = { + fileType: 'ts', + fileName: fileName, + filePath: moduleTsFile, + fileToBeAddedTo: fileToBeAddedTo, + edits: [ + { + nodeType: 'import', + codeBlock: `* as fromBooks`, + path: importPath, + }, + { + nodeType: 'addNgModuleImport', + codeBlock: moduleName, + } + ] + }; + const updatedFileToBeAddedTo = morphTypescript(editInput); + // TODO extract write file logic from morphCode logic + writeFileSync(moduleTsFile, updatedFileToBeAddedTo); + } +} \ No newline at end of file diff --git a/src/rz/angular/effects/ngrx/reducer/snapshots/ngrx-reducer-output.module.ts.snap b/src/rz/angular/effects/ngrx/reducer/snapshots/ngrx-reducer-output.module.ts.snap new file mode 100644 index 0000000..a8a41ba --- /dev/null +++ b/src/rz/angular/effects/ngrx/reducer/snapshots/ngrx-reducer-output.module.ts.snap @@ -0,0 +1,12 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import * as fromBooks from "./snapshots/sample.reducer"; + +@NgModule({ + imports: [ + CommonModule, + StoreModule.forFeature(fromTest.TEST_FEATURE_KEY, fromTest.reducer) + ], + providers: [], +}) +export class DataAccessBooksModule { } diff --git a/src/rz/angular/effects/ngrx/reducer/snapshots/ngrx-reducer.module.ts.snap b/src/rz/angular/effects/ngrx/reducer/snapshots/ngrx-reducer.module.ts.snap new file mode 100644 index 0000000..0b3abdf --- /dev/null +++ b/src/rz/angular/effects/ngrx/reducer/snapshots/ngrx-reducer.module.ts.snap @@ -0,0 +1,10 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +@NgModule({ + imports: [ + CommonModule, + ], + providers: [], +}) +export class DataAccessBooksModule {} diff --git a/src/rz/angular/effects/ngrx/reducer/test.module.ts b/src/rz/angular/effects/ngrx/reducer/test.module.ts new file mode 100644 index 0000000..0b3abdf --- /dev/null +++ b/src/rz/angular/effects/ngrx/reducer/test.module.ts @@ -0,0 +1,10 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +@NgModule({ + imports: [ + CommonModule, + ], + providers: [], +}) +export class DataAccessBooksModule {} diff --git a/src/rz/angular/effects/service/index.ts b/src/rz/angular/effects/service/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/rz/angular/effects/service/service.spec.ts b/src/rz/angular/effects/service/service.spec.ts new file mode 100644 index 0000000..9dcd28d --- /dev/null +++ b/src/rz/angular/effects/service/service.spec.ts @@ -0,0 +1,26 @@ +import { TemplateInputParameter } from './../../../utils/interfaces/template-parameters'; +import { writeFileSync, readFileSync } from 'fs'; +import { effects } from "../../../morph"; +import { AngularTypeNames } from "../../types/types"; + +describe('exportServiceFile', () => { + afterEach(() => { + writeFileSync('src/rz/angular/effects/service/index.ts', ''); + }); + it('should export service file', () => { + const mockFilePath = 'src/rz/angular/effects/service/user-service.ts'; + const mockTemplateInputParameter: TemplateInputParameter = { + defaultValue: 'libs/{name}-dialog', + description: 'File path for name file(s)', + inputType: 'text', + name: 'nameFilePath', + optionalTypes: [{name: 'isExported', selected: true}], + paramType: 'filePath', + type: AngularTypeNames.StandaloneComponent + } + effects(mockFilePath, mockTemplateInputParameter, 'angular'); + const result = readFileSync('src/rz/angular/effects/service/index.ts').toString(); + const expected = readFileSync('src/rz/angular/effects/service/snapshots/index-output.ts.snap').toString(); + expect(result).toEqual(expected); + }); +}); \ No newline at end of file diff --git a/src/rz/angular/effects/service/service.ts b/src/rz/angular/effects/service/service.ts new file mode 100644 index 0000000..01df410 --- /dev/null +++ b/src/rz/angular/effects/service/service.ts @@ -0,0 +1,7 @@ +import { exportTsFiles, isTsFile } from "../../../utils/add-export"; + +export function exportServiceFile(filePathWithName: string): void { + if (isTsFile(filePathWithName)) { + exportTsFiles(filePathWithName); + } +} \ No newline at end of file diff --git a/src/rz/angular/effects/service/snapshots/index-output.ts.snap b/src/rz/angular/effects/service/snapshots/index-output.ts.snap new file mode 100644 index 0000000..a850ca1 --- /dev/null +++ b/src/rz/angular/effects/service/snapshots/index-output.ts.snap @@ -0,0 +1 @@ +export * from "./user-service"; diff --git a/src/rz/angular/effects/standalone-component/index.ts b/src/rz/angular/effects/standalone-component/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/rz/angular/effects/standalone-component/snapshots/index-output.ts.snap b/src/rz/angular/effects/standalone-component/snapshots/index-output.ts.snap new file mode 100644 index 0000000..6b4650e --- /dev/null +++ b/src/rz/angular/effects/standalone-component/snapshots/index-output.ts.snap @@ -0,0 +1 @@ +export * from "./standalone-component"; diff --git a/src/rz/angular/effects/standalone-component/standalone-component.ts b/src/rz/angular/effects/standalone-component/standalone-component.ts new file mode 100644 index 0000000..a7d9af8 --- /dev/null +++ b/src/rz/angular/effects/standalone-component/standalone-component.ts @@ -0,0 +1,7 @@ +import { exportTsFiles, isTsFile } from "../../../utils/add-export"; + +export function exportComponentFile(filePathWithName: string): void { + if(isTsFile(filePathWithName)) { + exportTsFiles(filePathWithName); + } +} \ No newline at end of file diff --git a/src/rz/angular/effects/standalone-component/standlone-component.spec.ts b/src/rz/angular/effects/standalone-component/standlone-component.spec.ts new file mode 100644 index 0000000..e62e5db --- /dev/null +++ b/src/rz/angular/effects/standalone-component/standlone-component.spec.ts @@ -0,0 +1,27 @@ +import { TemplateInputParameter } from './../../../utils/interfaces/template-parameters'; +import { writeFileSync, readFileSync } from 'fs'; +import { effects } from "../../../morph"; +import { AngularTypeNames } from "../../types/types"; + +describe('exportComponentFile', () => { + afterEach(() => { + writeFileSync('src/rz/angular/effects/standalone-component/index.ts', ''); + }); + it('should export component file', () => { + const mockFilePath = 'src/rz/angular/effects/standalone-component/standalone-component.ts'; + const mockTemplateInputParameter: TemplateInputParameter = { + defaultValue: 'libs/{name}-dialog', + description: 'File path for name file(s)', + inputType: 'text', + name: 'nameFilePath', + optionalTypes: [{name: 'isExported', selected: true}], + paramType: 'filePath', + type: AngularTypeNames.StandaloneComponent + } + + effects(mockFilePath, mockTemplateInputParameter, 'angular'); + const result = readFileSync('src/rz/angular/effects/standalone-component/index.ts').toString(); + const expected = readFileSync('src/rz/angular/effects/standalone-component/snapshots/index-output.ts.snap').toString(); + expect(result).toEqual(expected); + }); +}); \ No newline at end of file diff --git a/src/rz/angular/index.ts b/src/rz/angular/index.ts new file mode 100644 index 0000000..9d7167a --- /dev/null +++ b/src/rz/angular/index.ts @@ -0,0 +1,2 @@ +export { angularTypes, AngularTypeNames } from "./types/types"; +export { defaultAngularTechnologies } from "./technologies/angular-technologies"; diff --git a/src/rz/angular/insert-into-html-tag/insert-into-html-tag.ts b/src/rz/angular/insert-into-html-tag/insert-into-html-tag.ts new file mode 100644 index 0000000..772d4c2 --- /dev/null +++ b/src/rz/angular/insert-into-html-tag/insert-into-html-tag.ts @@ -0,0 +1,22 @@ + +import visit = require('unist-util-visit'); +import { createUnifiedTree } from '../morph-angular-html'; +import { EditHtmlFile } from '../interfaces/edit-html.interface'; + + +// will insert code into an html block +export function insertIntoHtmlTag(editFile: EditHtmlFile, fileToBeModified: any): any { + const codeToAddTree = createUnifiedTree(editFile.codeBlock as string); + let counter = 0; + + visit(fileToBeModified, {type: 'element', tagName: editFile.tagNameToInsertInto}, (node: any, index) => { + visit(codeToAddTree, {type: 'element', tagName: 'body'}, (nodeOfCodeToAdd: any, index) => { + if(counter === 0) { + node.children.push(nodeOfCodeToAdd.children[0]); + counter++; + } + }); + }); + + return fileToBeModified +} \ No newline at end of file diff --git a/src/rz/angular/interfaces/edit-html.interface.ts b/src/rz/angular/interfaces/edit-html.interface.ts new file mode 100644 index 0000000..00ae199 --- /dev/null +++ b/src/rz/angular/interfaces/edit-html.interface.ts @@ -0,0 +1,15 @@ +export interface EditHtmlInput { + fileType?: 'html', + fileToBeAddedTo?: string | any; + edits: EditHtmlFile[]; +} + +export interface EditHtmlFile { + nodeType: 'editHtmlTag' | 'addSiblingHtml' | 'insertIntoHtmlTag' | + 'deleteHtmlElement' | 'prependHtml' | 'appendHtml'; + codeBlock: string | any[]; + tagNameToInsert?: string; + tagNameToInsertInto?: string; + tagNameToModify?: string; + siblingTagName?: string; +} \ No newline at end of file diff --git a/src/rz/angular/morph-angular-html.spec.ts b/src/rz/angular/morph-angular-html.spec.ts new file mode 100644 index 0000000..e26e929 --- /dev/null +++ b/src/rz/angular/morph-angular-html.spec.ts @@ -0,0 +1,66 @@ +import { readFileSync } from 'fs'; +import { morphHtml} from './morph-angular-html'; +import { EditHtmlFile, EditHtmlInput } from "./interfaces/edit-html.interface"; + +describe('EditAngularHTML', () => { + const fileToBeAddedTo = readFileSync('src/rz/angular/snapshots/data-table.component.html.snap').toString(); + const siblingCodeBlock = ` + `; + const modifyTagNameCodeBlock = 'matSort'; + it('should insert an html block of code after', () => { + const addSiblingHtml: EditHtmlFile = { + nodeType: 'addSiblingHtml', + codeBlock: siblingCodeBlock, + tagNameToInsert: 'mat-paginator', + siblingTagName: 'table' + }; + + const updateHtmlTag: EditHtmlFile = { + nodeType: 'editHtmlTag', + codeBlock: modifyTagNameCodeBlock, + tagNameToModify: 'table', + }; + + const editHtmlInput: EditHtmlInput = { + fileToBeAddedTo: fileToBeAddedTo, + edits: [ + addSiblingHtml, + updateHtmlTag + ] + }; + + const expected = readFileSync('src/rz/angular/snapshots/data-table-output.component.html.snap').toString(); + + const newHtmlString = morphHtml(editHtmlInput); + + expect(newHtmlString).toEqual(expected); + }); + + describe('insertIntoHtmlTag', () => { + it('should insert html into the specified tag', () => { + const fileToBeAddedTo = readFileSync('src/rz/angular/snapshots/insert-into-html-tag/insert-into-html-tag.html.snap').toString(); + const codeBlock = '
new div content
'; + const insertIntoHtmlTag: EditHtmlFile = { + nodeType: 'insertIntoHtmlTag', + codeBlock: codeBlock, + tagNameToInsertInto: 'div' + }; + + const editHtmlInput: EditHtmlInput = { + fileToBeAddedTo: fileToBeAddedTo, + edits: [ + insertIntoHtmlTag + ] + }; + + const expected = readFileSync('src/rz/angular/snapshots/insert-into-html-tag/insert-into-html-tag.html-output.snap').toString(); + const newHtmlString = morphHtml(editHtmlInput); + expect(newHtmlString).toEqual(expected); + }); + }); + +}); \ No newline at end of file diff --git a/src/rz/angular/morph-angular-html.ts b/src/rz/angular/morph-angular-html.ts new file mode 100644 index 0000000..546df6d --- /dev/null +++ b/src/rz/angular/morph-angular-html.ts @@ -0,0 +1,59 @@ +import { parse } from 'parse5'; +import fromParse5 = require('hast-util-from-parse5'); +import toHtml = require('hast-util-to-html'); +import { EditHtmlFile, EditHtmlInput } from './interfaces/edit-html.interface'; +import { insertCodeAfterElement } from './add-sibling-html'; +import { insertIntoHtmlTag } from './insert-into-html-tag/insert-into-html-tag'; +import { updateHtmlTag } from './update-html-tag'; +import * as prettier from 'prettier'; +import * as parserHtml from 'prettier/parser-html'; +import { deleteHtmlElement } from '../html/delete-html-element/delete-html-element'; +import { prependHtml } from '../html/prepend-html/prepend-html'; +import { appendHtml } from '../html/append-html/append-html'; + +function convertToAngularHtmlAndPrettify(tree: any) { + const transformedTreeInHtml = toHtml(tree) + .replace('','').replace('','') + .replace('','').replace('','') + .replace('','').replace('',''); + + return prettier.format(transformedTreeInHtml, { + parser: "html", + plugins: [parserHtml] + }); +} + +export function createUnifiedTree(htmlString: string | any): any { + const p5ast = parse(String(htmlString), {sourceCodeLocationInfo: true}); + return fromParse5(p5ast); +} + + +// fileToBeAddedToTree is top level +export function morphHtml(editHtmlInput: EditHtmlInput): string { + let fileToBeAddedToTree = createUnifiedTree(editHtmlInput.fileToBeAddedTo); + + editHtmlInput.edits.forEach((edit: EditHtmlFile) => { + switch (edit.nodeType) { + case 'editHtmlTag': + fileToBeAddedToTree = updateHtmlTag(edit, fileToBeAddedToTree); + break; + case 'addSiblingHtml': + fileToBeAddedToTree = insertCodeAfterElement(edit, fileToBeAddedToTree); + break; + case 'insertIntoHtmlTag': + fileToBeAddedToTree = insertIntoHtmlTag(edit, fileToBeAddedToTree); + break; + case 'deleteHtmlElement': + fileToBeAddedToTree = deleteHtmlElement(edit, fileToBeAddedToTree); + break; + case 'prependHtml': + fileToBeAddedToTree = prependHtml(edit, fileToBeAddedToTree); + break; + case 'appendHtml': + fileToBeAddedToTree = appendHtml(edit, fileToBeAddedToTree); + } + }) + + return convertToAngularHtmlAndPrettify(fileToBeAddedToTree); +} \ No newline at end of file diff --git a/src/rz/angular/snapshots/data-table-output.component.html.snap b/src/rz/angular/snapshots/data-table-output.component.html.snap new file mode 100644 index 0000000..c4aa8a6 --- /dev/null +++ b/src/rz/angular/snapshots/data-table-output.component.html.snap @@ -0,0 +1,76 @@ + + + + + Filter + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + {{tableColumn.name}} + + {{tableColumn.name}} + + {{element | dataPropertyGetter: tableColumn.dataKey}} +
+ +
diff --git a/src/rz/angular/snapshots/data-table-output.component.ts.snap b/src/rz/angular/snapshots/data-table-output.component.ts.snap new file mode 100644 index 0000000..e69de29 diff --git a/src/rz/angular/snapshots/data-table.component.html.snap b/src/rz/angular/snapshots/data-table.component.html.snap new file mode 100644 index 0000000..8dfcc83 --- /dev/null +++ b/src/rz/angular/snapshots/data-table.component.html.snap @@ -0,0 +1,48 @@ + + + + + + Filter + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + {{tableColumn.name}} + + {{tableColumn.name}} + + {{element | dataPropertyGetter: tableColumn.dataKey}} +
+
+ \ No newline at end of file diff --git a/src/rz/angular/snapshots/data-table.component.ts.snap b/src/rz/angular/snapshots/data-table.component.ts.snap new file mode 100644 index 0000000..e69de29 diff --git a/src/rz/angular/snapshots/insert-into-html-tag/insert-into-html-tag-header-output.html.snap b/src/rz/angular/snapshots/insert-into-html-tag/insert-into-html-tag-header-output.html.snap new file mode 100644 index 0000000..6791856 --- /dev/null +++ b/src/rz/angular/snapshots/insert-into-html-tag/insert-into-html-tag-header-output.html.snap @@ -0,0 +1,19 @@ +Sample header +
+ + +
diff --git a/src/rz/angular/snapshots/insert-into-html-tag/insert-into-html-tag-header.html.snap b/src/rz/angular/snapshots/insert-into-html-tag/insert-into-html-tag-header.html.snap new file mode 100644 index 0000000..b938510 --- /dev/null +++ b/src/rz/angular/snapshots/insert-into-html-tag/insert-into-html-tag-header.html.snap @@ -0,0 +1 @@ +Sample header diff --git a/src/rz/angular/snapshots/insert-into-html-tag/insert-into-html-tag.html-output.snap b/src/rz/angular/snapshots/insert-into-html-tag/insert-into-html-tag.html-output.snap new file mode 100644 index 0000000..24b4f4d --- /dev/null +++ b/src/rz/angular/snapshots/insert-into-html-tag/insert-into-html-tag.html-output.snap @@ -0,0 +1 @@ +
new div content
diff --git a/src/rz/angular/snapshots/insert-into-html-tag/insert-into-html-tag.html.snap b/src/rz/angular/snapshots/insert-into-html-tag/insert-into-html-tag.html.snap new file mode 100644 index 0000000..281c686 --- /dev/null +++ b/src/rz/angular/snapshots/insert-into-html-tag/insert-into-html-tag.html.snap @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/src/rz/angular/technologies/angular-technologies.ts b/src/rz/angular/technologies/angular-technologies.ts new file mode 100644 index 0000000..3bae434 --- /dev/null +++ b/src/rz/angular/technologies/angular-technologies.ts @@ -0,0 +1,12 @@ +export enum DefaultAngularTechnologies { + Angular = 'angular', + Apollo = 'apollo', + FontAwesome = 'font-awesome', + Jest = 'jest', + Nrwl = 'nrwl', + Prettier = 'prettier', + Rxjs = 'rxjs', + Scss = 'scss' +} + +export const defaultAngularTechnologies = Object.values(DefaultAngularTechnologies); \ No newline at end of file diff --git a/src/rz/angular/types/types.ts b/src/rz/angular/types/types.ts new file mode 100644 index 0000000..ac10a84 --- /dev/null +++ b/src/rz/angular/types/types.ts @@ -0,0 +1,68 @@ +export enum AngularTypeNames { + Generic = 'generic', + Class = 'class', + Component = 'component', + StandaloneComponent = 'standalone-component', + Directive = 'directive', + Enum = 'enum', + Guard = 'guard', + Graphql = 'graphql', + Interceptor = 'interceptor', + Interface = 'interface', + Library = 'library', + Module = 'module', + NgrxEffects = 'ngrx-effects', + NgrxFacade = 'ngrx-facade', + NgrxReducer = 'ngrx-reducer', + Pipe = 'pipe', + Resolver = 'resolver', + Service = 'service', + ServiceWorker = 'service-worker', + WebWorker = 'web-worker' +} + +export enum GlobalAngularOptionNames { + IsExported = 'isExported' +} + +export interface AngularOptionalType { + name: GlobalAngularOptionNames, + selected: boolean +} + +export interface AngularType { + name: AngularTypeNames; + optionalTypes?: AngularOptionalType[] +} + +export const angularTypes: Array = [ + { + name: AngularTypeNames.Component, + optionalTypes: [ + { + name: GlobalAngularOptionNames.IsExported, + selected: true + } + ] + }, + {name: AngularTypeNames.Generic}, + {name: AngularTypeNames.Class}, + {name: AngularTypeNames.StandaloneComponent}, + {name: AngularTypeNames.Directive}, + {name: AngularTypeNames.Enum}, + {name: AngularTypeNames.Guard}, + {name: AngularTypeNames.Graphql}, + {name: AngularTypeNames.Interceptor}, + {name: AngularTypeNames.Interceptor}, + {name: AngularTypeNames.Interface}, + {name: AngularTypeNames.Library}, + {name: AngularTypeNames.Module}, + {name: AngularTypeNames.NgrxEffects}, + {name: AngularTypeNames.NgrxFacade}, + {name: AngularTypeNames.NgrxReducer}, + {name: AngularTypeNames.Pipe}, + {name: AngularTypeNames.Resolver}, + {name: AngularTypeNames.Service}, + {name: AngularTypeNames.ServiceWorker}, + {name: AngularTypeNames.WebWorker}, +]; \ No newline at end of file diff --git a/src/rz/angular/update-html-tag.ts b/src/rz/angular/update-html-tag.ts new file mode 100644 index 0000000..e9358d4 --- /dev/null +++ b/src/rz/angular/update-html-tag.ts @@ -0,0 +1,12 @@ +import visit = require('unist-util-visit'); +import { EditHtmlFile } from "./interfaces/edit-html.interface"; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +// will add an angular directive to html +export function updateHtmlTag(editHtmlFile: EditHtmlFile, fileToBeAddedToTree: any): any { + visit(fileToBeAddedToTree, {type: 'element', tagName: editHtmlFile.tagNameToModify}, (htmlElement: any) => { + htmlElement.properties[editHtmlFile.codeBlock as string] = true; + }); + + return fileToBeAddedToTree; +} \ No newline at end of file diff --git a/src/rz/global-variables/colors/colors.spec.ts b/src/rz/global-variables/colors/colors.spec.ts new file mode 100644 index 0000000..5ec86db --- /dev/null +++ b/src/rz/global-variables/colors/colors.spec.ts @@ -0,0 +1,22 @@ +import { getPalette, createColorVariables } from './colors'; + +describe('getPalette', () => { + it('should return a color palette', () => { + const result = getPalette('#cc33ca'); + expect(result).toEqual( + {"100": "#f0c2ef", "200": "#e699e5", "300": "#db70da", "400": "#d452d2", "50": "#f9e7f9", "500": "#cc33ca", "600": "#c72ec5", "700": "#c027bd", "800": "#b920b7", "900": "#ad14ab", "A100": "#ffffff", "A200": "#fadbff", "A400": "#f2a8ff", "A700": "#ef8fff"} + ); + }); +}); + +describe('createColorVariables', () => { + it('should properly create color variables', () => { + const result = createColorVariables('#cc33ca', 'primary'); + const expected = { + "primary": "#cc33ca", + "primaryDarker": "#b920b7", + "primaryLighter": "#f0c2ef", + } + expect(result).toEqual(expected); + }); +}); \ No newline at end of file diff --git a/src/rz/global-variables/colors/colors.ts b/src/rz/global-variables/colors/colors.ts new file mode 100644 index 0000000..e37a216 --- /dev/null +++ b/src/rz/global-variables/colors/colors.ts @@ -0,0 +1,78 @@ +import { TinyColor } from '@ctrl/tinycolor'; + +export interface MaterialPalette { + [key: string]: { + key: string, + hex: string, + isLight: boolean + }; +} + +export interface SubPalette { + main: string; + lighter: string; + darker: string; +} + +type RGBA = any; + +const MIX_AMOUNTS_PRIMARY = { + 50: [true, 12], + 100: [true, 30], + 200: [true, 50], + 300: [true, 70], + 400: [true, 85], + 500: [true, 100], + 600: [false, 87], + 700: [false, 70], + 800: [false, 54], + 900: [false, 25] + }; + +const MIX_AMOUNTS_SECONDARY = { + A100: [15, 80, 65], + A200: [15, 80, 55], + A400: [15, 100, 45], + A700: [15, 100, 40] +}; + +function multiply(rgb1: RGBA, rgb2: RGBA) { + rgb1.b = Math.floor(rgb1.b * rgb2.b / 255); + rgb1.g = Math.floor(rgb1.g * rgb2.g / 255); + rgb1.r = Math.floor(rgb1.r * rgb2.r / 255); + return new TinyColor('rgb ' + rgb1.r + ' ' + rgb1.g + ' ' + rgb1.b); + } + +export function getPalette(color: string): MaterialPalette { + const baseLight = new TinyColor('#ffffff'); + const baseDark = multiply(new TinyColor(color).toRgb(), new TinyColor(color).toRgb()); + const [, , , baseTriad] = new TinyColor(color).tetrad(); + + const primary = Object.keys(MIX_AMOUNTS_PRIMARY) + .map(k => { + const [light, amount] = MIX_AMOUNTS_PRIMARY[k]; + return [k, new TinyColor(light ? baseLight : baseDark).mix(new TinyColor(color), amount)]; + }); + + const accent = Object.keys(MIX_AMOUNTS_SECONDARY) + .map(k => { + const [amount, sat, light] = MIX_AMOUNTS_SECONDARY[k]; + return [k, new TinyColor(baseDark).mix(new TinyColor(baseTriad), amount) + .saturate(sat).lighten(light)] as any; + }); + + return [...primary, ...accent].reduce((acc, [k, c]) => { + acc[k] = c.toHexString(); + return acc; + }, {}); +} + +export function createColorVariables(color: string, variableName: string): any { + const colorPallette = getPalette(color); + + return { + [`${variableName}Lighter`]: colorPallette['100'], + [`${variableName}`]: colorPallette['500'], + [`${variableName}Darker`]: colorPallette['800'], + } +} \ No newline at end of file diff --git a/src/rz/global-variables/index.ts b/src/rz/global-variables/index.ts new file mode 100644 index 0000000..08bd1d0 --- /dev/null +++ b/src/rz/global-variables/index.ts @@ -0,0 +1,8 @@ + +export { createColorVariables, getPalette } from "./colors/colors"; + +export { names } from "./names"; + +export { powerUpVariablesFlatData } from './variable-dependencies/variable-dependencies'; + +export { codeCmsVariablesToIgnore, powerUpVariables } from "./variables-to-ignore/variables-to-ignore"; \ No newline at end of file diff --git a/src/rz/global-variables/interfaces/global-variables.interface.ts b/src/rz/global-variables/interfaces/global-variables.interface.ts new file mode 100644 index 0000000..c465994 --- /dev/null +++ b/src/rz/global-variables/interfaces/global-variables.interface.ts @@ -0,0 +1,10 @@ +export type CodeCmsVariablesToIgnore = 'className' | 'propertyName' | 'constantName' | 'fileName'; +export interface PowerUpVariables { + name: string; + variablesToImplement?: any[]; + defaultValue: string; + description?: string; + codeExample?: string; + stubValue?: string; + variableDependency: string +} \ No newline at end of file diff --git a/src/rz/global-variables/interfaces/index.ts b/src/rz/global-variables/interfaces/index.ts new file mode 100644 index 0000000..a546aa7 --- /dev/null +++ b/src/rz/global-variables/interfaces/index.ts @@ -0,0 +1 @@ +export { CodeCmsVariablesToIgnore, PowerUpVariables } from './global-variables.interface'; \ No newline at end of file diff --git a/src/rz/global-variables/names.spec.ts b/src/rz/global-variables/names.spec.ts new file mode 100644 index 0000000..692f4f9 --- /dev/null +++ b/src/rz/global-variables/names.spec.ts @@ -0,0 +1,39 @@ +import { names } from './names'; + +describe('names', () => { + it('should support class names', () => { + expect(names('foo-bar').className).toEqual('FooBar'); + expect(names('foo_bar').className).toEqual('FooBar'); + expect(names('fooBar').className).toEqual('FooBar'); + expect(names('[fooBar]').className).toEqual('FooBar'); + expect(names('[...fooBar]').className).toEqual('FooBar'); + expect(names('foo-@bar').className).toEqual('FooBar'); + }); + + it('should support property names', () => { + expect(names('foo-bar').propertyName).toEqual('fooBar'); + expect(names('foo_bar').propertyName).toEqual('fooBar'); + expect(names('FooBar').propertyName).toEqual('fooBar'); + expect(names('[fooBar]').propertyName).toEqual('fooBar'); + expect(names('[...fooBar]').propertyName).toEqual('fooBar'); + expect(names('foo-@bar').propertyName).toEqual('fooBar'); + }); + + it('should support file names', () => { + expect(names('foo-bar').fileName).toEqual('foo-bar'); + expect(names('foo_bar').fileName).toEqual('foo-bar'); + expect(names('FooBar').fileName).toEqual('foo-bar'); + expect(names('[fooBar]').fileName).toEqual('[foo-bar]'); + expect(names('[...fooBar]').fileName).toEqual('[...foo-bar]'); + expect(names('foo-@bar').fileName).toEqual('foo-@bar'); + }); + + it('should support title names', () => { + expect(names('foo-bar').titleName).toEqual('Foo Bar'); + expect(names('foo_bar').titleName).toEqual('Foo Bar'); + expect(names('fooBar').titleName).toEqual('Foo Bar'); + expect(names('[fooBar]').titleName).toEqual('Foo Bar'); + expect(names('[...fooBar]').titleName).toEqual('Foo Bar'); + expect(names('foo-@bar').titleName).toEqual('Foo Bar'); + }); +}); diff --git a/src/rz/global-variables/names.ts b/src/rz/global-variables/names.ts new file mode 100644 index 0000000..bd86fb0 --- /dev/null +++ b/src/rz/global-variables/names.ts @@ -0,0 +1,79 @@ +/** + * Util function to generate different strings based off the provided name. + * + * Examples: + * + * ```typescript + * names("my-name") // {name: 'my-name', className: 'MyName', propertyName: 'myName', constantName: 'MY_NAME', fileName: 'my-name'} + * names("myName") // {name: 'my-name', className: 'MyName', propertyName: 'myName', constantName: 'MY_NAME', fileName: 'my-name'} + * ``` + * @param name + */ + export function names(name: string): { + name: string; + className: string; + propertyName: string; + constantName: string; + fileName: string; + titleName: string; + } { + return { + name, + className: toClassName(name), + propertyName: toPropertyName(name), + constantName: toConstantName(name), + fileName: toFileName(name), + titleName: toTitleName(name) + }; + } + + /** + * Hyphenated to UpperCamelCase + */ + function toClassName(str: string): string { + return toCapitalCase(toPropertyName(str)); + } + + /** + * Hyphenated to lowerCamelCase + */ + function toPropertyName(s: string): string { + return s + .replace(/([^a-zA-Z0-9])+(.)?/g, (_, __, chr) => + chr ? chr.toUpperCase() : '' + ) + .replace(/[^a-zA-Z\d]/g, '') + .replace(/^([A-Z])/, (m) => m.toLowerCase()); + } + + /** + * Hyphenated to CONSTANT_CASE + */ + function toConstantName(s: string): string { + return s.replace(/([^a-zA-Z0-9])/g, '_').toUpperCase(); + } + + /** + * Upper camelCase to lowercase, hyphenated + */ + function toFileName(s: string): string { + return s + .replace(/([a-z\d])([A-Z])/g, '$1_$2') + .toLowerCase() + .replace(/[ _]/g, '-'); + } + + /** + * Capitalizes the first letter of a string + */ + function toCapitalCase(s: string): string { + return s.charAt(0).toUpperCase() + s.substr(1); + } + + function toTitleName(s: string): string { + return toClassName(s).split('') + .map(letter => + letter.match(/[A-Z]/)?' ' + letter : letter + ).join('').trim() + } + \ No newline at end of file diff --git a/src/rz/global-variables/variable-dependencies/variable-dependencies.ts b/src/rz/global-variables/variable-dependencies/variable-dependencies.ts new file mode 100644 index 0000000..4c0f597 --- /dev/null +++ b/src/rz/global-variables/variable-dependencies/variable-dependencies.ts @@ -0,0 +1,141 @@ +import { PowerUpVariables } from "../interfaces" + +export const powerUpVariablesFlatData: PowerUpVariables[] = [ + { + name: 'name', + description: 'Template variable to be used for class creation', + defaultValue: 'name', + stubValue: 'name', + variableDependency: 'name' + }, + { + name: 'className', + defaultValue: 'className', + description: 'Hyphenated to UpperCamelCase', + codeExample: 'my-name to MyName', + stubValue: 'className', + variableDependency: 'name' + }, + { + name: 'propertyName', + defaultValue: 'propertyName', + stubValue: 'propertyName', + description: 'Hyphenated to lowerCamelCase', + codeExample: 'my-name to myName', + variableDependency: 'name' + }, + { + name: 'constantName', + defaultValue: 'constantName', + stubValue: 'constantName', + description: 'Hyphenated to CONSTANT_CASE', + codeExample: 'my-name to MY_NAME', + variableDependency: 'name' + }, + { + name: 'fileName', + defaultValue: 'fileName', + stubValue: 'fileName', + description: 'Upper camelCase to lowercase, hyphenated', + codeExample: 'myName to my-name', + variableDependency: 'name' + }, + { + name: 'titleName', + description: 'will create a Title Case title out of your kebab-case name', + defaultValue: '<%= titleName %>', + stubValue: 'titleName', + codeExample: 'myName to My Name', + variableDependency: 'name' + }, + { + name: 'orgName', + description: 'Will dyanmically get organization name', + defaultValue: '<%= orgName %>', + stubValue: 'orgName', + variableDependency: 'orgName' + }, + { + name: 'projectName', + description: 'Will dynamically get project name', + defaultValue: '<%= projectName %>', + stubValue: 'projectName', + variableDependency: 'projectName' + }, + { + name: 'primary', + description: 'primary color to be used within app', + defaultValue: 'primary', + stubValue: 'primary', + variableDependency: 'primary' + }, + { + name: 'primaryLighter', + defaultValue: 'primaryLighter', + stubValue: 'primaryLighter', + description: 'Lighter color of primary', + codeExample: 'primary to primaryLighter', + variableDependency: 'primary' + }, + { + name: 'primaryDarker', + defaultValue: 'primaryDarker', + stubValue: 'primaryDarker', + description: 'Darker color of primary', + codeExample: 'primary to primaryDarker', + variableDependency: 'primary' + }, + { + name: 'accent', + stubValue: 'accent', + description: 'accent color to be used within app', + defaultValue: 'accent', + variableDependency: 'accent' + }, + { + name: 'accentLighter', + stubValue: 'accentLighter', + defaultValue: 'accentLighter', + description: 'Lighter color of accent', + codeExample: 'accent to accentLighter', + variableDependency: 'accent' + }, + { + name: 'accentDarker', + stubValue: 'accentDarker', + defaultValue: 'accentDarker', + description: 'Darker color of accent', + codeExample: 'accent to accentDarker', + variableDependency: 'accent' + }, + { + name: 'warn', + stubValue: 'warn', + defaultValue: 'warn', + description: 'warn color to be used within app', + variableDependency: 'warn' + }, + { + name: 'warnLighter', + stubValue: 'warnLighter', + defaultValue: 'warnLighter', + description: 'Lighter color of warn', + codeExample: 'warn to warnLighter', + variableDependency: 'warn' + }, + { + name: 'warnDarker', + stubValue: 'warnDarker', + defaultValue: 'warnDarker', + description: 'Darker color of warn', + codeExample: 'warn to warnDarker', + variableDependency: 'warn' + }, + { + name: 'infrastructureCommandPath', + description: 'Template variable to be used for infrastructure commands', + defaultValue: 'infrastructureCommandPath', + stubValue: 'infrastructureCommandPath', + variableDependency: 'infrastructureCommandPath' + }, + ] \ No newline at end of file diff --git a/src/rz/global-variables/variables-to-ignore/variables-to-ignore.ts b/src/rz/global-variables/variables-to-ignore/variables-to-ignore.ts new file mode 100644 index 0000000..b347bbe --- /dev/null +++ b/src/rz/global-variables/variables-to-ignore/variables-to-ignore.ts @@ -0,0 +1,116 @@ +import { PowerUpVariables } from "../interfaces"; + +export const codeCmsVariablesToIgnore: string[] = ['className', 'propertyName', 'constantName', 'fileName']; + +export const powerUpVariables: PowerUpVariables[] = [ + { + name: 'name', + description: 'Template variable to be used for class creation', + defaultValue: 'name', + variableDependency: 'name', + variablesToImplement: [ + { + name: 'className', + description: 'Hyphenated to UpperCamelCase', + codeExample: 'my-name to MyName', + variableDependency: 'name' + }, + { + name: 'propertyName', + description: 'Hyphenated to lowerCamelCase', + codeExample: 'my-name to myName', + variableDependency: 'name' + }, + { + name: 'constantName', + description: 'Hyphenated to CONSTANT_CASE', + codeExample: 'my-name to MY_NAME' + }, + { + name: 'fileName', + description: 'Upper camelCase to lowercase, hyphenated', + codeExample: 'myName to my-name' + }, + { + name: 'titleName', + description: 'camelCase to Title Case', + codeExample: 'myName to My Name' + } + ] + }, + { + name: 'orgName', + description: 'Will dyanmically get organization name', + defaultValue: '<%= orgName %>', + stubValue: 'orgName', + variableDependency: 'orgName' + }, + { + name: 'projectName', + description: 'Will dynamically get project name', + defaultValue: '<%= projectName %>', + stubValue: 'projectName', + variableDependency: 'projectName' + }, + { + name: 'primary', + description: 'primary color to be used within app', + defaultValue: 'primary', + variableDependency: 'primary', + variablesToImplement: [ + { + name: 'primaryLighter', + description: 'Lighter color of primary', + codeExample: 'primary to primaryLighter' + }, + { + name: 'primaryDarker', + description: 'Darker color of primary', + codeExample: 'primary to primaryDarker' + } + ] + }, + { + name: 'accent', + description: 'accent color to be used within app', + defaultValue: 'accent', + variableDependency: 'accent', + variablesToImplement: [ + { + name: 'accentLighter', + description: 'Lighter color of accent', + codeExample: 'accent to accentLighter' + }, + { + name: 'accentDarker', + description: 'Darker color of accent', + codeExample: 'accent to accentDarker' + } + ] + }, + { + name: 'warn', + defaultValue: 'warn', + description: 'warn color to be used within app', + variableDependency: 'warn', + variablesToImplement: [ + { + name: 'warnLighter', + description: 'Lighter color of warn', + codeExample: 'warn to warnLighter' + }, + { + name: 'warnDarker', + description: 'Darker color of warn', + codeExample: 'warn to warnDarker' + } + ] + }, + { + name: 'infrastructureCommandPath', + description: 'Template variable to be used for infrastructure commands', + defaultValue: 'infrastructureCommandPath', + stubValue: 'infrastructureCommandPath', + variableDependency: 'infrastructureCommandPath' + } +] \ No newline at end of file diff --git a/src/rz/html/append-html/append-html.spec.ts b/src/rz/html/append-html/append-html.spec.ts new file mode 100644 index 0000000..c2928f0 --- /dev/null +++ b/src/rz/html/append-html/append-html.spec.ts @@ -0,0 +1,42 @@ +import { EditHtmlInput } from '../../angular/interfaces/edit-html.interface'; +import { readFileSync } from 'fs'; +import { morphHtml } from '../../angular/morph-angular-html'; + +describe('appendHtml' , () => { + it('should append an html element', () => { + const fileToBeAddedTo = readFileSync('src/rz/html/append-html/snapshots/append-html.html.snap').toString(); + + const editHtmlInput: EditHtmlInput = { + fileToBeAddedTo: fileToBeAddedTo, + edits: [ + { + nodeType: 'appendHtml', + codeBlock: '', + } + ] + }; + + const expected = readFileSync('src/rz/html/append-html/snapshots/append-html-output.html.snap').toString(); + const newHtmlString = morphHtml(editHtmlInput); + expect(newHtmlString).toEqual(expected); + }); + + it('should append html code inside of passed in optional element', () => { + const fileToBeAddedTo = readFileSync('src/rz/html/append-html/snapshots/append-html-into-element/append-html-into-element.html.snap').toString(); + + const editHtmlInput: EditHtmlInput = { + fileToBeAddedTo: fileToBeAddedTo, + edits: [ + { + nodeType: 'appendHtml', + codeBlock: '
test
', + tagNameToInsertInto: 'mat-toolbar' + } + ] + }; + + const expected = readFileSync('src/rz/html/append-html/snapshots/append-html-into-element/append-html-into-element-output.html.snap').toString(); + const newHtmlString = morphHtml(editHtmlInput); + expect(newHtmlString).toEqual(expected); + }); +}); \ No newline at end of file diff --git a/src/rz/html/append-html/append-html.ts b/src/rz/html/append-html/append-html.ts new file mode 100644 index 0000000..b5fbd4f --- /dev/null +++ b/src/rz/html/append-html/append-html.ts @@ -0,0 +1,21 @@ +import { createUnifiedTree } from '../../angular/morph-angular-html'; +import visit = require('unist-util-visit'); +import { EditHtmlFile } from "../../angular/interfaces/edit-html.interface"; + +export function appendHtml(editHtmlFile: EditHtmlFile, fileToBeAddedToTree: any): any { + let counter = 0; + let elementToReturn: any; + + const element = editHtmlFile.tagNameToInsertInto ? {type: 'element', tagName: editHtmlFile.tagNameToInsertInto} : {type: 'element', tagName: 'body'}; + + visit(fileToBeAddedToTree, element, (htmlElement: any) => { + const codeToAddTree = createUnifiedTree(editHtmlFile.codeBlock as string); + if(counter === 0) { + htmlElement.children.push(codeToAddTree); + elementToReturn = editHtmlFile.tagNameToInsertInto ? htmlElement : htmlElement.children; + counter++; + } + }); + + return elementToReturn; +} \ No newline at end of file diff --git a/src/rz/html/append-html/snapshots/append-html-into-element/append-html-into-element-output.html.snap b/src/rz/html/append-html/snapshots/append-html-into-element/append-html-into-element-output.html.snap new file mode 100644 index 0000000..659a554 --- /dev/null +++ b/src/rz/html/append-html/snapshots/append-html-into-element/append-html-into-element-output.html.snap @@ -0,0 +1,4 @@ + +
hello
+
test
diff --git a/src/rz/html/append-html/snapshots/append-html-into-element/append-html-into-element.html.snap b/src/rz/html/append-html/snapshots/append-html-into-element/append-html-into-element.html.snap new file mode 100644 index 0000000..d645aeb --- /dev/null +++ b/src/rz/html/append-html/snapshots/append-html-into-element/append-html-into-element.html.snap @@ -0,0 +1,3 @@ + +
hello
+
diff --git a/src/rz/html/append-html/snapshots/append-html-output.html.snap b/src/rz/html/append-html/snapshots/append-html-output.html.snap new file mode 100644 index 0000000..16bcade --- /dev/null +++ b/src/rz/html/append-html/snapshots/append-html-output.html.snap @@ -0,0 +1,2 @@ +
+ diff --git a/src/rz/html/append-html/snapshots/append-html.html.snap b/src/rz/html/append-html/snapshots/append-html.html.snap new file mode 100644 index 0000000..281c686 --- /dev/null +++ b/src/rz/html/append-html/snapshots/append-html.html.snap @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/src/rz/html/delete-html-element/delete-html-element.spec.ts b/src/rz/html/delete-html-element/delete-html-element.spec.ts new file mode 100644 index 0000000..c5a753c --- /dev/null +++ b/src/rz/html/delete-html-element/delete-html-element.spec.ts @@ -0,0 +1,23 @@ +import { EditHtmlInput } from './../../angular/interfaces/edit-html.interface'; +import { readFileSync } from 'fs'; +import { morphHtml } from '../../angular/morph-angular-html'; + +describe('deleteHtmlElement' , () => { + it('should delete an html element', () => { + const fileToBeAddedTo = readFileSync('src/rz/html/delete-html-element/snapshots/delete-html-element.html.snap').toString(); + + const editHtmlInput: EditHtmlInput = { + fileToBeAddedTo: fileToBeAddedTo, + edits: [ + { + nodeType: 'deleteHtmlElement', + codeBlock: 'mat-toolbar', + } + ] + }; + + const expected = readFileSync('src/rz/html/delete-html-element/snapshots/delete-html-element-output.html.snap').toString(); + const newHtmlString = morphHtml(editHtmlInput); + expect(newHtmlString).toEqual(expected); + }); +}); \ No newline at end of file diff --git a/src/rz/html/delete-html-element/delete-html-element.ts b/src/rz/html/delete-html-element/delete-html-element.ts new file mode 100644 index 0000000..456bdf7 --- /dev/null +++ b/src/rz/html/delete-html-element/delete-html-element.ts @@ -0,0 +1,10 @@ +import visit = require('unist-util-visit'); +import { EditHtmlFile } from "../../angular/interfaces/edit-html.interface"; + +export function deleteHtmlElement(editHtmlFile: EditHtmlFile, fileToBeAddedToTree: any): any { + visit(fileToBeAddedToTree, {type: 'element', tagName: editHtmlFile.codeBlock}, (htmlElement: any, index, parent) => { + parent.children.splice(index, 1); + }); + + return fileToBeAddedToTree; +} \ No newline at end of file diff --git a/src/rz/html/delete-html-element/snapshots/delete-html-element-output.html.snap b/src/rz/html/delete-html-element/snapshots/delete-html-element-output.html.snap new file mode 100644 index 0000000..e69de29 diff --git a/src/rz/html/delete-html-element/snapshots/delete-html-element.html.snap b/src/rz/html/delete-html-element/snapshots/delete-html-element.html.snap new file mode 100644 index 0000000..2739a75 --- /dev/null +++ b/src/rz/html/delete-html-element/snapshots/delete-html-element.html.snap @@ -0,0 +1 @@ +Sample header \ No newline at end of file diff --git a/src/rz/html/prepend-html/prepend-html.spec.ts b/src/rz/html/prepend-html/prepend-html.spec.ts new file mode 100644 index 0000000..1b5fc7e --- /dev/null +++ b/src/rz/html/prepend-html/prepend-html.spec.ts @@ -0,0 +1,42 @@ +import { EditHtmlInput } from './../../angular/interfaces/edit-html.interface'; +import { readFileSync } from 'fs'; +import { morphHtml } from '../../angular/morph-angular-html'; + +describe('prependHtml' , () => { + it('should prepend html code into root of html file', () => { + const fileToBeAddedTo = readFileSync('src/rz/html/prepend-html/snapshots/prepend-html.html.snap').toString(); + + const editHtmlInput: EditHtmlInput = { + fileToBeAddedTo: fileToBeAddedTo, + edits: [ + { + nodeType: 'prependHtml', + codeBlock: '', + } + ] + }; + + const expected = readFileSync('src/rz/html/prepend-html/snapshots/prepend-html-output.html.snap').toString(); + const newHtmlString = morphHtml(editHtmlInput); + expect(newHtmlString).toEqual(expected); + }); + + it('should prepend an html code inside of passed in optional element', () => { + const fileToBeAddedTo = readFileSync('src/rz/html/prepend-html/snapshots/prepend-html-into-element/prepend-html-into-element.html.snap').toString(); + + const editHtmlInput: EditHtmlInput = { + fileToBeAddedTo: fileToBeAddedTo, + edits: [ + { + nodeType: 'prependHtml', + codeBlock: '
test
', + tagNameToInsertInto: 'mat-toolbar' + } + ] + }; + + const expected = readFileSync('src/rz/html/prepend-html/snapshots/prepend-html-into-element/prepend-html-into-element-ouput.html.snap').toString(); + const newHtmlString = morphHtml(editHtmlInput); + expect(newHtmlString).toEqual(expected); + }); +}); \ No newline at end of file diff --git a/src/rz/html/prepend-html/prepend-html.ts b/src/rz/html/prepend-html/prepend-html.ts new file mode 100644 index 0000000..25fdeed --- /dev/null +++ b/src/rz/html/prepend-html/prepend-html.ts @@ -0,0 +1,21 @@ +import { createUnifiedTree } from '../../angular/morph-angular-html'; +import visit = require('unist-util-visit'); +import { EditHtmlFile } from "../../angular/interfaces/edit-html.interface"; + +export function prependHtml(editHtmlFile: EditHtmlFile, fileToBeAddedToTree: any): any { + let counter = 0; + let elementToReturn: any; + + const element = editHtmlFile.tagNameToInsertInto ? {type: 'element', tagName: editHtmlFile.tagNameToInsertInto} : {type: 'element', tagName: 'body'}; + + visit(fileToBeAddedToTree, element, (htmlElement: any) => { + const codeToAddTree = createUnifiedTree(editHtmlFile.codeBlock as string); + if(counter === 0) { + htmlElement.children.unshift(codeToAddTree); + elementToReturn = editHtmlFile.tagNameToInsertInto ? htmlElement : htmlElement.children; + counter++; + } + }); + + return elementToReturn; +} \ No newline at end of file diff --git a/src/rz/html/prepend-html/snapshots/prepend-html-into-element/prepend-html-into-element-ouput.html.snap b/src/rz/html/prepend-html/snapshots/prepend-html-into-element/prepend-html-into-element-ouput.html.snap new file mode 100644 index 0000000..32a3eb3 --- /dev/null +++ b/src/rz/html/prepend-html/snapshots/prepend-html-into-element/prepend-html-into-element-ouput.html.snap @@ -0,0 +1,4 @@ +
test
+
hello
+
diff --git a/src/rz/html/prepend-html/snapshots/prepend-html-into-element/prepend-html-into-element.html.snap b/src/rz/html/prepend-html/snapshots/prepend-html-into-element/prepend-html-into-element.html.snap new file mode 100644 index 0000000..d9595f0 --- /dev/null +++ b/src/rz/html/prepend-html/snapshots/prepend-html-into-element/prepend-html-into-element.html.snap @@ -0,0 +1,3 @@ + +
hello
+
\ No newline at end of file diff --git a/src/rz/html/prepend-html/snapshots/prepend-html-output.html.snap b/src/rz/html/prepend-html/snapshots/prepend-html-output.html.snap new file mode 100644 index 0000000..e8dddce --- /dev/null +++ b/src/rz/html/prepend-html/snapshots/prepend-html-output.html.snap @@ -0,0 +1,2 @@ + +
diff --git a/src/rz/html/prepend-html/snapshots/prepend-html.html.snap b/src/rz/html/prepend-html/snapshots/prepend-html.html.snap new file mode 100644 index 0000000..281c686 --- /dev/null +++ b/src/rz/html/prepend-html/snapshots/prepend-html.html.snap @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/src/rz/json/edit-json/edit-json.spec.ts b/src/rz/json/edit-json/edit-json.spec.ts new file mode 100644 index 0000000..1f7e230 --- /dev/null +++ b/src/rz/json/edit-json/edit-json.spec.ts @@ -0,0 +1,46 @@ +import { readFileSync } from 'fs'; +import { EditJson } from './../interfaces/json-morph.interface'; +import { editJson, addJsonKeyValue } from './edit-json'; +describe('editJson', () => { + it('should edit json and return value', () => { + const mockJson = readFileSync('src/rz/json/snapshots/project.json.snap').toString(); + + const mockEditJson: EditJson = { + nodeType: 'editJson', + valueToModify: '/targets/build/configurations/production/fileReplacements', + codeBlock: '[{replace: "libs/common/environments/src/lib/environment.ts", with: "libs/common/environments/src/lib/environment.prod.ts"}]' + } + const modifiedJson = editJson(mockEditJson, mockJson); + const expected = JSON.parse(readFileSync('src/rz/json/snapshots/project-output.json.snap').toString()); + + expect(modifiedJson).toEqual(expected); + }); + + it('should edit nested json and return value', () => { + const mockJson = readFileSync('src/rz/json/snapshots/nested-package-json/nested-package.json.snap').toString(); + + const mockEditJson: EditJson = { + nodeType: 'editJson', + valueToModify: '/contributes/menus', + codeBlock: {data: "test"} + } + const modifiedJson = editJson(mockEditJson, mockJson); + const expected = JSON.parse(readFileSync('src/rz/json/snapshots/nested-package-json/nested-package-output.json.snap').toString()); + + expect(modifiedJson).toEqual(expected); + }); + + it('should add a key value', () => { + const mockJson = readFileSync('src/rz/json/snapshots/package-json/package.json.snap').toString(); + const mockEditJson: EditJson = { + nodeType: 'editJson', + valueToModify: 'scripts', + codeBlock: '{"test:ci": "npm run nx -- run-many --target=test --all --parallel --coverage --coverageReporters=lcov && node ./tools/coverageMerger.js"}' + } + + const modifiedJson = addJsonKeyValue(mockEditJson, mockJson); + const expected = JSON.parse(readFileSync('src/rz/json/snapshots/package-json/package-output.json.snap').toString()); + + expect(modifiedJson).toEqual(expected); + }); +}); \ No newline at end of file diff --git a/src/rz/json/edit-json/edit-json.ts b/src/rz/json/edit-json/edit-json.ts new file mode 100644 index 0000000..a9e4c73 --- /dev/null +++ b/src/rz/json/edit-json/edit-json.ts @@ -0,0 +1,31 @@ +import { EditJson } from "../interfaces/json-morph.interface"; +import { JSONPath } from 'jsonpath-plus'; +import pointer = require('json-pointer'); + +export function editJson(editJson: EditJson, json: string): any { + const codeBlock = eval(editJson.codeBlock); + json = JSON.parse(json); + //2. Set value + pointer.set(json, editJson.valueToModify, codeBlock); + + //3. Return modified value + return json; +} + +export function addJsonKeyValue(editJson: EditJson, json: string): any { + const codeBlock = typeof editJson.codeBlock === 'string' ? JSON.parse((editJson.codeBlock)) : editJson.codeBlock; + json = JSON.parse(json); + //Get Pointer + const JsonPointer = JSONPath({path: `$..${editJson.valueToModify}`, json, resultType: 'pointer'}); + const firstJsonMatchedPointer = JsonPointer[0]; + + //Get value of json + const jsonToEdit = pointer.get(json, firstJsonMatchedPointer); + + //Set value + pointer.set(json, firstJsonMatchedPointer, {...jsonToEdit, ...codeBlock}); + + //Return modified value + return json; +} + diff --git a/src/rz/json/interfaces/json-morph.interface.ts b/src/rz/json/interfaces/json-morph.interface.ts new file mode 100644 index 0000000..89ba6d9 --- /dev/null +++ b/src/rz/json/interfaces/json-morph.interface.ts @@ -0,0 +1,10 @@ +export interface EditJsonInput { + fileToBeAddedTo?: string; + edits: EditJson[]; + } + + export interface EditJson { + nodeType: 'editJson' | 'addJsonKeyValue'; + valueToModify: any; + codeBlock: string | any | any[]; + } \ No newline at end of file diff --git a/src/rz/json/json-morph.ts b/src/rz/json/json-morph.ts new file mode 100644 index 0000000..192417b --- /dev/null +++ b/src/rz/json/json-morph.ts @@ -0,0 +1,18 @@ +import { EditJson, EditJsonInput } from './interfaces/json-morph.interface'; +import { addJsonKeyValue, editJson } from './edit-json/edit-json'; + +export function morphJson(editJsonInput: EditJsonInput): string { + let json = editJsonInput.fileToBeAddedTo; + + editJsonInput.edits.forEach((editJsonValue: EditJson) => { + switch(editJsonValue.nodeType) { + case 'editJson': + json = editJson(editJsonValue, json); + break; + case 'addJsonKeyValue': + json = addJsonKeyValue(editJsonValue, json); + break; + } + }); + return JSON.stringify(json, null, 2); +} \ No newline at end of file diff --git a/src/rz/json/snapshots/nested-package-json/nested-package-output.json.snap b/src/rz/json/snapshots/nested-package-json/nested-package-output.json.snap new file mode 100644 index 0000000..a72b151 --- /dev/null +++ b/src/rz/json/snapshots/nested-package-json/nested-package-output.json.snap @@ -0,0 +1,61 @@ +{ + "name": "razroo-nrwl-angular-shell", + "version": "0.0.0", + "license": "MIT", + "scripts": { + "ng": "nx", + "postinstall": "node ./decorate-angular-cli.js && ngcc --properties es2015 browser module main", + "start": "nx serve", + "build": "nx build", + "test": "nx test" + }, + "private": true, + "dependencies": { + "@angular/animations": "~13.2.0", + "@angular/common": "~13.2.0", + "@angular/compiler": "~13.2.0", + "@angular/core": "~13.2.0", + "@angular/forms": "~13.2.0", + "@angular/platform-browser": "~13.2.0", + "@angular/platform-browser-dynamic": "~13.2.0", + "@angular/router": "~13.2.0", + "@nrwl/angular": "13.8.5", + "rxjs": "~7.4.0", + "tslib": "^2.0.0", + "zone.js": "~0.11.4" + }, + "devDependencies": { + "@angular-devkit/build-angular": "~13.2.0", + "@angular-eslint/eslint-plugin": "~13.0.1", + "@angular-eslint/eslint-plugin-template": "~13.0.1", + "@angular-eslint/template-parser": "~13.0.1", + "@angular/cli": "~13.2.0", + "@angular/compiler-cli": "~13.2.0", + "@angular/language-service": "~13.2.0", + "@nrwl/cli": "13.8.5", + "@nrwl/cypress": "13.8.5", + "@nrwl/eslint-plugin-nx": "13.8.5", + "@nrwl/jest": "13.8.5", + "@nrwl/linter": "13.8.5", + "@nrwl/tao": "13.8.5", + "@nrwl/workspace": "13.8.5", + "@types/jest": "27.0.2", + "@types/node": "16.11.7", + "@typescript-eslint/eslint-plugin": "~5.10.0", + "@typescript-eslint/parser": "~5.10.0", + "cypress": "^9.1.0", + "eslint": "~8.7.0", + "eslint-config-prettier": "8.1.0", + "eslint-plugin-cypress": "^2.10.3", + "jest": "27.2.3", + "jest-preset-angular": "11.1.1", + "prettier": "^2.5.1", + "ts-jest": "27.0.5", + "typescript": "~4.5.2" + }, + "contributes": { + "menus": { + "data": "test" + } + } +} diff --git a/src/rz/json/snapshots/nested-package-json/nested-package.json.snap b/src/rz/json/snapshots/nested-package-json/nested-package.json.snap new file mode 100644 index 0000000..3c65b68 --- /dev/null +++ b/src/rz/json/snapshots/nested-package-json/nested-package.json.snap @@ -0,0 +1,59 @@ +{ + "name": "razroo-nrwl-angular-shell", + "version": "0.0.0", + "license": "MIT", + "scripts": { + "ng": "nx", + "postinstall": "node ./decorate-angular-cli.js && ngcc --properties es2015 browser module main", + "start": "nx serve", + "build": "nx build", + "test": "nx test" + }, + "private": true, + "dependencies": { + "@angular/animations": "~13.2.0", + "@angular/common": "~13.2.0", + "@angular/compiler": "~13.2.0", + "@angular/core": "~13.2.0", + "@angular/forms": "~13.2.0", + "@angular/platform-browser": "~13.2.0", + "@angular/platform-browser-dynamic": "~13.2.0", + "@angular/router": "~13.2.0", + "@nrwl/angular": "13.8.5", + "rxjs": "~7.4.0", + "tslib": "^2.0.0", + "zone.js": "~0.11.4" + }, + "devDependencies": { + "@angular-devkit/build-angular": "~13.2.0", + "@angular-eslint/eslint-plugin": "~13.0.1", + "@angular-eslint/eslint-plugin-template": "~13.0.1", + "@angular-eslint/template-parser": "~13.0.1", + "@angular/cli": "~13.2.0", + "@angular/compiler-cli": "~13.2.0", + "@angular/language-service": "~13.2.0", + "@nrwl/cli": "13.8.5", + "@nrwl/cypress": "13.8.5", + "@nrwl/eslint-plugin-nx": "13.8.5", + "@nrwl/jest": "13.8.5", + "@nrwl/linter": "13.8.5", + "@nrwl/tao": "13.8.5", + "@nrwl/workspace": "13.8.5", + "@types/jest": "27.0.2", + "@types/node": "16.11.7", + "@typescript-eslint/eslint-plugin": "~5.10.0", + "@typescript-eslint/parser": "~5.10.0", + "cypress": "^9.1.0", + "eslint": "~8.7.0", + "eslint-config-prettier": "8.1.0", + "eslint-plugin-cypress": "^2.10.3", + "jest": "27.2.3", + "jest-preset-angular": "11.1.1", + "prettier": "^2.5.1", + "ts-jest": "27.0.5", + "typescript": "~4.5.2" + }, + "contributes": { + "menus": {} + } +} diff --git a/src/rz/json/snapshots/package-json/package-output.json.snap b/src/rz/json/snapshots/package-json/package-output.json.snap new file mode 100644 index 0000000..9548fda --- /dev/null +++ b/src/rz/json/snapshots/package-json/package-output.json.snap @@ -0,0 +1,57 @@ +{ + "name": "razroo-nrwl-angular-shell", + "version": "0.0.0", + "license": "MIT", + "scripts": { + "ng": "nx", + "postinstall": "node ./decorate-angular-cli.js && ngcc --properties es2015 browser module main", + "start": "nx serve", + "build": "nx build", + "test": "nx test", + "test:ci": "npm run nx -- run-many --target=test --all --parallel --coverage --coverageReporters=lcov && node ./tools/coverageMerger.js" + }, + "private": true, + "dependencies": { + "@angular/animations": "~13.2.0", + "@angular/common": "~13.2.0", + "@angular/compiler": "~13.2.0", + "@angular/core": "~13.2.0", + "@angular/forms": "~13.2.0", + "@angular/platform-browser": "~13.2.0", + "@angular/platform-browser-dynamic": "~13.2.0", + "@angular/router": "~13.2.0", + "@nrwl/angular": "13.8.5", + "rxjs": "~7.4.0", + "tslib": "^2.0.0", + "zone.js": "~0.11.4" + }, + "devDependencies": { + "@angular-devkit/build-angular": "~13.2.0", + "@angular-eslint/eslint-plugin": "~13.0.1", + "@angular-eslint/eslint-plugin-template": "~13.0.1", + "@angular-eslint/template-parser": "~13.0.1", + "@angular/cli": "~13.2.0", + "@angular/compiler-cli": "~13.2.0", + "@angular/language-service": "~13.2.0", + "@nrwl/cli": "13.8.5", + "@nrwl/cypress": "13.8.5", + "@nrwl/eslint-plugin-nx": "13.8.5", + "@nrwl/jest": "13.8.5", + "@nrwl/linter": "13.8.5", + "@nrwl/tao": "13.8.5", + "@nrwl/workspace": "13.8.5", + "@types/jest": "27.0.2", + "@types/node": "16.11.7", + "@typescript-eslint/eslint-plugin": "~5.10.0", + "@typescript-eslint/parser": "~5.10.0", + "cypress": "^9.1.0", + "eslint": "~8.7.0", + "eslint-config-prettier": "8.1.0", + "eslint-plugin-cypress": "^2.10.3", + "jest": "27.2.3", + "jest-preset-angular": "11.1.1", + "prettier": "^2.5.1", + "ts-jest": "27.0.5", + "typescript": "~4.5.2" + } +} \ No newline at end of file diff --git a/src/rz/json/snapshots/package-json/package.json.snap b/src/rz/json/snapshots/package-json/package.json.snap new file mode 100644 index 0000000..34dce3d --- /dev/null +++ b/src/rz/json/snapshots/package-json/package.json.snap @@ -0,0 +1,56 @@ +{ + "name": "razroo-nrwl-angular-shell", + "version": "0.0.0", + "license": "MIT", + "scripts": { + "ng": "nx", + "postinstall": "node ./decorate-angular-cli.js && ngcc --properties es2015 browser module main", + "start": "nx serve", + "build": "nx build", + "test": "nx test" + }, + "private": true, + "dependencies": { + "@angular/animations": "~13.2.0", + "@angular/common": "~13.2.0", + "@angular/compiler": "~13.2.0", + "@angular/core": "~13.2.0", + "@angular/forms": "~13.2.0", + "@angular/platform-browser": "~13.2.0", + "@angular/platform-browser-dynamic": "~13.2.0", + "@angular/router": "~13.2.0", + "@nrwl/angular": "13.8.5", + "rxjs": "~7.4.0", + "tslib": "^2.0.0", + "zone.js": "~0.11.4" + }, + "devDependencies": { + "@angular-devkit/build-angular": "~13.2.0", + "@angular-eslint/eslint-plugin": "~13.0.1", + "@angular-eslint/eslint-plugin-template": "~13.0.1", + "@angular-eslint/template-parser": "~13.0.1", + "@angular/cli": "~13.2.0", + "@angular/compiler-cli": "~13.2.0", + "@angular/language-service": "~13.2.0", + "@nrwl/cli": "13.8.5", + "@nrwl/cypress": "13.8.5", + "@nrwl/eslint-plugin-nx": "13.8.5", + "@nrwl/jest": "13.8.5", + "@nrwl/linter": "13.8.5", + "@nrwl/tao": "13.8.5", + "@nrwl/workspace": "13.8.5", + "@types/jest": "27.0.2", + "@types/node": "16.11.7", + "@typescript-eslint/eslint-plugin": "~5.10.0", + "@typescript-eslint/parser": "~5.10.0", + "cypress": "^9.1.0", + "eslint": "~8.7.0", + "eslint-config-prettier": "8.1.0", + "eslint-plugin-cypress": "^2.10.3", + "jest": "27.2.3", + "jest-preset-angular": "11.1.1", + "prettier": "^2.5.1", + "ts-jest": "27.0.5", + "typescript": "~4.5.2" + } +} diff --git a/src/rz/json/snapshots/project-output.json.snap b/src/rz/json/snapshots/project-output.json.snap new file mode 100644 index 0000000..8dc9d80 --- /dev/null +++ b/src/rz/json/snapshots/project-output.json.snap @@ -0,0 +1,100 @@ +{ + "projectType": "application", + "root": "apps/razroo-nrwl-angular-shell", + "sourceRoot": "apps/razroo-nrwl-angular-shell/src", + "prefix": "razroo-nrwl-angular-shell", + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:browser", + "outputs": [ + "{options.outputPath}" + ], + "options": { + "outputPath": "dist/apps/razroo-nrwl-angular-shell", + "index": "apps/razroo-nrwl-angular-shell/src/index.html", + "main": "apps/razroo-nrwl-angular-shell/src/main.ts", + "polyfills": "apps/razroo-nrwl-angular-shell/src/polyfills.ts", + "tsConfig": "apps/razroo-nrwl-angular-shell/tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + "apps/razroo-nrwl-angular-shell/src/favicon.ico", + "apps/razroo-nrwl-angular-shell/src/assets" + ], + "styles": [ + "apps/razroo-nrwl-angular-shell/src/styles.scss" + ], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "fileReplacements": [ + { + "replace": "libs/common/environments/src/lib/environment.ts", + "with": "libs/common/environments/src/lib/environment.prod.ts" + } + ], + "outputHashing": "all" + }, + "development": { + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "browserTarget": "razroo-nrwl-angular-shell:build:production" + }, + "development": { + "browserTarget": "razroo-nrwl-angular-shell:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "browserTarget": "razroo-nrwl-angular-shell:build" + } + }, + "lint": { + "executor": "@nrwl/linter:eslint", + "options": { + "lintFilePatterns": [ + "apps/razroo-nrwl-angular-shell/src/**/*.ts", + "apps/razroo-nrwl-angular-shell/src/**/*.html" + ] + } + }, + "test": { + "executor": "@nrwl/jest:jest", + "outputs": [ + "coverage/apps/razroo-nrwl-angular-shell" + ], + "options": { + "jestConfig": "apps/razroo-nrwl-angular-shell/jest.config.js", + "passWithNoTests": true + } + } + }, + "tags": [] +} \ No newline at end of file diff --git a/src/rz/json/snapshots/project.json.snap b/src/rz/json/snapshots/project.json.snap new file mode 100644 index 0000000..56268f8 --- /dev/null +++ b/src/rz/json/snapshots/project.json.snap @@ -0,0 +1,94 @@ +{ + "projectType": "application", + "root": "apps/razroo-nrwl-angular-shell", + "sourceRoot": "apps/razroo-nrwl-angular-shell/src", + "prefix": "razroo-nrwl-angular-shell", + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:browser", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/razroo-nrwl-angular-shell", + "index": "apps/razroo-nrwl-angular-shell/src/index.html", + "main": "apps/razroo-nrwl-angular-shell/src/main.ts", + "polyfills": "apps/razroo-nrwl-angular-shell/src/polyfills.ts", + "tsConfig": "apps/razroo-nrwl-angular-shell/tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + "apps/razroo-nrwl-angular-shell/src/favicon.ico", + "apps/razroo-nrwl-angular-shell/src/assets" + ], + "styles": ["apps/razroo-nrwl-angular-shell/src/styles.scss"], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "fileReplacements": [ + { + "replace": "apps/razroo-nrwl-angular-shell/src/environments/environment.ts", + "with": "apps/razroo-nrwl-angular-shell/src/environments/environment.prod.ts" + } + ], + "outputHashing": "all" + }, + "development": { + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "browserTarget": "razroo-nrwl-angular-shell:build:production" + }, + "development": { + "browserTarget": "razroo-nrwl-angular-shell:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "browserTarget": "razroo-nrwl-angular-shell:build" + } + }, + "lint": { + "executor": "@nrwl/linter:eslint", + "options": { + "lintFilePatterns": [ + "apps/razroo-nrwl-angular-shell/src/**/*.ts", + "apps/razroo-nrwl-angular-shell/src/**/*.html" + ] + } + }, + "test": { + "executor": "@nrwl/jest:jest", + "outputs": ["coverage/apps/razroo-nrwl-angular-shell"], + "options": { + "jestConfig": "apps/razroo-nrwl-angular-shell/jest.config.js", + "passWithNoTests": true + } + } + }, + "tags": [] +} diff --git a/src/rz/morph/community-paths/community-paths.ts b/src/rz/morph/community-paths/community-paths.ts new file mode 100644 index 0000000..6fd68b4 --- /dev/null +++ b/src/rz/morph/community-paths/community-paths.ts @@ -0,0 +1,50 @@ +export enum CommunityPaths { + Angular = 'angular', + React = 'react', + Aws = 'aws', + Azure = 'azure', + Gcp = 'gcp', + Git = 'git', + Npm = 'npm', + Pnmp = 'pnpm', + Yarn = 'yarn', + Python = 'python', + Docker = 'docker' +} + +export const communityPaths = Object.values(CommunityPaths); + +export const supportedCommunityPaths = [ + { + id: 'angular', + displayName: 'Angular', + }, + { + id: 'react', + displayName: 'React', + }, + { + id: 'aws', + displayName: 'Aws', + }, + { + id: 'git', + displayName: 'Git', + }, + { + id: 'npm', + displayName: 'Npm', + }, + { + id: 'pnpm', + displayName: 'Pnmp', + }, + { + id: 'yarn', + displayName: 'Yarn', + }, + { + id: 'python', + displayName: 'Python', + } +]; \ No newline at end of file diff --git a/src/rz/morph/index.ts b/src/rz/morph/index.ts new file mode 100644 index 0000000..438270a --- /dev/null +++ b/src/rz/morph/index.ts @@ -0,0 +1,3 @@ +export { morphCode, effects, Parameters, types } from "./morph"; +export { EditFile, EditInput} from "./interfaces/morph.interface"; +export { communityPaths, supportedCommunityPaths } from "./community-paths/community-paths"; \ No newline at end of file diff --git a/src/rz/morph/interfaces/morph.interface.ts b/src/rz/morph/interfaces/morph.interface.ts new file mode 100644 index 0000000..8aed70d --- /dev/null +++ b/src/rz/morph/interfaces/morph.interface.ts @@ -0,0 +1,26 @@ +export interface EditInput { + fileType?: 'html' | 'ts' | string; + fileName: string; + filePath?: string; + fileToBeAddedTo?: string | any; + bodyText?: string; + edits: EditFile[]; +} + +export interface EditFile { + nodeType?: 'import' | 'export' | 'classDeclaration' | 'classMethod' | 'addNgModuleImport' | 'addNgModuleImportToSpec' | + 'addNgModuleProvider' | 'addNgModuleDeclaration' | 'addNgModuleProviderToSpec' | 'addToVariableObject' | 'editImport' | 'addConstructorMethod' | + 'addVariableDeclarationStatement' | 'addClassMethod' | 'addImportsToExisting' | 'addNgModuleExport' | 'addFunction'; + codeBlock: string | any | any[]; + isExported?: boolean; + nodeToInsertInto?: string; + fileToBeAddedTo?: string | any; + valueToModify?: any; + type?: string; + path?: string; + name?: string; + tagNameToInsert?: string; + siblingTagName?: string; + parameters?: any; + bodyText?: string; +} \ No newline at end of file diff --git a/src/rz/morph/morph.spec.ts b/src/rz/morph/morph.spec.ts new file mode 100644 index 0000000..ff7abbe --- /dev/null +++ b/src/rz/morph/morph.spec.ts @@ -0,0 +1,155 @@ +import { EditScssInput } from './../scss/interfaces/morph-scss.interface'; +import { readFileSync, writeFileSync } from 'fs'; +import { EditHtmlFile, EditHtmlInput } from '../angular/interfaces/edit-html.interface'; +import { EditInput } from './interfaces/morph.interface'; +import { morphCode } from "./morph"; + +describe('morph', () => { + const siblingCodeBlock = ` + `; + const modifyTagNameCodeBlock = 'matSort'; + + const addSiblingHtml: EditHtmlFile = { + nodeType: 'addSiblingHtml', + codeBlock: siblingCodeBlock, + tagNameToInsert: 'mat-paginator', + siblingTagName: 'table' + }; + + const updateHtmlTag: EditHtmlFile = { + nodeType: 'editHtmlTag', + codeBlock: modifyTagNameCodeBlock, + tagNameToModify: 'table', + }; + + const morphMock = [ + { + "fileType": "html", + "edits": [ + addSiblingHtml, + updateHtmlTag + ] + }, + { + "fileType": "ts", + "edits": [ + { + "nodeType": "import", + "codeBlock": "{ MatPaginator }", + "path": "@angular/material/paginator" + }, + { + "nodeType": "classDeclaration", + "codeBlock": "@ViewChild(MatPaginator) paginator", + "type": "MatPaginator" + }, + { + "nodeType": "classMethod", + "nodeToInsertInto": "ngAfterViewInit", + "codeBlock": "this.dataSource.paginator = this.paginator;", + "type": "MatPaginator" + } + ] + } +]; + it('should morph the html file', () => { + const fileToBeAddedTo = readFileSync('src/rz/angular/snapshots/data-table.component.html.snap').toString(); + + const editInput = { + ...morphMock[0], + fileToBeAddedTo, + } + const editedCode = morphCode(editInput as EditInput); + const expected = String(readFileSync('src/rz/angular/snapshots/data-table-output.component.html.snap')); + + expect(editedCode).toEqual(expected); + }); + + it('should morph the ts file', () => { + const fileToBeAddedTo = String(readFileSync('src/rz/typescript/snapshots/data-table.component.ts.snap')); + + const editInput = { + ...morphMock[1], + fileToBeAddedTo, + } + const editedCode = morphCode(editInput as EditInput); + const expected = String(readFileSync('src/rz/typescript/snapshots/data-table-output.component.ts.snap')); + + expect(editedCode).toEqual(expected); + }); + + it('should morph the scss file', () => { + const fileToBeAddedTo = readFileSync('src/rz/scss/snapshots/themes.scss.snap').toString(); + const codeBlock = `.LightMode { + background: #ffffff; + color: #000000; + }; + ` + + const editScssInput = { + fileToBeAddedTo: fileToBeAddedTo, + fileType: 'scss', + edits: [ + { + nodeType: 'addScssBlock', + codeBlock: codeBlock + } + ] + } + + const expected = readFileSync('src/rz/scss/snapshots/themes-output.scss.snap').toString(); + + expect(morphCode(editScssInput as EditScssInput)).toEqual(expected); + }); + + describe('insertIntoHtmlTag', () => { + it('should insert html into the specified tag if parent tag is div', () => { + const fileToBeAddedTo = readFileSync('src/rz/angular/snapshots/insert-into-html-tag/insert-into-html-tag.html.snap').toString(); + const codeBlock = '
new div content
'; + const insertIntoHtmlTag: EditHtmlFile = { + nodeType: 'insertIntoHtmlTag', + codeBlock: codeBlock, + tagNameToInsertInto: 'div' + }; + + const editHtmlInput: EditHtmlInput = { + fileType: 'html', + fileToBeAddedTo: fileToBeAddedTo, + edits: [ + insertIntoHtmlTag + ] + }; + + const expected = readFileSync('src/rz/angular/snapshots/insert-into-html-tag/insert-into-html-tag.html-output.snap').toString(); + const newHtmlString = morphCode(editHtmlInput); + expect(newHtmlString).toEqual(expected); + }); + + it('should insert html into the specified tag if parent tag is matToolbar', () => { + const fileToBeAddedTo = readFileSync('src/rz/angular/snapshots/insert-into-html-tag/insert-into-html-tag-header.html.snap').toString(); + const codeBlock = "
"; + const insertIntoHtmlTag: EditHtmlFile = { + nodeType: 'insertIntoHtmlTag', + codeBlock: codeBlock, + tagNameToInsertInto: 'mat-toolbar' + }; + + const editHtmlInput: EditHtmlInput = { + fileType: 'html', + fileToBeAddedTo: fileToBeAddedTo, + edits: [ + insertIntoHtmlTag + ] + }; + + const expected = readFileSync('src/rz/angular/snapshots/insert-into-html-tag/insert-into-html-tag-header-output.html.snap').toString(); + const newHtmlString = morphCode(editHtmlInput); + writeFileSync('src/rz/angular/snapshots/insert-into-html-tag/insert-into-html-tag-header-output.html.snap', newHtmlString) + expect(newHtmlString).toEqual(expected); + }); + }); +}) \ No newline at end of file diff --git a/src/rz/morph/morph.ts b/src/rz/morph/morph.ts new file mode 100644 index 0000000..eed5187 --- /dev/null +++ b/src/rz/morph/morph.ts @@ -0,0 +1,58 @@ +import { CommunityPaths } from './community-paths/community-paths'; +import { AngularOptionalType, AngularTypeNames, angularTypes } from './../angular/types/types'; +import { EditScssInput } from './../scss/interfaces/morph-scss.interface'; +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ +import { EditHtmlInput } from './../angular/interfaces/edit-html.interface'; +import { morphHtml } from "../angular/morph-angular-html"; +import { morphTypescript } from '../typescript/morph-typescript'; +import { morphScss } from '../scss/morph-scss'; +import { morphJson } from '../json/json-morph'; +import { EditJsonInput } from '../json/interfaces/json-morph.interface'; +import { angularEffects } from '../angular/angular-effects'; +import { reactTypes } from '../react'; +import { TemplateInputParameter } from '../utils/interfaces/template-parameters'; + +// takes in singular object and makes all edits to files +// used when editing a file +export function morphCode(editInput: any): string { + switch(editInput.fileType) { + case 'html': + return morphHtml(editInput as EditHtmlInput) + case 'ts': + case 'spec.ts': + return morphTypescript(editInput); + case 'scss': + return morphScss(editInput as EditScssInput); + case 'json': + return morphJson(editInput as EditJsonInput); + } +} + +export interface Parameters { + className?: string; + constantName?: string; +} + +// effects are called whenever a file is generated +// such as automatically exporting file in closes index ts file +// type respresents component, guard, pipe etc, which is specific to programming language +export function effects(filePathWithName: string, parameter: TemplateInputParameter, programmingLanguage: string, parameters?: string): void { + const parsedParemeters: Parameters = typeof parameters === 'string' ? JSON.parse(parameters) : parameters; + + switch(programmingLanguage) { + case 'angular': + angularEffects(filePathWithName, (parameter.type) as AngularTypeNames, parsedParemeters, (parameter.optionalTypes) as any as AngularOptionalType[]); + } +} + +export function types(programmingLanguage: string): any[] { + switch(programmingLanguage) { + case CommunityPaths.Angular: + return angularTypes; + case CommunityPaths.React: + return reactTypes; + default: + return []; + } + +} \ No newline at end of file diff --git a/src/rz/params/html-params/html-params.ts b/src/rz/params/html-params/html-params.ts new file mode 100644 index 0000000..977873c --- /dev/null +++ b/src/rz/params/html-params/html-params.ts @@ -0,0 +1,119 @@ +export function getHtmlParameters(optionName: string): any { + switch(optionName) { + case 'editHtmlTag': + return { + nodeType: 'editHtmlTag', + inputs: [ + { + name: 'codeBlock', + inputType: 'text', + description: 'New tag name to put in place of modified tag.', + codeExample: 'matSort' + }, + { + name: 'tagNameToModify', + inputType: 'text', + description: 'Tag name to modify.', + codeExample: 'mat-table' + } + ] + } + case 'addSiblingHtml': + return { + nodeType: 'addSiblingHtml', + inputs: [ + { + name: 'codeBlock', + inputType: 'code', + description: 'Code of html to insert after sibling html.', + codeExample: '' + }, + { + name: 'tagNameToInsert', + inputType: 'text', + description: 'Tag name of code that will be inserted in.', + codeExample: 'mat-paginator' + }, + { + name: 'siblingTagName', + inputType: 'text', + description: 'Tag name to insert code after', + codeExample: 'mat-table' + } + ] + }; + case 'insertIntoHtmlTag': + return { + nodeType: 'insertIntoHtmlTag', + inputs: [ + { + name: 'codeBlock', + inputType: 'code', + description: 'Code that will be inserted into html code.', + codeExample: '
' + }, + { + name: 'tagNameToInsertInto', + inputType: 'text', + description: 'Tag name of html we will be inserting into.', + codeExample: 'mat-toolbar' + } + ] + } + case 'deleteHtmlElement': + return { + nodeType: 'deleteHtmlElement', + inputs: [ + { + name: 'codeBlock', + inputType: 'code', + description: 'html element that will be delete', + codeExample: 'mat-toolbar' + }, + ] + } + case 'prependHtml': + return { + nodeType: 'prependHtml', + inputs: [ + { + name: 'codeBlock', + inputType: 'code', + description: 'html code to be prepended', + codeExample: 'global-header' + }, + { + name: 'tagNameToInsertInto', + inputType: 'code', + description: 'html tag to prepend code into', + codeExample: 'global-header' + } + ] + } + case 'appendHtml': + return { + nodeType: 'appendHtml', + inputs: [ + { + name: 'codeBlock', + inputType: 'code', + description: 'html element to go to end of file', + codeExample: 'global-header' + }, + { + name: 'tagNameToInsertInto', + inputType: 'code', + description: 'html tag to append code into', + codeExample: 'global-header' + } + ] + } + default: + return {} + } + } + + export function getHtmlOptions(): string[] { + return ['editHtmlTag', 'addSiblingHtml', 'insertIntoHtmlTag', 'deleteHtmlElement', 'prependHtml', + 'appendHtml']; + } \ No newline at end of file diff --git a/src/rz/params/index.ts b/src/rz/params/index.ts new file mode 100644 index 0000000..bf231d3 --- /dev/null +++ b/src/rz/params/index.ts @@ -0,0 +1 @@ +export { getCodeModParams, getCodeModOptions } from "./params"; \ No newline at end of file diff --git a/src/rz/params/json-params/json-params.ts b/src/rz/params/json-params/json-params.ts new file mode 100644 index 0000000..b30df13 --- /dev/null +++ b/src/rz/params/json-params/json-params.ts @@ -0,0 +1,52 @@ +export function getJsonParameters(optionName: string): any { + switch(optionName) { + case 'editJson': + return { + nodeType: 'editJson', + inputs: [ + { + name: 'valueToModify', + inputType: 'text', + description: 'Json key to modify.', + codeExample: 'fileReplacements' + }, + { + name: 'codeBlock', + description: 'Json value to put for key.', + codeExample: `[{ + "replace": "libs/common/environments/src/lib/environment.ts", + "with": "libs/common/environments/src/lib/environment.prod.ts" + }]`, + inputType: 'code' + } + ] + } + case 'addJsonKeyValue': + return { + nodeType: 'addJsonKeyValue', + inputs: [ + { + name: 'valueToModify', + inputType: 'text', + description: 'Json key to add more json key/values to', + codeExample: 'scripts' + }, + { + name: 'codeBlock', + inputType: 'code', + description: 'object of key/values to add to the json key', + codeExample: `{ + "compodoc": "compodoc -p tsconfig.json", + "compodoc-serve": "compodoc -s tsconfig.json" + }` + } + ] + }; + default: + return {} + } + } + + export function getJsonOptions(): string[] { + return ['editJson', 'addJsonKeyValue']; + } \ No newline at end of file diff --git a/src/rz/params/params.ts b/src/rz/params/params.ts new file mode 100644 index 0000000..9d0de78 --- /dev/null +++ b/src/rz/params/params.ts @@ -0,0 +1,32 @@ +import { getHtmlOptions, getHtmlParameters } from "./html-params/html-params"; +import { getJsonOptions, getJsonParameters } from "./json-params/json-params"; +import { getScssOptions, getScssParameters } from "./scss-params/scss-params"; +import { getTsOptions, getTsParameters } from "./typescript-params/typescript-params"; + +export function getCodeModParams(fileType: string, optionName: string): any { + switch(fileType) { + case 'html': + return getHtmlParameters(optionName) + case 'ts': + case 'spec.ts': + return getTsParameters(optionName); + case 'scss': + return getScssParameters(optionName); + case 'json': + return getJsonParameters(optionName); + } +} + +export function getCodeModOptions(fileType: string): any { + switch(fileType) { + case 'html': + return getHtmlOptions() + case 'ts': + case 'spec.ts': + return getTsOptions(); + case 'scss': + return getScssOptions(); + case 'json': + return getJsonOptions(); + } +} \ No newline at end of file diff --git a/src/rz/params/scss-params/scss-params.ts b/src/rz/params/scss-params/scss-params.ts new file mode 100644 index 0000000..15dd28f --- /dev/null +++ b/src/rz/params/scss-params/scss-params.ts @@ -0,0 +1,34 @@ +export function getScssParameters(optionName: string): any { + switch(optionName) { + case 'addScssBlock': + return { + nodeType: 'addScssBlock', + inputs: [ + { + name: 'codeBlock', + inputType: 'code', + description: 'Scss block of code to add to scss file.', + codeExample: '.Toolbar__Icons{display: flex; cursor: pointer; margin-left: auto; gap: 16px;}' + }, + ] + } + case 'import': + return { + nodeType: 'import', + inputs: [ + { + name: 'path', + inputType: 'text', + description: 'Import path to add to Scss file', + codeExample: '~normalize.css' + }, + ] + }; + default: + return {} + } + } + +export function getScssOptions(): string[] { + return ['addScssBlock', 'import']; +} diff --git a/src/rz/params/typescript-params/typescript-params.ts b/src/rz/params/typescript-params/typescript-params.ts new file mode 100644 index 0000000..da27ff2 --- /dev/null +++ b/src/rz/params/typescript-params/typescript-params.ts @@ -0,0 +1,256 @@ +export function getTsParameters(optionName: string): any { + switch(optionName) { + case 'import': + return { + nodeType: 'import', + inputs: [ + { + name: 'codeBlock', + inputType: 'text', + description: 'Import to add', + codeExample: '{ HttpClientModule }' + }, + { + name: 'path', + inputType: 'text', + description: 'Path of the import to add', + codeExample: '@angular/common/http' + }, + ] + } + case 'export': + return { + nodeType: 'export', + inputs: [ + { + name: 'codeBlock', + inputType: 'text', + description: 'Namespace of export and optional', + codeExample: 'dataTable' + }, + { + name: 'path', + inputType: 'text', + description: 'Path of the export to add', + codeExample: './data-table/data-table' + }, + ] + } + case 'addImportsToExisting': + return { + nodeType: 'addImportsToExisting', + inputs: [ + { + name: 'codeBlock', + inputType: 'text', + description: 'Named Import(s) to add', + codeExample: 'EventEmitter, Output' + }, + { + name: 'path', + inputType: 'text', + description: 'Existing path to add import(s) to', + codeExample: '@angular/common/http' + }, + ] + } + case 'editImport': + return { + nodeType: 'editImport', + inputs: [ + { + name: 'codeBlock', + inputType: 'text', + description: 'Import to edit', + codeExample: 'environment' + }, + { + name: 'path', + inputType: 'text', + description: 'Path of the import to edit', + codeExample: '@common-environments' + }, + ] + } + case 'classDeclaration': + return { + nodeType: 'classDeclaration', + inputs: [ + { + name: 'codeBlock', + inputType: 'text', + description: 'Code to add to top level inside of class.', + codeExample: 'faBell = faBell' + }, + { + name: 'type', + inputType: 'text', + description: 'Type to add to your code.', + codeExample: 'Function' + } + ] + } + case 'addClassMethod': + return { + nodeType: 'addClassMethod', + inputs: [ + { + name: 'codeBlock', + inputType: 'text', + description: 'Name of class method', + codeExample: 'determineStatus' + }, + { + name: 'type', + inputType: 'text', + description: 'Type of class method', + codeExample: 'string' + }, + { + name: 'parameters', + inputType: 'code', + description: 'Parameters to add to method', + codeExample: "[{name: 'event', type: 'any'}]" + }, + { + name: 'bodyText', + inputType: 'code', + description: 'Code to go inside of method', + codeExample: "this.toggle.emit(event);" + } + ] + } + case 'classMethod': + return { + nodeType: 'classMethod', + inputs: [ + { + name: 'nodeToInsertInto', + inputType: 'text', + description: 'Class method to insert code into.', + codeExample: 'ngAfterViewInit' + }, + { + name: 'codeBlock', + inputType: 'code', + description: 'Code to add into class method.', + codeExample: 'this.dataSource.sort = this.sort;' + }, + ] + } + case 'addNgModuleImport': + return { + nodeType: 'addNgModuleImport', + inputs: [ + { + name: 'codeBlock', + inputType: 'code', + description: 'ngModuleImport to add to imports array.', + codeExample: 'BrowserModule' + }, + ] + } + case 'addNgModuleExport': + return { + nodeType: 'addNgModuleExport', + inputs: [ + { + name: 'codeBlock', + inputType: 'code', + description: 'export to add to exports array.', + codeExample: 'HomeComponent' + }, + ] + } + case 'addNgModuleProvider': + return { + nodeType: 'addNgModuleProvider', + inputs: [ + { + name: 'codeBlock', + inputType: 'code', + description: 'Angular provider to add to providers array.', + codeExample: 'LanguageService' + }, + ] + } + case 'addNgModuleDeclaration': + return { + nodeType: 'addNgModuleDeclaration', + inputs: [ + { + name: 'codeBlock', + inputType: 'code', + description: 'Angular declaration to add to declarations array.', + codeExample: 'HomeComponent' + }, + ] + } + case 'addNgModuleImportToSpec': + return { + nodeType: 'addNgModuleImportToSpec', + inputs: [ + { + name: 'codeBlock', + inputType: 'code', + description: 'Angular provider to add to the spec providers array.', + codeExample: 'FontAwesomeModule' + }, + ] + } + case 'addToVariableObject': + return { + nodeType: 'addToVariableObject', + inputs: [ + { + name: 'codeBlock', + inputType: 'code', + description: 'Variable object to add to ts object file', + codeExample: `{ + "uri": "<%= prod-uri %>" + }` + }, + ] + } + case 'addConstructorMethod': + return { + nodeType: 'addConstructorMethod', + inputs: [ + { + name: 'codeBlock', + inputType: 'text', + description: 'Name of private method(left side)', + codeExample: 'router' + }, + { + name: 'type', + inputType: 'text', + description: 'Name of type of private method(right side)', + codeExample: 'Router' + } + ] + } + case 'addVariableDeclarationStatement': + return { + nodeType: 'addVariableDeclarationStatement', + inputs: [ + { + name: 'codeBlock', + inputType: 'text', + description: 'Name of variable', + codeExample: 'gtag' + }, + { + name: 'type', + inputType: 'text', + description: 'Type of variable', + codeExample: 'Function' + } + ] + } + } +} + +export function getTsOptions(): string[] { + return ['import', 'export', 'addImportsToExisting', 'editImport', 'classDeclaration', 'addClassMethod', 'classMethod', 'addNgModuleImport', 'addNgModuleExport', 'addNgModuleProvider', 'addNgModuleDeclaration', 'addNgModuleImportToSpec', 'addToVariableObject', 'addConstructorMethod', 'addVariableDeclarationStatement']; +} \ No newline at end of file diff --git a/src/rz/react/index.ts b/src/rz/react/index.ts new file mode 100644 index 0000000..7c7c37e --- /dev/null +++ b/src/rz/react/index.ts @@ -0,0 +1 @@ +export { reactTypes, ReactTypeNames } from "./types/react-types"; \ No newline at end of file diff --git a/src/rz/react/types/react-types.ts b/src/rz/react/types/react-types.ts new file mode 100644 index 0000000..e118d3f --- /dev/null +++ b/src/rz/react/types/react-types.ts @@ -0,0 +1,22 @@ +export enum ReactTypeNames { + Generic = 'generic', + Class = 'class', + Component = 'component' +} + +export enum GlobalReactOptionNames { + +} + +export type ReactOptionalTypes = [{name: GlobalReactOptionNames, selected: boolean }]; + +export interface ReactType { + name: ReactTypeNames; + optionalTypes?: ReactOptionalTypes +} + +export const reactTypes: ReactType[] = [ + {name: ReactTypeNames.Generic}, + {name: ReactTypeNames.Class}, + {name: ReactTypeNames.Component}, +]; \ No newline at end of file diff --git a/src/rz/scss/interfaces/morph-scss.interface.ts b/src/rz/scss/interfaces/morph-scss.interface.ts new file mode 100644 index 0000000..ef3586e --- /dev/null +++ b/src/rz/scss/interfaces/morph-scss.interface.ts @@ -0,0 +1,10 @@ +export interface EditScssInput { + fileToBeAddedTo?: string; + edits: EditScss[]; +} + +export interface EditScss { + nodeType: 'addScssBlock' | 'import'; + codeBlock?: string | any; + path?: string; +} \ No newline at end of file diff --git a/src/rz/scss/morph-scss.spec.ts b/src/rz/scss/morph-scss.spec.ts new file mode 100644 index 0000000..67f93d9 --- /dev/null +++ b/src/rz/scss/morph-scss.spec.ts @@ -0,0 +1,47 @@ +import { EditScssInput } from './interfaces/morph-scss.interface'; +import { readFileSync } from 'fs'; +import { morphScss } from "./morph-scss"; + +describe('morphScss', () => { + it('should be able to edit a scss file', () => { + const fileToBeAddedTo = readFileSync('src/rz/scss/snapshots/themes.scss.snap').toString(); + const codeBlock = `.LightMode { + background: #ffffff; + color: #000000; + }; + ` + + const editScssInput: EditScssInput = { + fileToBeAddedTo: fileToBeAddedTo, + edits: [ + { + nodeType: 'addScssBlock', + codeBlock: codeBlock + } + ] + } + + const expected = readFileSync('src/rz/scss/snapshots/themes-output.scss.snap').toString(); + + expect(morphScss(editScssInput)).toEqual(expected); + }); + + it('should add scss import to the top of a file', () => { + const fileToBeAddedTo = readFileSync('src/rz/scss/snapshots/add-import-output/add-import.scss.snap').toString(); + const path = `apps/<%= orgName%>/src/<%= orgName%>-styles.scss`; + + const editScssInput: EditScssInput = { + fileToBeAddedTo: fileToBeAddedTo, + edits: [ + { + nodeType: 'import', + path: path + } + ] + } + + const expected = readFileSync('src/rz/scss/snapshots/add-import-output/add-import-output.scss.snap').toString(); + + expect(morphScss(editScssInput)).toEqual(expected); + }); +}); \ No newline at end of file diff --git a/src/rz/scss/morph-scss.ts b/src/rz/scss/morph-scss.ts new file mode 100644 index 0000000..efeaaa4 --- /dev/null +++ b/src/rz/scss/morph-scss.ts @@ -0,0 +1,60 @@ +import { EditScssInput, EditScss } from "./interfaces/morph-scss.interface"; + +import { parse, stringify } from 'scss-parser'; +const parserPostcss = require('prettier/parser-postcss'); +const prettier = require('prettier'); + +export function morphScss(editScssInput: EditScssInput): string { + const ast = parse(editScssInput.fileToBeAddedTo); + + editScssInput.edits.forEach((editScss: EditScss) => { + switch(editScss.nodeType) { + case 'addScssBlock': + addScssCodeBlock(editScss, ast) + break; + case 'import': + addScssImport(editScss, ast) + break; + } + + }); + + return prettifyAndReturnString(ast); +} + +function prettifyAndReturnString(ast) { + return prettier.format(stringify(ast), { + parser: "scss", + plugins: [parserPostcss], + tabWidth: 2, + }); +} + +// css-parser is a bit quirky. Taps into AST to manually create space +function addLineToAst(ast) { + ast.value.push({type: 'space', value: '\n'}); +} + +function addScssCodeBlock(editScss: EditScss, ast): void { + const newCodeBlockAst = parse(editScss.codeBlock); + addLineToAst(ast); + + newCodeBlockAst.value.forEach(item => { + ast.value.push(item); + }); +} + +function addScssImport(editScss: EditScss, ast): void { + const atRulifiedPath = _atRulifyPath(editScss.path); + const newCodeBlockAst = parse(atRulifiedPath); + // adding line after import as this will be added to top + addLineToAst(newCodeBlockAst); + // using unshift to add line to the top of file + newCodeBlockAst.value.forEach(item => { + ast.value.unshift(item); + }); +} + +function _atRulifyPath(path: string) { + return `@import "${path}";`; +} \ No newline at end of file diff --git a/src/rz/scss/snapshots/add-import-output/add-import-output.scss.snap b/src/rz/scss/snapshots/add-import-output/add-import-output.scss.snap new file mode 100644 index 0000000..7ec7e70 --- /dev/null +++ b/src/rz/scss/snapshots/add-import-output/add-import-output.scss.snap @@ -0,0 +1,5 @@ +@import "apps/<%= orgName%>/src/<%= orgName%>-styles.scss"; +.DarkMode { + background: #000000; + color: #ffffff; +} diff --git a/src/rz/scss/snapshots/add-import-output/add-import.scss.snap b/src/rz/scss/snapshots/add-import-output/add-import.scss.snap new file mode 100644 index 0000000..b938fe6 --- /dev/null +++ b/src/rz/scss/snapshots/add-import-output/add-import.scss.snap @@ -0,0 +1,4 @@ +.DarkMode { + background: #000000; + color: #ffffff; +} \ No newline at end of file diff --git a/src/rz/scss/snapshots/themes-output.scss.snap b/src/rz/scss/snapshots/themes-output.scss.snap new file mode 100644 index 0000000..aa816d0 --- /dev/null +++ b/src/rz/scss/snapshots/themes-output.scss.snap @@ -0,0 +1,8 @@ +.DarkMode { + background: #000000; + color: #ffffff; +} +.LightMode { + background: #ffffff; + color: #000000; +} diff --git a/src/rz/scss/snapshots/themes.scss.snap b/src/rz/scss/snapshots/themes.scss.snap new file mode 100644 index 0000000..07971b9 --- /dev/null +++ b/src/rz/scss/snapshots/themes.scss.snap @@ -0,0 +1,4 @@ +.DarkMode { + background: #000000; + color: #ffffff; +} \ No newline at end of file diff --git a/src/rz/typescript/add-class-method/add-class-method.spec.ts b/src/rz/typescript/add-class-method/add-class-method.spec.ts new file mode 100644 index 0000000..45a571d --- /dev/null +++ b/src/rz/typescript/add-class-method/add-class-method.spec.ts @@ -0,0 +1,28 @@ +import { EditInput } from "src/rz/morph/interfaces/morph.interface"; +import { readFileSync } from 'fs'; +import { morphTypescript } from '../morph-typescript'; + +describe('addClassMethod', () => { + it('should create a class method', () => { + const editTypescriptInput: EditInput = { + fileToBeAddedTo: readFileSync('src/rz/typescript/snapshots/add-class-method/add-class-method.ts.snap').toString(), + fileName: 'name', + edits: [ + { + nodeType: 'addClassMethod', + codeBlock: 'determineStatus', + type: 'string', + parameters: "[{name: 'event', type: 'any'}]", + bodyText: "this.toggle.emit(event);" + } + ] + }; + + const typescriptString = morphTypescript(editTypescriptInput); + + const expected = readFileSync('src/rz/typescript/snapshots/add-class-method/add-class-method-output.ts.snap').toString(); + + expect(typescriptString).toEqual(expected); + }); + +}); \ No newline at end of file diff --git a/src/rz/typescript/add-class-method/add-class-method.ts b/src/rz/typescript/add-class-method/add-class-method.ts new file mode 100644 index 0000000..a99322d --- /dev/null +++ b/src/rz/typescript/add-class-method/add-class-method.ts @@ -0,0 +1,19 @@ +import { EditCodeBlockInput } from "../interfaces/edit-typescript.interface"; + +export function addClassMethod(editCodeBlockInput: EditCodeBlockInput): void { + const classDeclaration = editCodeBlockInput.sourceFile.getClasses()[0]; + editCodeBlockInput.parameters; + const addMethod = { + isStatic: false, + name: editCodeBlockInput.codeBlock, + returnType: editCodeBlockInput.type, + } + if(editCodeBlockInput.parameters) { + const parameters = eval(editCodeBlockInput.parameters) as unknown as {name: string, type: string}[]; + addMethod['parameters'] = parameters + } + + classDeclaration.addMethod({ + ...addMethod, + }).setBodyText(editCodeBlockInput.bodyText ? editCodeBlockInput.bodyText : ''); +} \ No newline at end of file diff --git a/src/rz/typescript/add-constructor-method/add-constructor-method.spec.ts b/src/rz/typescript/add-constructor-method/add-constructor-method.spec.ts new file mode 100644 index 0000000..6ccf7ca --- /dev/null +++ b/src/rz/typescript/add-constructor-method/add-constructor-method.spec.ts @@ -0,0 +1,45 @@ +import { readFileSync } from 'fs'; +import { EditInput } from '../../morph/interfaces/morph.interface'; +import { morphTypescript } from '../morph-typescript'; + +describe('addConstructorMethod', () => { + it('should add a constructor to a private method if existing constructor', () => { + const editTypescriptInput: EditInput = { + fileToBeAddedTo: readFileSync('src/rz/typescript/add-constructor-method/snapshots/add-constructor-method/add-constructor-method.ts.snap').toString(), + fileName: 'name', + edits: [ + { + nodeType: 'addConstructorMethod', + codeBlock: 'userService', + type: 'UserService' + } + ] + }; + + const typescriptString = morphTypescript(editTypescriptInput); + + const expected = readFileSync('src/rz/typescript/add-constructor-method/snapshots/add-constructor-method/add-constructor-method-output.ts.snap').toString(); + + expect(typescriptString).toEqual(expected); + }); + + it('should add a constructor to a private method even if no existing constructor', () => { + const editTypescriptInput: EditInput = { + fileToBeAddedTo: readFileSync('src/rz/typescript/add-constructor-method/snapshots/add-constructor-method/add-constructor-method-no-constructor.ts.snap').toString(), + fileName: 'name', + edits: [ + { + nodeType: 'addConstructorMethod', + codeBlock: 'userService', + type: 'UserService' + } + ] + }; + + const typescriptString = morphTypescript(editTypescriptInput); + + const expected = readFileSync('src/rz/typescript/add-constructor-method/snapshots/add-constructor-method/add-constructor-method-output.ts.snap').toString(); + + expect(typescriptString).toEqual(expected); + }); +}) diff --git a/src/rz/typescript/add-constructor-method/add-constructor-method.ts b/src/rz/typescript/add-constructor-method/add-constructor-method.ts new file mode 100644 index 0000000..23891b7 --- /dev/null +++ b/src/rz/typescript/add-constructor-method/add-constructor-method.ts @@ -0,0 +1,14 @@ +import { EditCodeBlockInput } from "../interfaces/edit-typescript.interface"; +import { Scope } from "ts-morph"; + +// right now assumes all methods will be private +export function addConstructorMethod(editCodeBlockInput: EditCodeBlockInput): void { + const classDeclaration = editCodeBlockInput.sourceFile.getClasses()[0]; + + let classConstructor = classDeclaration.getConstructors()[0]; + if (!classConstructor) { + classConstructor = classDeclaration.addConstructor(); + } + + classConstructor.addParameter({scope: Scope.Private, name: editCodeBlockInput.codeBlock, type: editCodeBlockInput.type}); +} \ No newline at end of file diff --git a/src/rz/typescript/add-constructor-method/snapshots/add-constructor-method/add-constructor-method-no-constructor.ts.snap b/src/rz/typescript/add-constructor-method/snapshots/add-constructor-method/add-constructor-method-no-constructor.ts.snap new file mode 100644 index 0000000..6fa7c32 --- /dev/null +++ b/src/rz/typescript/add-constructor-method/snapshots/add-constructor-method/add-constructor-method-no-constructor.ts.snap @@ -0,0 +1,10 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'razroo-test', + templateUrl: './razroo-test.component.html', + styleUrls: ['./razroo-test.component.scss'], +}) +export class RazrooTestComponent implements OnInit { + ngOnInit(): void {} +} \ No newline at end of file diff --git a/src/rz/typescript/add-constructor-method/snapshots/add-constructor-method/add-constructor-method-output.ts.snap b/src/rz/typescript/add-constructor-method/snapshots/add-constructor-method/add-constructor-method-output.ts.snap new file mode 100644 index 0000000..67d6c15 --- /dev/null +++ b/src/rz/typescript/add-constructor-method/snapshots/add-constructor-method/add-constructor-method-output.ts.snap @@ -0,0 +1,13 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'razroo-test', + templateUrl: './razroo-test.component.html', + styleUrls: ['./razroo-test.component.scss'], +}) +export class RazrooTestComponent implements OnInit { + ngOnInit(): void { } + + constructor(private userService: UserService) { + } +} diff --git a/src/rz/typescript/add-constructor-method/snapshots/add-constructor-method/add-constructor-method.ts.snap b/src/rz/typescript/add-constructor-method/snapshots/add-constructor-method/add-constructor-method.ts.snap new file mode 100644 index 0000000..78e7fe5 --- /dev/null +++ b/src/rz/typescript/add-constructor-method/snapshots/add-constructor-method/add-constructor-method.ts.snap @@ -0,0 +1,13 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'razroo-test', + templateUrl: './razroo-test.component.html', + styleUrls: ['./razroo-test.component.scss'], +}) +export class RazrooTestComponent implements OnInit { + ngOnInit(): void {} + + constructor() { + } +} \ No newline at end of file diff --git a/src/rz/typescript/add-export/add-export.spec.ts b/src/rz/typescript/add-export/add-export.spec.ts new file mode 100644 index 0000000..b9f79c4 --- /dev/null +++ b/src/rz/typescript/add-export/add-export.spec.ts @@ -0,0 +1,26 @@ +import { EditInput } from "src/rz/morph/interfaces/morph.interface"; +import { readFileSync } from 'fs'; +import { morphTypescript } from '../morph-typescript'; + +describe('addExportToExisting', () => { + it('should add an import(s) to an existing path', () => { + const editTypescriptInput: EditInput = { + fileToBeAddedTo: readFileSync('src/rz/typescript/add-export/snapshots/add-export.ts.snap').toString(), + fileName: 'name', + edits: [ + { + nodeType: 'export', + path: './component/component-name', + codeBlock: undefined + } + ] + }; + + const typescriptString = morphTypescript(editTypescriptInput); + + const expected = readFileSync('src/rz/typescript/add-export/snapshots/add-export-output.ts.snap').toString(); + + expect(typescriptString).toEqual(expected); + }); + +}) \ No newline at end of file diff --git a/src/rz/typescript/add-export/add-export.ts b/src/rz/typescript/add-export/add-export.ts new file mode 100644 index 0000000..5c7cf72 --- /dev/null +++ b/src/rz/typescript/add-export/add-export.ts @@ -0,0 +1,10 @@ +import { EditCodeBlockInput } from "../interfaces/edit-typescript.interface"; + +// importName: "{ MatPaginator }", +// importPath: "@angular/material/paginator", +export function addExportToTypescriptFile(editCodeBlockInput: EditCodeBlockInput): void { + editCodeBlockInput.sourceFile.addExportDeclaration({ + moduleSpecifier: editCodeBlockInput.path, + namespaceExport: editCodeBlockInput.codeBlock as string, + }); +} \ No newline at end of file diff --git a/src/rz/typescript/add-export/snapshots/add-export-output.ts.snap b/src/rz/typescript/add-export/snapshots/add-export-output.ts.snap new file mode 100644 index 0000000..e89d66c --- /dev/null +++ b/src/rz/typescript/add-export/snapshots/add-export-output.ts.snap @@ -0,0 +1 @@ +export * from "./component/component-name"; diff --git a/src/rz/typescript/add-export/snapshots/add-export.ts.snap b/src/rz/typescript/add-export/snapshots/add-export.ts.snap new file mode 100644 index 0000000..e69de29 diff --git a/src/rz/typescript/add-function/add-function.spec.ts b/src/rz/typescript/add-function/add-function.spec.ts new file mode 100644 index 0000000..ea3f491 --- /dev/null +++ b/src/rz/typescript/add-function/add-function.spec.ts @@ -0,0 +1,32 @@ +import { EditInput } from './../../morph/interfaces/morph.interface'; +import { readFileSync } from 'fs'; +import { morphTypescript } from '../morph-typescript'; +describe('addFunction', () => { + it('should add a function to a ts file', () => { + const editTypescriptInput: EditInput = { + fileToBeAddedTo: readFileSync('src/rz/typescript/add-function/snapshots/add-function.ts.snap').toString(), + fileName: 'name', + edits: [ + { + nodeType: 'addFunction', + name: 'generateAngularComponent', + type: 'string', + isExported: true, + parameters: [{name: 'test', type: 'string'}], + codeBlock: ` + return vscode.commands.registerCommand( + GENERATE_ANGULAR_COMPONENT, + async ({path}) => createScaffold('angular-15.0.0', 'angular-core', path, context, isProduction, 'component', packageJsonParams) + ); + ` + } + ] + }; + + const typescriptString = morphTypescript(editTypescriptInput); + + const expected = readFileSync('src/rz/typescript/add-function/snapshots/add-function-output.ts.snap').toString(); + + expect(typescriptString).toEqual(expected); + }) +}) \ No newline at end of file diff --git a/src/rz/typescript/add-function/add-function.ts b/src/rz/typescript/add-function/add-function.ts new file mode 100644 index 0000000..9c27e54 --- /dev/null +++ b/src/rz/typescript/add-function/add-function.ts @@ -0,0 +1,13 @@ +import { EditCodeBlockInput } from "../interfaces/edit-typescript.interface"; + +export function addFunction(editCodeBlockInput: EditCodeBlockInput): void { + const sourceFile = editCodeBlockInput.sourceFile; + + sourceFile.addFunction({ + name: editCodeBlockInput.name, + returnType: editCodeBlockInput.type, + statements: editCodeBlockInput.codeBlock, + isExported: editCodeBlockInput.isExported, + parameters: editCodeBlockInput.parameters + }); +} \ No newline at end of file diff --git a/src/rz/typescript/add-function/snapshots/add-function-output.ts.snap b/src/rz/typescript/add-function/snapshots/add-function-output.ts.snap new file mode 100644 index 0000000..0e03a18 --- /dev/null +++ b/src/rz/typescript/add-function/snapshots/add-function-output.ts.snap @@ -0,0 +1,11 @@ +export function pushScaffoldCommands(context, isProduction: boolean, packageJsonParams) { +} + +export function generateAngularComponent(test: string): string { + + return vscode.commands.registerCommand( + GENERATE_ANGULAR_COMPONENT, + async ({ path }) => createScaffold('angular-15.0.0', 'angular-core', path, context, isProduction, 'component', packageJsonParams) + ); + +} diff --git a/src/rz/typescript/add-function/snapshots/add-function.ts.snap b/src/rz/typescript/add-function/snapshots/add-function.ts.snap new file mode 100644 index 0000000..18991aa --- /dev/null +++ b/src/rz/typescript/add-function/snapshots/add-function.ts.snap @@ -0,0 +1,2 @@ +export function pushScaffoldCommands(context, isProduction: boolean, packageJsonParams) { +} \ No newline at end of file diff --git a/src/rz/typescript/add-import-to-existing/add-import-to-existing.spec.ts b/src/rz/typescript/add-import-to-existing/add-import-to-existing.spec.ts new file mode 100644 index 0000000..5980f93 --- /dev/null +++ b/src/rz/typescript/add-import-to-existing/add-import-to-existing.spec.ts @@ -0,0 +1,26 @@ +import { EditInput } from "src/rz/morph/interfaces/morph.interface"; +import { readFileSync } from 'fs'; +import { morphTypescript } from '../morph-typescript'; + +describe('addImportsToExisting', () => { + it('should add an import(s) to an existing path', () => { + const editTypescriptInput: EditInput = { + fileToBeAddedTo: readFileSync('src/rz/typescript/snapshots/add-import-to-existing/add-import-to-existing.ts.snap').toString(), + fileName: 'name', + edits: [ + { + nodeType: 'addImportsToExisting', + path: '@angular/core', + codeBlock: 'EventEmitter, Output', + } + ] + }; + + const typescriptString = morphTypescript(editTypescriptInput); + + const expected = readFileSync('src/rz/typescript/snapshots/add-import-to-existing/add-import-to-existing-output.ts.snap').toString(); + + expect(typescriptString).toEqual(expected); + }); + +}) \ No newline at end of file diff --git a/src/rz/typescript/add-import-to-existing/add-import-to-existing.ts b/src/rz/typescript/add-import-to-existing/add-import-to-existing.ts new file mode 100644 index 0000000..5c92e4e --- /dev/null +++ b/src/rz/typescript/add-import-to-existing/add-import-to-existing.ts @@ -0,0 +1,12 @@ +import { EditCodeBlockInput } from "../interfaces/edit-typescript.interface"; + +export function addImportsToExisting(editCodeBlockInput: EditCodeBlockInput): void { + const importToModify = editCodeBlockInput.sourceFile.getImportDeclarations() + .filter(importDeclartation => { + return importDeclartation.getModuleSpecifierValue() === editCodeBlockInput.path + }) + + // Assumes that imports will be comma separated + const namedImports = editCodeBlockInput.codeBlock.split(','); + importToModify[0].addNamedImports(namedImports); +} \ No newline at end of file diff --git a/src/rz/typescript/add-variable-declaration-statement/add-variable-declaration-statement.ts b/src/rz/typescript/add-variable-declaration-statement/add-variable-declaration-statement.ts new file mode 100644 index 0000000..dd20b86 --- /dev/null +++ b/src/rz/typescript/add-variable-declaration-statement/add-variable-declaration-statement.ts @@ -0,0 +1,13 @@ +import { EditCodeBlockInput } from "../interfaces/edit-typescript.interface"; + +// right now assumes all methods will be private +export function addVariableDeclarationStatement(editCodeBlockInput: EditCodeBlockInput): void { + const sourceFile = editCodeBlockInput.sourceFile; + sourceFile.insertVariableStatement(0, { + hasDeclareKeyword: true, + declarations: [{ + name: editCodeBlockInput.codeBlock, + type: editCodeBlockInput.type + }] + }); +} \ No newline at end of file diff --git a/src/rz/typescript/angular-typescript/add-ngmodule-import/ngmodule-imports.spec.ts b/src/rz/typescript/angular-typescript/add-ngmodule-import/ngmodule-imports.spec.ts new file mode 100644 index 0000000..562aae1 --- /dev/null +++ b/src/rz/typescript/angular-typescript/add-ngmodule-import/ngmodule-imports.spec.ts @@ -0,0 +1,28 @@ +import { readFileSync } from 'fs'; +import { EditInput } from "../../../morph/interfaces/morph.interface"; +import { morphTypescript } from '../../morph-typescript'; + +describe('addNgModuleImport', () => { + it('should add an import to the angular ngModule', () => { + const editTypescriptInput: EditInput = { + fileToBeAddedTo: readFileSync('src/rz/typescript/snapshots/angular-typescript/add-ngmodule-import/add-ngmodule-import.ts.snap').toString(), + fileName: 'name', + edits: [ + { + nodeType: 'import', + codeBlock: '{ MatSortModule }', + path: '@angular/material/sort' + }, + { + nodeType: 'addNgModuleImport', + codeBlock: 'MatSortModule', + } + ] + }; + + const typescriptString = morphTypescript(editTypescriptInput); + const expected = readFileSync('src/rz/typescript/snapshots/angular-typescript/add-ngmodule-import/add-ngmodule-import-output.ts.snap').toString(); + + expect(typescriptString).toEqual(expected); + }); +}) \ No newline at end of file diff --git a/src/rz/typescript/angular-typescript/add-ngmodule-import/ngmodule-imports.ts b/src/rz/typescript/angular-typescript/add-ngmodule-import/ngmodule-imports.ts new file mode 100644 index 0000000..5aff6d0 --- /dev/null +++ b/src/rz/typescript/angular-typescript/add-ngmodule-import/ngmodule-imports.ts @@ -0,0 +1,15 @@ +import { SyntaxKind, ts } from "ts-morph"; +import { EditCodeBlockInput } from "../../interfaces/edit-typescript.interface"; + +export function addNgModuleImport(editCodeBlockInput: EditCodeBlockInput): void { + const ngModuleClass = editCodeBlockInput.sourceFile.getClass(c => c.getText().includes('@NgModule')); + const ngModuleDecorator = ngModuleClass.getDecorator('NgModule'); + const moduleArguments = ngModuleDecorator.getArguments()[0]; + + const declarationsProp = moduleArguments.getDescendants() + .find(d => d.getKind() === SyntaxKind.PropertyAssignment && + (d.compilerNode as ts.PropertyAssignment).name.getText() === "imports"); + + const array = declarationsProp.getFirstChildByKindOrThrow(SyntaxKind.ArrayLiteralExpression); + array.addElement(editCodeBlockInput.codeBlock); +} \ No newline at end of file diff --git a/src/rz/typescript/angular-typescript/add-ngmodule-item/add-ngmodule-item.spec.ts b/src/rz/typescript/angular-typescript/add-ngmodule-item/add-ngmodule-item.spec.ts new file mode 100644 index 0000000..894bfe3 --- /dev/null +++ b/src/rz/typescript/angular-typescript/add-ngmodule-item/add-ngmodule-item.spec.ts @@ -0,0 +1,74 @@ +import { readFileSync } from 'fs'; +import { EditInput } from "../../../morph/interfaces/morph.interface"; +import { morphTypescript } from '../../morph-typescript'; + +describe('addNgModuleprovider', () => { + it('should add an provider to the angular ngModule', () => { + const editTypescriptInput: EditInput = { + fileToBeAddedTo: readFileSync('src/rz/typescript/snapshots/angular-typescript/add-ngmodule-provider/add-ngmodule-provider.ts.snap').toString(), + fileName: 'name', + edits: [ + { + nodeType: 'addNgModuleProvider', + codeBlock: `{ + provide: APOLLO_OPTIONS, + useFactory: (httpLink: HttpLink) => { + return { + cache: new InMemoryCache(), + link: httpLink.create({ + uri: environment.uri, + }), + }; + }, + deps: [HttpLink], + }`, + } + ] + }; + + const typescriptString = morphTypescript(editTypescriptInput); + const expected = readFileSync('src/rz/typescript/snapshots/angular-typescript/add-ngmodule-provider/add-ngmodule-provider-output.ts.snap').toString(); + + expect(typescriptString).toEqual(expected); + }); +}) + +describe('addNgModuleExport', () => { + it('should add an export to ngModule and add export if it doesnt already exist', () => { + const editTypescriptInput: EditInput = { + fileToBeAddedTo: readFileSync('src/rz/typescript/angular-typescript/snapshots/add-ngmodule-export/add-ngmodule-export.ts.snap').toString(), + fileName: 'name', + edits: [ + { + nodeType: 'addNgModuleExport', + codeBlock: `HomeComponent`, + } + ] + }; + + const typescriptString = morphTypescript(editTypescriptInput); + const expected = readFileSync('src/rz/typescript/angular-typescript/snapshots/add-ngmodule-export/add-ngmodule-export-output.ts.snap').toString(); + + expect(typescriptString).toEqual(expected); + }); +}); + +describe('addNgModuleImportToSpec', () => { + it('should add an import to the ngModule spec and add imports if it doesnt already exist', () => { + const editTypescriptInput: EditInput = { + fileToBeAddedTo: readFileSync('src/rz/typescript/angular-typescript/snapshots/add-spec-ngmodule-spec/add-spec-ngmodule-spec.ts.snap').toString(), + fileName: 'name', + edits: [ + { + nodeType: 'addNgModuleImportToSpec', + codeBlock: `FontAwesomeModule`, + } + ] + }; + + const typescriptString = morphTypescript(editTypescriptInput); + const expected = readFileSync('src/rz/typescript/angular-typescript/snapshots/add-spec-ngmodule-spec/add-spec-ngmodule-spec-output.ts.snap').toString(); + + expect(typescriptString).toEqual(expected); + }); +}); \ No newline at end of file diff --git a/src/rz/typescript/angular-typescript/add-ngmodule-item/add-ngmodule-item.ts b/src/rz/typescript/angular-typescript/add-ngmodule-item/add-ngmodule-item.ts new file mode 100644 index 0000000..275643b --- /dev/null +++ b/src/rz/typescript/angular-typescript/add-ngmodule-item/add-ngmodule-item.ts @@ -0,0 +1,72 @@ +import { SyntaxKind, ts } from "ts-morph"; +import { EditCodeBlockInput } from "../../interfaces/edit-typescript.interface"; + +export function addNgModuleItem(editCodeBlockInput: EditCodeBlockInput, itemNameToAdd = 'providers'): void { + const ngModuleClass = editCodeBlockInput.sourceFile.getClass(c => c.getText().includes('@NgModule')); + const ngModuleDecorator = ngModuleClass.getDecorator('NgModule'); + const moduleArguments = ngModuleDecorator.getArguments()[0]; + + const declarationsProp = moduleArguments.getDescendants() + .find(d => d.getKind() === SyntaxKind.PropertyAssignment && + (d.compilerNode as ts.PropertyAssignment).name.getText() === itemNameToAdd); + + // add object key / value if does not exist + if(!declarationsProp) { + const objectToUpdate = ngModuleDecorator.getDescendants().find(d => { + return d.getKind() === SyntaxKind.ObjectLiteralExpression + }); + + // this is an object which can know be updated + (objectToUpdate as any).addPropertyAssignment({ + name: itemNameToAdd, + initializer: `[${editCodeBlockInput.codeBlock}]`, + }) + } + else { + const array = declarationsProp.getFirstChildByKind(SyntaxKind.ArrayLiteralExpression); + array.addElement(editCodeBlockInput.codeBlock); + } +} + +export function addNgModuleItemToSpec(editCodeBlockInput: EditCodeBlockInput, itemNameToAdd = 'providers'): void { + const sourceFile = editCodeBlockInput.sourceFile; + const describeBlock = sourceFile.getDescendantsOfKind(SyntaxKind.ExpressionStatement)[0]; + console.log('describeBlock'); + console.log(describeBlock.getText()); + + const beforeEachStatement = describeBlock.getDescendants() + .find(d => { + return d.getKind() === SyntaxKind.CallExpression && + (d.compilerNode as ts.ExpressionStatement).getText().includes("configureTestingModule") + }); + + const ngModulePropItem = beforeEachStatement.getDescendants() + .find(d => d.getKind() === SyntaxKind.PropertyAssignment && + (d.compilerNode as ts.PropertyAssignment).name.getText() === itemNameToAdd); + + // add object key / value if does not exist + if(!ngModulePropItem) { + const objectToUpdate = beforeEachStatement.getDescendants().find(d => { + return d.getKind() === SyntaxKind.ObjectLiteralExpression + }); + + // this is an object which can know be updated + (objectToUpdate as any).addPropertyAssignment({ + name: itemNameToAdd, + initializer: `[${editCodeBlockInput.codeBlock}]`, + }) + } + else { + const array = ngModulePropItem.getFirstChildByKind(SyntaxKind.ArrayLiteralExpression); + array.addElement(editCodeBlockInput.codeBlock); + } +} + +export function addNgModuleProviderToSpec(editCodeBlockInput: EditCodeBlockInput): void { + const sourceFile = editCodeBlockInput.sourceFile; + const providersProp = sourceFile.getDescendants() + .find(d => d.getKind() === SyntaxKind.PropertyAssignment && + (d.compilerNode as ts.PropertyAssignment).name.getText() === "providers"); + const array = providersProp.getFirstChildByKindOrThrow(SyntaxKind.ArrayLiteralExpression); + array.addElement(editCodeBlockInput.codeBlock); +} \ No newline at end of file diff --git a/src/rz/typescript/angular-typescript/snapshots/add-ngmodule-export/add-ngmodule-export-output.ts.snap b/src/rz/typescript/angular-typescript/snapshots/add-ngmodule-export/add-ngmodule-export-output.ts.snap new file mode 100644 index 0000000..e63910c --- /dev/null +++ b/src/rz/typescript/angular-typescript/snapshots/add-ngmodule-export/add-ngmodule-export-output.ts.snap @@ -0,0 +1,9 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +@NgModule({ + providers: [], + imports: [CommonModule], + exports: [HomeComponent] +}) +export class HomeModule { } diff --git a/src/rz/typescript/angular-typescript/snapshots/add-ngmodule-export/add-ngmodule-export.ts.snap b/src/rz/typescript/angular-typescript/snapshots/add-ngmodule-export/add-ngmodule-export.ts.snap new file mode 100644 index 0000000..6454cc2 --- /dev/null +++ b/src/rz/typescript/angular-typescript/snapshots/add-ngmodule-export/add-ngmodule-export.ts.snap @@ -0,0 +1,8 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +@NgModule({ + providers: [], + imports: [CommonModule] +}) +export class HomeModule {} diff --git a/src/rz/typescript/angular-typescript/snapshots/add-spec-ngmodule-spec/add-spec-ngmodule-spec-output.ts.snap b/src/rz/typescript/angular-typescript/snapshots/add-spec-ngmodule-spec/add-spec-ngmodule-spec-output.ts.snap new file mode 100644 index 0000000..fa9a5a4 --- /dev/null +++ b/src/rz/typescript/angular-typescript/snapshots/add-spec-ngmodule-spec/add-spec-ngmodule-spec-output.ts.snap @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { GlobalHeaderComponent } from './global-header.component'; + +describe('GlobalComponent', () => { + let component: GlobalHeaderComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [GlobalHeaderComponent], + imports: [FontAwesomeModule] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(GlobalHeaderComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/rz/typescript/angular-typescript/snapshots/add-spec-ngmodule-spec/add-spec-ngmodule-spec.ts.snap b/src/rz/typescript/angular-typescript/snapshots/add-spec-ngmodule-spec/add-spec-ngmodule-spec.ts.snap new file mode 100644 index 0000000..a1c2603 --- /dev/null +++ b/src/rz/typescript/angular-typescript/snapshots/add-spec-ngmodule-spec/add-spec-ngmodule-spec.ts.snap @@ -0,0 +1,24 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { GlobalHeaderComponent } from './global-header.component'; + +describe('GlobalComponent', () => { + let component: GlobalHeaderComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ GlobalHeaderComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(GlobalHeaderComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); \ No newline at end of file diff --git a/src/rz/typescript/interfaces/edit-typescript.interface.ts b/src/rz/typescript/interfaces/edit-typescript.interface.ts new file mode 100644 index 0000000..b047bc5 --- /dev/null +++ b/src/rz/typescript/interfaces/edit-typescript.interface.ts @@ -0,0 +1,14 @@ +import { SourceFile } from "ts-morph"; + +// used for the individual function params +export interface EditCodeBlockInput { + sourceFile: SourceFile; + codeBlock: string | any; + nodeToInsertInto?: string; + type?: string; + name?: string; + path?: string; + parameters?: any; + bodyText?: string; + isExported?: boolean; +} \ No newline at end of file diff --git a/src/rz/typescript/morph-typescript.spec.ts b/src/rz/typescript/morph-typescript.spec.ts new file mode 100644 index 0000000..18f5156 --- /dev/null +++ b/src/rz/typescript/morph-typescript.spec.ts @@ -0,0 +1,77 @@ +import { readFileSync } from 'fs'; +import { EditInput } from '../morph/interfaces/morph.interface'; +import { morphTypescript } from './morph-typescript'; + +describe('EditTypescript', () => { + it('should insert typescript into a certain block of code', () => { + const editTypescriptInput: EditInput = { + fileToBeAddedTo: readFileSync('src/rz/typescript/snapshots/data-table.component.ts.snap').toString(), + fileName: 'name', + edits: [ + { + nodeType: 'import', + codeBlock: '{ MatPaginator }', + path: '@angular/material/paginator' + }, + { + nodeType: 'classDeclaration', + codeBlock: '@ViewChild(MatPaginator) paginator', + type: 'MatPaginator' + }, + { + nodeType: 'classMethod', + nodeToInsertInto: 'ngAfterViewInit', + codeBlock: 'this.dataSource.paginator = this.paginator;', + type: 'MatPaginator' + } + ] + }; + + const typescriptString = morphTypescript(editTypescriptInput); + + const expected = readFileSync('src/rz/typescript/snapshots/data-table-output.component.ts.snap').toString(); + + expect(typescriptString).toEqual(expected); + }); + + it('should update an existing typesscript import', () => { + const editTypescriptInput: EditInput = { + fileToBeAddedTo: readFileSync('src/rz/typescript/snapshots/edit-import/edit-import.ts.snap').toString(), + fileName: 'name', + edits: [ + { + nodeType: 'editImport', + codeBlock: 'environment', + path: '@common-environments' + } + ] + }; + + const typescriptString = morphTypescript(editTypescriptInput); + + const expected = readFileSync('src/rz/typescript/snapshots/edit-import/edit-import-output.ts.snap').toString(); + + expect(typescriptString).toEqual(expected); + }); + + it('should add a variable declaration statement to a file', () => { + const editTypescriptInput: EditInput = { + fileToBeAddedTo: readFileSync('src/rz/typescript/snapshots/add-variable-declaration-statement/add-variable-declaration-statement.ts.snap').toString(), + fileName: 'name', + edits: [ + { + nodeType: 'addVariableDeclarationStatement', + codeBlock: 'gtag', + type: 'Function' + } + ] + }; + + const typescriptString = morphTypescript(editTypescriptInput); + + const expected = readFileSync('src/rz/typescript/snapshots/add-variable-declaration-statement/add-variable-declaration-statement-output.ts.snap').toString(); + + expect(typescriptString).toEqual(expected); + }); + +}); \ No newline at end of file diff --git a/src/rz/typescript/morph-typescript.ts b/src/rz/typescript/morph-typescript.ts new file mode 100644 index 0000000..c6bd229 --- /dev/null +++ b/src/rz/typescript/morph-typescript.ts @@ -0,0 +1,132 @@ +import { Project, ScriptTarget, SourceFile, SyntaxKind } from "ts-morph"; +import { EditFile, EditInput } from "../morph/interfaces/morph.interface"; +import { addClassMethod } from "./add-class-method/add-class-method"; +import { addConstructorMethod } from "./add-constructor-method/add-constructor-method"; +import { addExportToTypescriptFile } from "./add-export/add-export"; +import { addFunction } from "./add-function/add-function"; +import { addImportsToExisting } from "./add-import-to-existing/add-import-to-existing"; +import { addVariableDeclarationStatement } from "./add-variable-declaration-statement/add-variable-declaration-statement"; +import { addNgModuleImport } from "./angular-typescript/add-ngmodule-import/ngmodule-imports"; +import { addNgModuleItem, addNgModuleItemToSpec } from "./angular-typescript/add-ngmodule-item/add-ngmodule-item"; +import { EditCodeBlockInput } from "./interfaces/edit-typescript.interface"; +import { addToVariableObject } from "./variable-statement/variable-statement"; + +// methodToInsertInto e.g. 'ngAfterViewInit' +// codeBeingAdded e.g. 'this.dataSource.paginator = this.paginator;' +function insertCodeIntoClassMethod(editCodeBlockInput: EditCodeBlockInput): void { + const classDeclaration = editCodeBlockInput.sourceFile.getClasses()[0]; + const currentBodyText = classDeclaration.getInstanceMethod(editCodeBlockInput.nodeToInsertInto).getBodyText(); + classDeclaration.getInstanceMethod(editCodeBlockInput.nodeToInsertInto).setBodyText( + `${currentBodyText} + ${editCodeBlockInput.codeBlock}`) +} + +// importName: "{ MatPaginator }", +// importPath: "@angular/material/paginator", +function insertImportToTypescriptFile(editCodeBlockInput: EditCodeBlockInput): void { + editCodeBlockInput.sourceFile.addImportDeclaration({ + defaultImport: editCodeBlockInput.codeBlock as string, + moduleSpecifier: editCodeBlockInput.path, + }); +} + +// e.g. replaces import { environment } from './environments/environment'; +// with import { environment } from "@common-environments"; +function editTypescriptImport(editCodeBlockInput: EditCodeBlockInput): void { + const testImport = editCodeBlockInput.sourceFile.getImportDeclarations() + .filter(importDeclartation => { + const namedImports = importDeclartation.getNamedImports(); + const namedImportsArr = namedImports.filter(namedImport => namedImport.getText() === editCodeBlockInput.codeBlock) + return namedImportsArr.length > 0; + }) + + testImport[0].setModuleSpecifier(editCodeBlockInput.path); +} + +// for now the index is at 0 and will always assume to insert at beginning of file +// class decleration also known as a property +function insertDeclarationIntoClass(editCodeBlockInput: EditCodeBlockInput): void { + const sourceFile = editCodeBlockInput.sourceFile; + const classDeclaration = sourceFile.getFirstDescendantByKindOrThrow(SyntaxKind.ClassDeclaration); + + classDeclaration.insertProperty(0, { + name: editCodeBlockInput.codeBlock as string, + type: editCodeBlockInput.type + }); +} + +function formatTypescriptFileAndReturnString(sourceFile: SourceFile): string { + sourceFile.formatText({ + indentSize: 2 + }); + + return sourceFile.getText(); +} + +export function morphTypescript(editTypescriptInput: EditInput): string { + const project = new Project({ + useInMemoryFileSystem: true, + compilerOptions: { + target: ScriptTarget.ES2019, + skipAddingFilesFromTsConfig: true + }, + }); + + const sourceFile = project.createSourceFile('editTypescriptInput.fileToBeAddedTo', editTypescriptInput.fileToBeAddedTo as string); + + editTypescriptInput.edits.forEach((edit: EditFile) => { + const editWithSourceFile = {...edit, sourceFile}; + switch (edit.nodeType) { + case 'import': + insertImportToTypescriptFile(editWithSourceFile) + break; + case 'export': + addExportToTypescriptFile(editWithSourceFile) + break; + case 'editImport': + editTypescriptImport(editWithSourceFile) + break; + case 'addImportsToExisting': + addImportsToExisting(editWithSourceFile) + break; + case 'classDeclaration': + insertDeclarationIntoClass(editWithSourceFile); + break; + case 'addFunction': + addFunction(editWithSourceFile); + break; + case 'classMethod': + insertCodeIntoClassMethod(editWithSourceFile); + break; + case 'addNgModuleImport': + addNgModuleImport(editWithSourceFile); + break; + case 'addNgModuleImportToSpec': + addNgModuleItemToSpec(editWithSourceFile, 'imports'); + break; + case 'addNgModuleProvider': + addNgModuleItem(editWithSourceFile, 'providers'); + break; + case 'addNgModuleDeclaration': + addNgModuleItem(editWithSourceFile, 'declarations'); + break; + case 'addNgModuleExport': + addNgModuleItem(editWithSourceFile, 'exports'); + break; + case 'addToVariableObject': + addToVariableObject(editWithSourceFile); + break; + case 'addConstructorMethod': + addConstructorMethod(editWithSourceFile); + break; + case 'addVariableDeclarationStatement': + addVariableDeclarationStatement(editWithSourceFile) + break; + case 'addClassMethod': + addClassMethod(editWithSourceFile) + break; + } + }); + + return formatTypescriptFileAndReturnString(sourceFile); +} \ No newline at end of file diff --git a/src/rz/typescript/snapshots/add-class-method/add-class-method-output.ts.snap b/src/rz/typescript/snapshots/add-class-method/add-class-method-output.ts.snap new file mode 100644 index 0000000..7389f33 --- /dev/null +++ b/src/rz/typescript/snapshots/add-class-method/add-class-method-output.ts.snap @@ -0,0 +1,16 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'razroo-test', + templateUrl: './razroo-test.component.html', + styleUrls: ['./razroo-test.component.scss'], +}) +export class RazrooTestComponent implements OnInit { + constructor() { } + + ngOnInit(): void { } + + determineStatus(event: any): string { + this.toggle.emit(event); + } +} diff --git a/src/rz/typescript/snapshots/add-class-method/add-class-method.ts.snap b/src/rz/typescript/snapshots/add-class-method/add-class-method.ts.snap new file mode 100644 index 0000000..ceabad9 --- /dev/null +++ b/src/rz/typescript/snapshots/add-class-method/add-class-method.ts.snap @@ -0,0 +1,12 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'razroo-test', + templateUrl: './razroo-test.component.html', + styleUrls: ['./razroo-test.component.scss'], +}) +export class RazrooTestComponent implements OnInit { + constructor() { } + + ngOnInit(): void {} +} \ No newline at end of file diff --git a/src/rz/typescript/snapshots/add-import-to-existing/add-import-to-existing-output.ts.snap b/src/rz/typescript/snapshots/add-import-to-existing/add-import-to-existing-output.ts.snap new file mode 100644 index 0000000..b70935a --- /dev/null +++ b/src/rz/typescript/snapshots/add-import-to-existing/add-import-to-existing-output.ts.snap @@ -0,0 +1,10 @@ +import { enableProdMode, EventEmitter, Output } from "@angular/core"; +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; + +if (environment.production) { + enableProdMode(); +} + +platformBrowserDynamic() + .bootstrapModule(AppModule) + .catch((err) => console.error(err)); diff --git a/src/rz/typescript/snapshots/add-import-to-existing/add-import-to-existing.ts.snap b/src/rz/typescript/snapshots/add-import-to-existing/add-import-to-existing.ts.snap new file mode 100644 index 0000000..99cff34 --- /dev/null +++ b/src/rz/typescript/snapshots/add-import-to-existing/add-import-to-existing.ts.snap @@ -0,0 +1,10 @@ +import { enableProdMode } from "@angular/core"; +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; + +if (environment.production) { + enableProdMode(); +} + +platformBrowserDynamic() + .bootstrapModule(AppModule) + .catch((err) => console.error(err)); diff --git a/src/rz/typescript/snapshots/add-variable-declaration-statement/add-variable-declaration-statement-output.ts.snap b/src/rz/typescript/snapshots/add-variable-declaration-statement/add-variable-declaration-statement-output.ts.snap new file mode 100644 index 0000000..48a962a --- /dev/null +++ b/src/rz/typescript/snapshots/add-variable-declaration-statement/add-variable-declaration-statement-output.ts.snap @@ -0,0 +1,20 @@ +declare let gtag: Function; + +import { Component, OnInit } from '@angular/core'; +import { NavigationEnd, Router } from '@angular/router'; +import { filter } from 'rxjs'; + +@Component({ + selector: '<%= projectName %>-root', + templateUrl: './app.component.html', + styleUrls: ['./app.component.scss'], +}) +export class AppComponent implements OnInit { + title = 'razroo-nrwl-angular-shell'; + + constructor(private router: Router) { } + + ngOnInit() { + this.setUpAnalytics(); + } +} diff --git a/src/rz/typescript/snapshots/add-variable-declaration-statement/add-variable-declaration-statement.ts.snap b/src/rz/typescript/snapshots/add-variable-declaration-statement/add-variable-declaration-statement.ts.snap new file mode 100644 index 0000000..13c27a5 --- /dev/null +++ b/src/rz/typescript/snapshots/add-variable-declaration-statement/add-variable-declaration-statement.ts.snap @@ -0,0 +1,18 @@ +import { Component, OnInit } from '@angular/core'; +import { NavigationEnd, Router } from '@angular/router'; +import { filter } from 'rxjs'; + +@Component({ + selector: '<%= projectName %>-root', + templateUrl: './app.component.html', + styleUrls: ['./app.component.scss'], +}) +export class AppComponent implements OnInit { + title = 'razroo-nrwl-angular-shell'; + + constructor(private router: Router) {} + + ngOnInit() { + this.setUpAnalytics(); + } +} diff --git a/src/rz/typescript/snapshots/angular-typescript/add-ngmodule-import-spec/add-ngmodule-import-output-spec.ts.snap b/src/rz/typescript/snapshots/angular-typescript/add-ngmodule-import-spec/add-ngmodule-import-output-spec.ts.snap new file mode 100644 index 0000000..58c699f --- /dev/null +++ b/src/rz/typescript/snapshots/angular-typescript/add-ngmodule-import-spec/add-ngmodule-import-output-spec.ts.snap @@ -0,0 +1,33 @@ +import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { MatTableModule } from '@angular/material/table'; +import { MatPaginatorModule } from '@angular/material/paginator'; +import { DataTableComponent } from './data-table.component'; +import { MatSortModule } from "@angular/material/sort"; + +describe('DataTableComponent', () => { + let component: DataTableComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [DataTableComponent], + imports: [ + NoopAnimationsModule, + MatPaginatorModule, + MatTableModule, + MatSortModule + ] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(DataTableComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should compile', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/rz/typescript/snapshots/angular-typescript/add-ngmodule-import-spec/add-ngmodule-import-spec.ts.snap b/src/rz/typescript/snapshots/angular-typescript/add-ngmodule-import-spec/add-ngmodule-import-spec.ts.snap new file mode 100644 index 0000000..f8ead93 --- /dev/null +++ b/src/rz/typescript/snapshots/angular-typescript/add-ngmodule-import-spec/add-ngmodule-import-spec.ts.snap @@ -0,0 +1,31 @@ +import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { MatTableModule } from '@angular/material/table'; +import { MatPaginatorModule } from '@angular/material/paginator'; +import { DataTableComponent } from './data-table.component'; + +describe('DataTableComponent', () => { + let component: DataTableComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [DataTableComponent], + imports: [ + NoopAnimationsModule, + MatPaginatorModule, + MatTableModule + ] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(DataTableComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should compile', () => { + expect(component).toBeTruthy(); + }); +}); \ No newline at end of file diff --git a/src/rz/typescript/snapshots/angular-typescript/add-ngmodule-import/add-ngmodule-import-output.ts.snap b/src/rz/typescript/snapshots/angular-typescript/add-ngmodule-import/add-ngmodule-import-output.ts.snap new file mode 100644 index 0000000..aa5ea95 --- /dev/null +++ b/src/rz/typescript/snapshots/angular-typescript/add-ngmodule-import/add-ngmodule-import-output.ts.snap @@ -0,0 +1,35 @@ +import { MatTableModule } from '@angular/material/table'; +import { CommonModule } from '@angular/common'; +import { AfterViewInit, Component, ViewChild, NgModule } from '@angular/core'; +import { MatPaginator, MatPaginatorModule } from '@angular/material/paginator'; +import { MatSortModule } from "@angular/material/sort"; + +/** + * @title Table with pagination + */ +@Component({ + selector: 'table-pagination-example', + styleUrls: ['table-pagination-example.css'], + templateUrl: 'table-pagination-example.html', +}) +export class MatDataTable implements AfterViewInit { + @ViewChild(MatPaginator) paginator: MatPaginator; + @ViewChild(MatSort) sort: MatSort; + + ngAfterViewInit() { + this.dataSource.paginator = this.paginator; + this.dataSource.sort = this.sort; + } +} + +@NgModule({ + imports: [ + CommonModule, + MatTableModule, + MatPaginatorModule, + MatSortModule + ], + declarations: [DataTableComponent], + exports: [DataTableComponent], +}) +export class DataTableModule { } diff --git a/src/rz/typescript/snapshots/angular-typescript/add-ngmodule-import/add-ngmodule-import.ts.snap b/src/rz/typescript/snapshots/angular-typescript/add-ngmodule-import/add-ngmodule-import.ts.snap new file mode 100644 index 0000000..0323597 --- /dev/null +++ b/src/rz/typescript/snapshots/angular-typescript/add-ngmodule-import/add-ngmodule-import.ts.snap @@ -0,0 +1,33 @@ +import { MatTableModule } from '@angular/material/table'; +import { CommonModule } from '@angular/common'; +import { AfterViewInit, Component, ViewChild, NgModule } from '@angular/core'; +import { MatPaginator, MatPaginatorModule } from '@angular/material/paginator'; + +/** + * @title Table with pagination + */ +@Component({ + selector: 'table-pagination-example', + styleUrls: ['table-pagination-example.css'], + templateUrl: 'table-pagination-example.html', +}) +export class MatDataTable implements AfterViewInit { + @ViewChild(MatPaginator) paginator: MatPaginator; + @ViewChild(MatSort) sort: MatSort; + + ngAfterViewInit() { + this.dataSource.paginator = this.paginator; + this.dataSource.sort = this.sort; + } +} + +@NgModule({ + imports: [ + CommonModule, + MatTableModule, + MatPaginatorModule + ], + declarations: [DataTableComponent], + exports: [DataTableComponent], +}) +export class DataTableModule { } \ No newline at end of file diff --git a/src/rz/typescript/snapshots/angular-typescript/add-ngmodule-provider/add-ngmodule-provider-output.ts.snap b/src/rz/typescript/snapshots/angular-typescript/add-ngmodule-provider/add-ngmodule-provider-output.ts.snap new file mode 100644 index 0000000..ada7b48 --- /dev/null +++ b/src/rz/typescript/snapshots/angular-typescript/add-ngmodule-provider/add-ngmodule-provider-output.ts.snap @@ -0,0 +1,19 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +@NgModule({ + imports: [CommonModule], + providers: [{ + provide: APOLLO_OPTIONS, + useFactory: (httpLink: HttpLink) => { + return { + cache: new InMemoryCache(), + link: httpLink.create({ + uri: environment.uri, + }), + }; + }, + deps: [HttpLink], + }] +}) +export class RazrooNrwlAngularShellDataGrapqhlModule { } diff --git a/src/rz/typescript/snapshots/angular-typescript/add-ngmodule-provider/add-ngmodule-provider.ts.snap b/src/rz/typescript/snapshots/angular-typescript/add-ngmodule-provider/add-ngmodule-provider.ts.snap new file mode 100644 index 0000000..bda6d3d --- /dev/null +++ b/src/rz/typescript/snapshots/angular-typescript/add-ngmodule-provider/add-ngmodule-provider.ts.snap @@ -0,0 +1,8 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +@NgModule({ + imports: [CommonModule], + providers: [] +}) +export class RazrooNrwlAngularShellDataGrapqhlModule {} diff --git a/src/rz/typescript/snapshots/data-table-output.component.ts.snap b/src/rz/typescript/snapshots/data-table-output.component.ts.snap new file mode 100644 index 0000000..7157b10 --- /dev/null +++ b/src/rz/typescript/snapshots/data-table-output.component.ts.snap @@ -0,0 +1,27 @@ +import { AfterViewInit, Component, ViewChild } from '@angular/core'; +import { MatTable } from '@angular/material/table'; +import { TestDataSource, TestItem } from './test-datasource'; +import { MatPaginator } from "@angular/material/paginator"; + +@Component({ + selector: 'razroo-zeta', + templateUrl: './test.component.html', + styleUrls: ['./test.component.scss'], +}) +export class TestComponent implements AfterViewInit { + @ViewChild(MatPaginator) paginator: MatPaginator; + @ViewChild(MatTable) table!: MatTable; + dataSource: TestDataSource; + + /** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */ + displayedColumns = ['id', 'name']; + + constructor() { + this.dataSource = new TestDataSource(); + } + + ngAfterViewInit(): void { + this.table.dataSource = this.dataSource; + this.dataSource.paginator = this.paginator; + } +} diff --git a/src/rz/typescript/snapshots/data-table.component.ts.snap b/src/rz/typescript/snapshots/data-table.component.ts.snap new file mode 100644 index 0000000..b18a8f2 --- /dev/null +++ b/src/rz/typescript/snapshots/data-table.component.ts.snap @@ -0,0 +1,24 @@ +import { AfterViewInit, Component, ViewChild } from '@angular/core'; +import { MatTable } from '@angular/material/table'; +import { TestDataSource, TestItem } from './test-datasource'; + +@Component({ + selector: 'razroo-zeta', + templateUrl: './test.component.html', + styleUrls: ['./test.component.scss'], +}) +export class TestComponent implements AfterViewInit { + @ViewChild(MatTable) table!: MatTable; + dataSource: TestDataSource; + + /** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */ + displayedColumns = ['id', 'name']; + + constructor() { + this.dataSource = new TestDataSource(); + } + + ngAfterViewInit(): void { + this.table.dataSource = this.dataSource; + } +} diff --git a/src/rz/typescript/snapshots/edit-import/edit-import-output.ts.snap b/src/rz/typescript/snapshots/edit-import/edit-import-output.ts.snap new file mode 100644 index 0000000..09b229f --- /dev/null +++ b/src/rz/typescript/snapshots/edit-import/edit-import-output.ts.snap @@ -0,0 +1,13 @@ +import { enableProdMode } from '@angular/core'; +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; + +import { AppModule } from './app/app.module'; +import { environment } from '@common-environments'; + +if (environment.production) { + enableProdMode(); +} + +platformBrowserDynamic() + .bootstrapModule(AppModule) + .catch((err) => console.error(err)); diff --git a/src/rz/typescript/snapshots/edit-import/edit-import.ts.snap b/src/rz/typescript/snapshots/edit-import/edit-import.ts.snap new file mode 100644 index 0000000..d9a2e7e --- /dev/null +++ b/src/rz/typescript/snapshots/edit-import/edit-import.ts.snap @@ -0,0 +1,13 @@ +import { enableProdMode } from '@angular/core'; +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; + +import { AppModule } from './app/app.module'; +import { environment } from './environments/environment'; + +if (environment.production) { + enableProdMode(); +} + +platformBrowserDynamic() + .bootstrapModule(AppModule) + .catch((err) => console.error(err)); diff --git a/src/rz/typescript/snapshots/header-component/header-component-output.ts.snap b/src/rz/typescript/snapshots/header-component/header-component-output.ts.snap new file mode 100644 index 0000000..bbe1fa5 --- /dev/null +++ b/src/rz/typescript/snapshots/header-component/header-component-output.ts.snap @@ -0,0 +1,25 @@ +import { MatToolbarModule } from '@angular/material/toolbar'; +import { Component, OnInit, NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +@Component({ + selector: '<%= selector %>-header', + templateUrl: './<%= fileName %>-header.component.html', + styleUrls: ['./<%= fileName %>-header.component.scss'], +}) +export class <%= className %>HeaderComponent implements OnInit { + faBell = faBell; + constructor() {} + + ngOnInit(): void {} +} + +@NgModule({ + imports: [ + CommonModule, + MatToolbarModule + ], + declarations: [<%= className %>HeaderComponent], + exports: [MatToolbarModule, <%= className %>HeaderComponent], +}) +export class <%= className %>HeaderModule {} diff --git a/src/rz/typescript/snapshots/header-component/header-component.ts.snap b/src/rz/typescript/snapshots/header-component/header-component.ts.snap new file mode 100644 index 0000000..7b5f9c2 --- /dev/null +++ b/src/rz/typescript/snapshots/header-component/header-component.ts.snap @@ -0,0 +1,24 @@ +import { MatToolbarModule } from '@angular/material/toolbar'; +import { Component, OnInit, NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +@Component({ + selector: '<%= selector %>-header', + templateUrl: './<%= fileName %>-header.component.html', + styleUrls: ['./<%= fileName %>-header.component.scss'], +}) +export class <%= className %>HeaderComponent implements OnInit { + constructor() {} + + ngOnInit(): void {} +} + +@NgModule({ + imports: [ + CommonModule, + MatToolbarModule + ], + declarations: [<%= className %>HeaderComponent], + exports: [MatToolbarModule, <%= className %>HeaderComponent], +}) +export class <%= className %>HeaderModule {} diff --git a/src/rz/typescript/snapshots/variable-statement/variable-statement-output-spec.snap.ts b/src/rz/typescript/snapshots/variable-statement/variable-statement-output-spec.snap.ts new file mode 100644 index 0000000..9b4fb88 --- /dev/null +++ b/src/rz/typescript/snapshots/variable-statement/variable-statement-output-spec.snap.ts @@ -0,0 +1,4 @@ +export const environment = { + production: true, + uri: 'test.com' +}; diff --git a/src/rz/typescript/snapshots/variable-statement/variable-statement-spec.snap.ts b/src/rz/typescript/snapshots/variable-statement/variable-statement-spec.snap.ts new file mode 100644 index 0000000..c966979 --- /dev/null +++ b/src/rz/typescript/snapshots/variable-statement/variable-statement-spec.snap.ts @@ -0,0 +1,3 @@ +export const environment = { + production: true, +}; diff --git a/src/rz/typescript/variable-statement/add-to-variable-object.spec.ts b/src/rz/typescript/variable-statement/add-to-variable-object.spec.ts new file mode 100644 index 0000000..84a37df --- /dev/null +++ b/src/rz/typescript/variable-statement/add-to-variable-object.spec.ts @@ -0,0 +1,22 @@ +import { readFileSync } from 'fs'; +import { EditInput } from '../../morph/interfaces/morph.interface'; +import { morphTypescript } from '../morph-typescript'; + +it('should insert new lines into a typescript variable', () => { + const editTypescriptInput: EditInput = { + fileToBeAddedTo: readFileSync('src/rz/typescript/snapshots/variable-statement/variable-statement-spec.snap.ts').toString(), + fileName: 'name', + edits: [ + { + nodeType: 'addToVariableObject', + codeBlock: '{"uri":"test.com"}', + } + ] + }; + + const typescriptString = morphTypescript(editTypescriptInput); + + const expected = readFileSync('src/rz/typescript/snapshots/variable-statement/variable-statement-output-spec.snap.ts').toString(); + + expect(typescriptString).toEqual(expected); + }); \ No newline at end of file diff --git a/src/rz/typescript/variable-statement/variable-statement.ts b/src/rz/typescript/variable-statement/variable-statement.ts new file mode 100644 index 0000000..926eb14 --- /dev/null +++ b/src/rz/typescript/variable-statement/variable-statement.ts @@ -0,0 +1,18 @@ +import { ObjectLiteralExpression } from "ts-morph"; +import { EditCodeBlockInput } from "../interfaces/edit-typescript.interface"; + +export function addToVariableObject(editCodeBlockInput: EditCodeBlockInput): void { + const variableDeclaration = editCodeBlockInput.sourceFile.getVariableDeclarations()[0]; + const objectLiteralExpression = variableDeclaration.getInitializer() as ObjectLiteralExpression; + // initalizer is the equivalent of value for key + const objectToModify = typeof editCodeBlockInput.codeBlock === 'string' ? JSON.parse(editCodeBlockInput.codeBlock) : editCodeBlockInput.codeBlock; + for (const [key, value] of Object.entries(objectToModify)) { + if(typeof value === 'string') { + // have to hack the compiler. For some reason de-stringing value + objectLiteralExpression.addPropertyAssignment({name: key, initializer: `'${value}'` as string}) + } + else { + objectLiteralExpression.addPropertyAssignment({name: key, initializer: value as string}) + } + } +} \ No newline at end of file diff --git a/src/rz/utils/add-export/add-export.spec.ts b/src/rz/utils/add-export/add-export.spec.ts new file mode 100644 index 0000000..bad48c3 --- /dev/null +++ b/src/rz/utils/add-export/add-export.spec.ts @@ -0,0 +1,32 @@ +import { createRelativePath, findNearestIndexFile } from './add-export'; + +test('findNearestIndexFile()', () => { + const currentDir = __dirname; + const indexPath = currentDir + "/index.ts"; + const result = findNearestIndexFile(currentDir); + // index.ts file added to folder specifically for this unit test + expect(result).toBe(indexPath); +}); + +describe('createRelativePath', () => { + it('should create a relative path via two paths if they are in the same folder level', () => { + const pathToBeExported = 'src/rz/utils/add-export/add-export.ts'; + const pathToBeUpdated = 'src/rz/utils/add-export/index.ts'; + + expect(createRelativePath(pathToBeExported, pathToBeUpdated)).toEqual('./add-export'); + }); + + it('should create a relative path via two paths if path to be exported is levels above', () => { + const pathToBeExported = 'src/rz/utils/add-export.ts'; + const pathToBeUpdated = 'src/rz/utils/add-export/index.ts'; + + expect(createRelativePath(pathToBeExported, pathToBeUpdated)).toEqual('../add-export'); + }); + + it('should create a relative path via two paths if path to be exported is levels below', () => { + const pathToBeExported = 'src/rz/utils/add-export/libs/src/add-export.ts'; + const pathToBeUpdated = 'src/rz/utils/add-export/index.ts'; + + expect(createRelativePath(pathToBeExported, pathToBeUpdated)).toEqual('./libs/src/add-export'); + }); +}); diff --git a/src/rz/utils/add-export/add-export.ts b/src/rz/utils/add-export/add-export.ts new file mode 100644 index 0000000..f50c6ce --- /dev/null +++ b/src/rz/utils/add-export/add-export.ts @@ -0,0 +1,58 @@ +import { EditInput } from 'src/rz/morph/interfaces/morph.interface'; +import { existsSync, readFileSync, writeFileSync } from 'fs'; +import { dirname, parse, relative} from "path"; +import { morphTypescript } from '../../typescript/morph-typescript'; + +export function findNearestIndexFile(path: string, fileNameToFind = 'index.ts'): string { + let currentDir = path; + while (currentDir.length > 0) { + const indexPath = currentDir + `/${fileNameToFind}`; + if (existsSync(indexPath)) { + return indexPath; + } + currentDir = currentDir.substring(0, currentDir.lastIndexOf("/")); + } + return ""; +} + +// takes in file path and exports it in closest index.ts file +export function exportTsFiles(filePathWithName: string): void { + if(isTsFile(filePathWithName)) { + const indexTsFile = findNearestIndexFile(filePathWithName); + const exportPath = createRelativePath(filePathWithName, indexTsFile); + const fileToBeAddedTo = readFileSync(indexTsFile, 'utf-8').toString(); + const editInput: EditInput = { + fileType: 'ts', + fileName: 'index.ts', + filePath: indexTsFile, + fileToBeAddedTo: fileToBeAddedTo, + edits: [{ + nodeType: 'export', + codeBlock: '', + path: exportPath + }] + }; + const updatedFileToBeAddedTo = morphTypescript(editInput); + // TODO extract write file logic from morphCode logic + writeFileSync(indexTsFile, updatedFileToBeAddedTo); + } +} + +export function createRelativePath( + pathToBeExported: string, + pathToBeUpdated: string, +): string { + let relativePath = relative( + dirname(pathToBeUpdated), + dirname(pathToBeExported), + ); + if(relativePath && relativePath.substr(0, 2) !== '..') { + relativePath = './' + relativePath + } + + return `${relativePath ? relativePath : '.'}/${parse(pathToBeExported).name}`; +} + +export function isTsFile(filePathWithName: string): boolean { + return filePathWithName.endsWith(".ts") && !filePathWithName.endsWith(".spec.ts"); +} \ No newline at end of file diff --git a/src/rz/utils/add-export/index.ts b/src/rz/utils/add-export/index.ts new file mode 100644 index 0000000..0d2f463 --- /dev/null +++ b/src/rz/utils/add-export/index.ts @@ -0,0 +1 @@ +export * from "./add-export"; diff --git a/src/rz/utils/determine-parameter/determine-paramater.spec.ts b/src/rz/utils/determine-parameter/determine-paramater.spec.ts new file mode 100644 index 0000000..2cb7a39 --- /dev/null +++ b/src/rz/utils/determine-parameter/determine-paramater.spec.ts @@ -0,0 +1,30 @@ +import { determineFilePathParameter } from './determine-parameter'; + +describe('determineFilePathParameter', () => { + it('should determine the file path parameter currently being used based on file path', () => { + + const mockTemplateParameters = [ + { + defaultValue: 'libs/ui/common/src/lib', + description: 'File path for name file(s)', + inputType: 'text', + name: 'nameFilePath', + paramType: 'filePath', + type: 'component' + }, + { + defaultValue: 'name', + description: 'Value for name', + inputType: 'text', + name: 'name', + paramType: 'templateVariable', + type: null + } + ]; + const mockFilePath = '{nameFilePath}/{name}.component.spec.ts'; + + const result = determineFilePathParameter(mockFilePath, mockTemplateParameters); + expect(result).toEqual(mockTemplateParameters[0]); + }); +}) + diff --git a/src/rz/utils/determine-parameter/determine-parameter.ts b/src/rz/utils/determine-parameter/determine-parameter.ts new file mode 100644 index 0000000..0197640 --- /dev/null +++ b/src/rz/utils/determine-parameter/determine-parameter.ts @@ -0,0 +1,12 @@ +import { TemplateInputParameter } from "../interfaces/template-parameters"; + +export function determineFilePathParameter(filePathAndName: string, templateParameters: TemplateInputParameter[]): TemplateInputParameter { + filePathAndName.split('/'); + const folderWithCurlyBraces = filePathAndName[0]; + folderWithCurlyBraces.replace('{', ''); + folderWithCurlyBraces.replace('}', ''); + + templateParameters.find(templateParameter => templateParameter.name === folderWithCurlyBraces); + + return templateParameters[0]; + } \ No newline at end of file diff --git a/src/rz/utils/determine-type/determine-type.spec.ts b/src/rz/utils/determine-type/determine-type.spec.ts new file mode 100644 index 0000000..df9c933 --- /dev/null +++ b/src/rz/utils/determine-type/determine-type.spec.ts @@ -0,0 +1,30 @@ +import { determineType } from './determine-type'; + +describe('determineType', () => { + it('should determine the type for code generation', () => { + + const mockTemplateParameters = [ + { + defaultValue: 'libs/ui/common/src/lib', + description: 'File path for name file(s)', + inputType: 'text', + name: 'nameFilePath', + paramType: 'filePath', + type: 'component' + }, + { + defaultValue: 'name', + description: 'Value for name', + inputType: 'text', + name: 'name', + paramType: 'templateVariable', + type: null + } + ]; + const mockFilePath = '{nameFilePath}/{name}.component.spec.ts'; + + const result = determineType(mockFilePath, mockTemplateParameters); + expect(result).toEqual('component'); + }); +}) + diff --git a/src/rz/utils/determine-type/determine-type.ts b/src/rz/utils/determine-type/determine-type.ts new file mode 100644 index 0000000..d407cad --- /dev/null +++ b/src/rz/utils/determine-type/determine-type.ts @@ -0,0 +1,12 @@ +import { TemplateInputParameter } from "../interfaces/template-parameters"; + +export function determineType(filePathAndName: string, templateParameters: TemplateInputParameter[]): string { + filePathAndName.split('/'); + const folderWithCurlyBraces = filePathAndName[0]; + folderWithCurlyBraces.replace('{', ''); + folderWithCurlyBraces.replace('}', ''); + + templateParameters.find(templateParameter => templateParameter.name === folderWithCurlyBraces); + + return templateParameters[0].type; +} \ No newline at end of file diff --git a/src/rz/utils/directories/directories.spec.ts b/src/rz/utils/directories/directories.spec.ts new file mode 100644 index 0000000..bd2b274 --- /dev/null +++ b/src/rz/utils/directories/directories.spec.ts @@ -0,0 +1,41 @@ +import { getAllDirectories, getAllDirectoriesFromVsCodeFolder, getDirectoriesAsFlatFolderArray } from './directories'; + +describe('getDirectoriesAsFlatFolderArray', () => { + it('should get the directories for a particular folder', () => { + const result = getDirectoriesAsFlatFolderArray('src/rz/utils/directories/test-directories'); + const expected = [ + 'src/rz/utils/directories/test-directories/test-directory-one', + 'src/rz/utils/directories/test-directories/test-directory-two' + ]; + + expect(result).toEqual(expected); + }); +}); + +describe('getAllDirectories', () => { + it('should get the directories and all children folders for a particular folder', () => { + const result = getAllDirectories('src/rz/utils/directories/test-directories'); + const expected = [ + 'src/rz/utils/directories/test-directories', + 'src/rz/utils/directories/test-directories/test-directory-one', + 'src/rz/utils/directories/test-directories/test-directory-one/test-directory-three', + 'src/rz/utils/directories/test-directories/test-directory-two' + ]; + + expect(result).toEqual(expected); + }); +}); + +describe('getDirectoriesWithoutPrivatePath', () => { + const mockVsCodeFolder = {name: 'test-directories', path: 'src/rz/utils/directories/test-directories'}; + const expected = [ + 'test-directories', + 'test-directories/test-directory-one', + 'test-directories/test-directory-one/test-directory-three', + 'test-directories/test-directory-two' + ]; + + it('should take in a VSCode item and return a folder file directory starting from directoryName', () => { + expect(getAllDirectoriesFromVsCodeFolder(mockVsCodeFolder)).toEqual(expected); + }) +}) diff --git a/src/rz/utils/directories/directories.ts b/src/rz/utils/directories/directories.ts new file mode 100644 index 0000000..bb32cda --- /dev/null +++ b/src/rz/utils/directories/directories.ts @@ -0,0 +1,34 @@ +import { join } from "path"; +import * as fs from 'fs' + +interface VsCodeFolderItem { + path: string; + name: string; +} + +export function getDirectoriesAsFlatFolderArray(pathToGetDirectoriesFrom: string): string[] { + return fs + .readdirSync(pathToGetDirectoriesFrom) + .map((file) => join(pathToGetDirectoriesFrom, file)) + .filter((path) => fs.statSync(path).isDirectory() && !path.includes('.git') && !path.includes('node_modules')) +} + +function flatten(lists: any): string[] { + return lists.reduce((fileOne: string, fileTwo: string) => fileOne.concat(fileTwo), []); +} + +// this will get all directories from the top level down to the last child +export function getAllDirectories(srcpath: string): string[] { + return [ + srcpath, + ...flatten(getDirectoriesAsFlatFolderArray(srcpath).map(getAllDirectories)), + ]; +} + +export function getAllDirectoriesFromVsCodeFolder(vsCodeFolderItem: VsCodeFolderItem): string[] { + //TODO change name to directory name + const { path, name } = vsCodeFolderItem; + return getAllDirectories(path)?.map((folder) => { + return folder.slice(folder.search(name), folder.length); + }); +}; \ No newline at end of file diff --git a/src/rz/utils/directories/test-directories/README.md b/src/rz/utils/directories/test-directories/README.md new file mode 100644 index 0000000..5ff1b9c --- /dev/null +++ b/src/rz/utils/directories/test-directories/README.md @@ -0,0 +1 @@ +This folder file structure is here for testing purposes \ No newline at end of file diff --git a/src/rz/utils/directories/test-directories/test-directory-one/.gitkeep b/src/rz/utils/directories/test-directories/test-directory-one/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/rz/utils/directories/test-directories/test-directory-one/test-directory-three/.gitkeep b/src/rz/utils/directories/test-directories/test-directory-one/test-directory-three/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/rz/utils/directories/test-directories/test-directory-two/.gitkeep b/src/rz/utils/directories/test-directories/test-directory-two/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/rz/utils/find-module-file/app.module.ts b/src/rz/utils/find-module-file/app.module.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/rz/utils/find-module-file/find-module-file.spec.ts b/src/rz/utils/find-module-file/find-module-file.spec.ts new file mode 100644 index 0000000..8d9f8f9 --- /dev/null +++ b/src/rz/utils/find-module-file/find-module-file.spec.ts @@ -0,0 +1,11 @@ +import { findClosestModuleFile } from './find-module-file'; + +describe('findClosestModuleFile', () => { + it('should find the near module', () => { + const currentDir = __dirname; + const modulePath = currentDir + "/app.module.ts"; + const result = findClosestModuleFile(currentDir); + // index.ts file added to folder specifically for this unit test + expect(result).toBe(modulePath); + }); +}); \ No newline at end of file diff --git a/src/rz/utils/find-module-file/find-module-file.ts b/src/rz/utils/find-module-file/find-module-file.ts new file mode 100644 index 0000000..b486bff --- /dev/null +++ b/src/rz/utils/find-module-file/find-module-file.ts @@ -0,0 +1,14 @@ +import glob = require("glob"); + +export function findClosestModuleFile(path: string, fileNameToFind = 'module.ts'): string { + let currentDir = path; + while (currentDir.length > 0) { + const modulePath = currentDir + `/*.${fileNameToFind}`; + const modules = glob.sync(modulePath); + if (modules.length > 0) { + return modules[0]; + } + currentDir = currentDir.substring(0, currentDir.lastIndexOf("/")); + } + return ""; +} diff --git a/src/rz/utils/git-utils/git-utils.spec.ts b/src/rz/utils/git-utils/git-utils.spec.ts new file mode 100644 index 0000000..56ab518 --- /dev/null +++ b/src/rz/utils/git-utils/git-utils.spec.ts @@ -0,0 +1,15 @@ +import {extractProjectName} from './git-utils'; + +describe('extractProjectName', () => { + it('should extract a user name and project name from a git ssh url', () => { + const result = extractProjectName('git@github.com:razroo/razroo-angular-starter.git'); + const expected = 'razroo_razroo-angular-starter'; + expect(result).toEqual(expected); + }); + + it('should extract a user name and project name from a git https url', () => { + const result = extractProjectName('https://github.com/razroo/razroo-angular-starter.git'); + const expected = 'razroo_razroo-angular-starter'; + expect(result).toEqual(expected); + }); +}); \ No newline at end of file diff --git a/src/rz/utils/git-utils/git-utils.ts b/src/rz/utils/git-utils/git-utils.ts new file mode 100644 index 0000000..e475cc9 --- /dev/null +++ b/src/rz/utils/git-utils/git-utils.ts @@ -0,0 +1,14 @@ +export function extractProjectName(url: string): string { + const urlParts = url.split('/'); + const userName = urlParts[urlParts.length - 2]; + const projectName = urlParts[urlParts.length - 1]; + const projectNameParts = projectName.split('.'); + if(userName.split(':').length > 1) { + const newUserName = userName.split(':'); + return `${newUserName[newUserName.length - 1]}/${projectNameParts[0]}`.replace('/', '_'); + } else { + return `${userName}/${projectNameParts[0]}`.replace('/', '_'); + } + +} + \ No newline at end of file diff --git a/src/rz/utils/index.ts b/src/rz/utils/index.ts new file mode 100644 index 0000000..322d3e0 --- /dev/null +++ b/src/rz/utils/index.ts @@ -0,0 +1,8 @@ +export { readPackageJson, getProjectDependencies, determineLanguagesUsed } from "./package-json/package-json"; +export { findClosestModuleFile } from "./find-module-file/find-module-file"; +export { getVersionAndNameString } from "./razroo-path/razroo-path"; +export { determineType } from "./determine-type/determine-type"; +export { determineFilePathParameter } from "./determine-parameter/determine-parameter"; +export { replaceCodeModEditsTemplateVariables } from "./replace-template-variables/replace-template-variables"; +export { getAllDirectoriesFromVsCodeFolder } from "./directories/directories"; +export { extractProjectName } from "./git-utils/git-utils"; \ No newline at end of file diff --git a/src/rz/utils/interfaces/template-parameters.ts b/src/rz/utils/interfaces/template-parameters.ts new file mode 100644 index 0000000..69b087a --- /dev/null +++ b/src/rz/utils/interfaces/template-parameters.ts @@ -0,0 +1,15 @@ +export interface TemplateInputParameter { + name: string; + defaultValue: string; + inputType?: any; + type?: string; + description?: string; + optionalTypes?: OptionalType[]; + paramType: any; + originalValue?: string; +} + +export interface OptionalType { + name: string, + selected: boolean +} \ No newline at end of file diff --git a/src/rz/utils/package-json/package-json.ts b/src/rz/utils/package-json/package-json.ts new file mode 100644 index 0000000..81d214a --- /dev/null +++ b/src/rz/utils/package-json/package-json.ts @@ -0,0 +1,91 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + + import * as fs from 'fs'; + import { dirname, join } from 'path'; + import * as resolve from 'resolve'; + + export interface PackageJson { + name: string; + version: string; + dependencies?: Record; + devDependencies?: Record; + peerDependencies?: Record; + optionalDependencies?: Record; + } + + function getAllDependencies(pkg: PackageJson): Set<[string, string]> { + return new Set([ + ...Object.entries(pkg.dependencies || []), + ...Object.entries(pkg.devDependencies || []), + ...Object.entries(pkg.peerDependencies || []), + ...Object.entries(pkg.optionalDependencies || []), + ]); + } + + export interface PackageTreeNode { + name: string; + version: string; + path: string; + package: PackageJson | undefined; + } + + export async function readPackageJson(packageJsonPath: string): Promise { + try { + return JSON.parse((await fs.promises.readFile(packageJsonPath)).toString()); + } catch { + return undefined; + } + } + + export function findPackageJson(workspaceDir: string, packageName: string): string | undefined { + try { + // avoid require.resolve here, see: https://github.com/angular/angular-cli/pull/18610#issuecomment-681980185 + const packageJsonPath = resolve.sync(`${packageName}/package.json`, { basedir: workspaceDir }); + + return packageJsonPath; + } catch { + return undefined; + } + } + + export async function determineLanguagesUsed(packageJsonMap: Map): Promise { + const languagesUsedArr = []; + if(packageJsonMap.has('@angular/core')) { + languagesUsedArr.push('angular'); + } + if(packageJsonMap.has('react')) { + languagesUsedArr.push('react'); + } + return languagesUsedArr; + } + + export async function getProjectDependencies(dir: string): Promise> { + const pkg = await readPackageJson(join(dir, 'package.json')); + if (!pkg) { + throw new Error('Could not find package.json'); + } + + const results = new Map(); + for (const [name, version] of getAllDependencies(pkg)) { + const packageJsonPath = findPackageJson(dir, name); + if (!packageJsonPath) { + continue; + } + + results.set(name, { + name, + version, + path: dirname(packageJsonPath), + package: await readPackageJson(packageJsonPath), + }); + } + + return results; + } + \ No newline at end of file diff --git a/src/rz/utils/razroo-path/razroo-path.spec.ts b/src/rz/utils/razroo-path/razroo-path.spec.ts new file mode 100644 index 0000000..d5d9b0d --- /dev/null +++ b/src/rz/utils/razroo-path/razroo-path.spec.ts @@ -0,0 +1,21 @@ +import { getVersionAndNameString } from './razroo-path'; + +describe('getVersionAndNameString', () => { + it('should return a name and version if it exists', () => { + const mockPathId = 'angular-13.3.0' + const expected = getVersionAndNameString(mockPathId); + expect(expected).toEqual({ + name: 'angular', + version: '13.3.0' + }) + }); + + it('should return name only if no version', () => { + const mockPathId = 'angular'; + const expected = getVersionAndNameString(mockPathId); + expect(expected).toEqual({ + name: 'angular', + version: undefined + }) + }); +}); \ No newline at end of file diff --git a/src/rz/utils/razroo-path/razroo-path.ts b/src/rz/utils/razroo-path/razroo-path.ts new file mode 100644 index 0000000..58cbd58 --- /dev/null +++ b/src/rz/utils/razroo-path/razroo-path.ts @@ -0,0 +1,13 @@ +export function getVersionAndNameString(pathId: string): {name: string, version: string | undefined} { + let version: string | undefined; + const splitPathId = pathId.split('-'); + if(hasSemver(pathId)) { + version = splitPathId.pop(); + } + const pathIdName = splitPathId.join('-'); + return {name: pathIdName, version: version}; + } + +function hasSemver(pathId: string) { +return /\d+\.\d+\.\d+/.test(pathId); +} \ No newline at end of file diff --git a/src/rz/utils/replace-template-variables/replace-template-variables.spec.ts b/src/rz/utils/replace-template-variables/replace-template-variables.spec.ts new file mode 100644 index 0000000..8586376 --- /dev/null +++ b/src/rz/utils/replace-template-variables/replace-template-variables.spec.ts @@ -0,0 +1,31 @@ +import { replaceCodeModEditsTemplateVariables } from './replace-template-variables'; + +describe('replaceCodeModEditsTemplateVariables', () => { + it('should replace code mod edits with the respective value to replace', () => { + const mockParameters = { + name: 'user', + className: 'User', + appFilePath: 'apps/razroo-angular-starter/src/app', + projectName: 'razroo-angular-starter' + }; + + const codeModEdits = [ + { + nodeType: "import", + path: "@<%= projectName %>/common/ui", + codeBlock: "{ <%= className %>Module }" + } + ]; + + const result = replaceCodeModEditsTemplateVariables(codeModEdits, mockParameters); + const expected = [ + { + nodeType: "import", + path: "@razroo-angular-starter/common/ui", + codeBlock:"{ UserModule }" + } + ]; + + expect(result).toEqual(expected); + }); +}) \ No newline at end of file diff --git a/src/rz/utils/replace-template-variables/replace-template-variables.ts b/src/rz/utils/replace-template-variables/replace-template-variables.ts new file mode 100644 index 0000000..1a52d38 --- /dev/null +++ b/src/rz/utils/replace-template-variables/replace-template-variables.ts @@ -0,0 +1,7 @@ +import { replaceTagParameters } from './../../../replace'; +export function replaceCodeModEditsTemplateVariables(codeModEdits: any, parameters: any): any { + return codeModEdits.map((codeMod: any) => { + return Object.fromEntries( + Object.entries(codeMod).map(([key, value]) => [key, replaceTagParameters(parameters, value as string)])); + }); +} \ No newline at end of file diff --git a/src/tsconfig.json b/src/tsconfig.json new file mode 100644 index 0000000..05225e1 --- /dev/null +++ b/src/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "../tsconfig.base.json", + "compilerOptions": { + "types": ["node", "jest"] + }, + "include": [], + "files": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/src/tsconfig.lib.json b/src/tsconfig.lib.json new file mode 100644 index 0000000..8851977 --- /dev/null +++ b/src/tsconfig.lib.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "commonjs", + "outDir": "../../dist/out-tsc", + "declaration": true, + "types": ["node"] + }, + "exclude": ["**/*.spec.ts", "**/*.test.ts", "**/*_spec.ts", "**/*_test.ts"], + "include": ["**/*.ts"] +} diff --git a/src/tsconfig.spec.json b/src/tsconfig.spec.json new file mode 100644 index 0000000..13959ba --- /dev/null +++ b/src/tsconfig.spec.json @@ -0,0 +1,21 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "include": [ + "**/*.spec.ts", + "**/*.test.ts", + "**/*_spec.ts", + "**/*_test.ts", + "**/*.spec.tsx", + "**/*.test.tsx", + "**/*.spec.js", + "**/*.test.js", + "**/*.spec.jsx", + "**/*.test.jsx", + "**/*.d.ts" + ] +} diff --git a/tsconfig.base.json b/tsconfig.base.json new file mode 100644 index 0000000..05e2ed5 --- /dev/null +++ b/tsconfig.base.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "es2015", + "sourceMap": true, + "importHelpers": true, + "module": "esnext", + "moduleResolution": "node", + "outDir": "build", + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "skipLibCheck": true, + "types": ["node", "jest"], + "lib": ["es2021"], + "declaration": true, + "resolveJsonModule": true, + "baseUrl": ".", + "rootDir": "." + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..956508f --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,71 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig.json to read more about this file */ + + /* Basic Options */ + // "incremental": true, /* Enable incremental compilation */ + "target": "es6" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */, + "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */, + "lib": ["ES2021"], /* Specify library files to be included in the compilation. */ + // "allowJs": true, /* Allow javascript files to be compiled. */ + // "checkJs": true, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ + "declaration": true /* Generates corresponding '.d.ts' file. */, + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + // "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + "outDir": "./dist" /* Redirect output structure to the directory. */, + // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ + // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + + /* Strict Type-Checking Options */ + "strict": true /* Enable all strict type-checking options. */, + "noImplicitAny": false, /* Raise error on expressions and declarations with an implied 'any' type. */ + "strictNullChecks": false, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + /* Module Resolution Options */ + "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + "resolveJsonModule": true, + "baseUrl": ".", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + "types": ["node", "jest"], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + + /* Experimental Options */ + "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + + /* Advanced Options */ + "skipLibCheck": true /* Skip type checking of declaration files. */, + "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ + }, + "include": ["src"], + "exclude": ["node_modules", "**/__tests__/*", "**/*.spec.ts", "**/*.test.ts", "**/*_spec.ts", "**/*_test.ts"] +}