├── .circleci └── config.yml ├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── .npmignore ├── LICENSE.txt ├── README.md ├── docs ├── edit-github-release.png ├── jest-schematic-demo-500.gif └── np-release.png ├── package-lock.json ├── package.json ├── sandboxes ├── single-app │ ├── .editorconfig │ ├── .gitignore │ ├── README.md │ ├── angular.json │ ├── package.json │ ├── src │ │ ├── app │ │ │ ├── app-routing.module.ts │ │ │ ├── app.component.css │ │ │ ├── app.component.html │ │ │ ├── app.component.spec.ts │ │ │ ├── app.component.ts │ │ │ └── app.module.ts │ │ ├── assets │ │ │ └── .gitkeep │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── main.ts │ │ └── styles.css │ ├── tsconfig.app.json │ ├── tsconfig.json │ ├── tsconfig.spec.json │ └── yarn.lock └── workspace │ ├── .editorconfig │ ├── .gitignore │ ├── README.md │ ├── angular.json │ ├── package.json │ ├── projects │ ├── app-one │ │ ├── src │ │ │ ├── app │ │ │ │ ├── app-routing.module.ts │ │ │ │ ├── app.component.css │ │ │ │ ├── app.component.html │ │ │ │ ├── app.component.spec.ts │ │ │ │ ├── app.component.ts │ │ │ │ └── app.module.ts │ │ │ ├── assets │ │ │ │ └── .gitkeep │ │ │ ├── favicon.ico │ │ │ ├── index.html │ │ │ ├── main.ts │ │ │ └── styles.css │ │ ├── tsconfig.app.json │ │ └── tsconfig.spec.json │ └── lib-one │ │ ├── README.md │ │ ├── ng-package.json │ │ ├── package.json │ │ ├── src │ │ ├── lib │ │ │ ├── lib-one.component.spec.ts │ │ │ ├── lib-one.component.ts │ │ │ ├── lib-one.module.ts │ │ │ ├── lib-one.service.spec.ts │ │ │ └── lib-one.service.ts │ │ └── public-api.ts │ │ ├── tsconfig.lib.json │ │ ├── tsconfig.lib.prod.json │ │ └── tsconfig.spec.json │ ├── tsconfig.json │ └── yarn.lock ├── scripts ├── e2e.ts ├── tsconfig.json └── utils.ts ├── src ├── collection.json ├── interfaces │ └── ts-config-schema.ts ├── jest │ ├── files │ │ └── jest.config.js │ ├── index.ts │ ├── schema.json │ └── workspace-files │ │ └── jest.config.js └── utility │ ├── dependencies.ts │ ├── json-utils.ts │ └── util.ts ├── tsconfig.json └── yarn.lock /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | build: 4 | docker: 5 | # https://circleci.com/developer/images/image/cimg/node 6 | - image: cimg/node:lts-browsers 7 | steps: 8 | - checkout 9 | # Download and cache dependencies 10 | - restore_cache: 11 | keys: 12 | # when lock file changes, use increasingly general patterns to restore cache 13 | - yarn-packages-v1-{{ .Branch }}-{{ checksum "yarn.lock" }} 14 | - yarn-packages-v1-{{ .Branch }}- 15 | - yarn-packages-v1- 16 | # https://circleci.com/docs/2.0/caching/#yarn-node 17 | - run: yarn --frozen-lockfile --cache-folder ~/.cache/yarn 18 | - run: yarn build 19 | - save_cache: 20 | paths: 21 | - ~/.cache/yarn 22 | key: yarn-packages-v1-{{ .Branch }}-{{ checksum "yarn.lock" }} 23 | 24 | test-single-app: 25 | docker: 26 | - image: cimg/node:lts-browsers 27 | steps: 28 | - checkout 29 | # Download and cache dependencies 30 | - restore_cache: 31 | keys: 32 | # when lock file changes, use increasingly general patterns to restore cache 33 | - yarn-packages-v1-{{ .Branch }}-{{ checksum "yarn.lock" }} 34 | - yarn-packages-v1-{{ .Branch }}- 35 | - yarn-packages-v1- 36 | - run: yarn --frozen-lockfile --cache-folder ~/.cache/yarn 37 | - run: yarn test single 38 | 39 | test-workspace-app-lib: 40 | docker: 41 | - image: cimg/node:lts-browsers 42 | steps: 43 | - checkout 44 | # Download and cache dependencies 45 | - restore_cache: 46 | keys: 47 | # when lock file changes, use increasingly general patterns to restore cache 48 | - yarn-packages-v1-{{ .Branch }}-{{ checksum "yarn.lock" }} 49 | - yarn-packages-v1-{{ .Branch }}- 50 | - yarn-packages-v1- 51 | - run: yarn --frozen-lockfile --cache-folder ~/.cache/yarn 52 | - run: yarn test workspace 53 | 54 | 55 | workflows: 56 | version: 2 57 | 58 | build-and-test: 59 | jobs: 60 | - build 61 | 62 | - test-single-app: 63 | requires: 64 | - build 65 | 66 | - test-workspace-app-lib: 67 | requires: 68 | - build -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | --- 5 | 6 | **Describe the bug** 7 | A clear and concise description of what the bug is. 8 | 9 | **To Reproduce** 10 | Steps to reproduce the behavior: 11 | 12 | 1. Go to '...' 13 | 2. Click on '....' 14 | 3. Scroll down to '....' 15 | 4. See error 16 | 17 | **Expected behavior** 18 | A clear and concise description of what you expected to happen. 19 | 20 | **Screenshots** 21 | If applicable, add screenshots to help explain your problem. 22 | 23 | **Desktop (please complete the following information):** 24 | 25 | - OS: [e.g. Mac, Windows] 26 | - Node version: [e.g. 8.11.3] 27 | - App type [e.g. Angular CLI, Nx Workspace] 28 | - Angular version [e.g. @angular/core: "6.0.3"] 29 | 30 | **Additional context** 31 | Add any other context about the problem here. 32 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | **Is your feature request related to a problem? Please describe.** 8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Outputs 2 | src/**/*.js 3 | !src/**/files/**/*.js 4 | !src/**/workspace-files/**/*.js 5 | src/**/*.js.map 6 | src/**/*.d.ts 7 | 8 | # IDEs 9 | .idea/ 10 | jsconfig.json 11 | .vscode/ 12 | 13 | # Misc 14 | node_modules/ 15 | npm-debug.log* 16 | yarn-error.log* 17 | *.todo 18 | .angular 19 | 20 | # Mac OSX Finder files. 21 | **/.DS_Store 22 | .DS_Store 23 | 24 | # IDE - VSCode 25 | .vscode/* 26 | !.vscode/settings.json 27 | !.vscode/tasks.json 28 | !.vscode/launch.json 29 | !.vscode/extensions.json -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Ignores TypeScript files, but keeps definitions. 2 | *.ts 3 | *.js.map 4 | !src/**/files/**/*.js 5 | !src/**/files/**/*.ts 6 | !*.d.ts 7 | 8 | .vscode/* 9 | 10 | sandboxes 11 | docs 12 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018-present Briebug and Contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Jest Angular Schematic 2 | 3 | This schematic will configure Angular to execute unit tests with Jest for single projects or workspaces. 4 | 5 | [![npm (scoped)](https://img.shields.io/npm/v/@briebug/jest-schematic.svg)](https://www.npmjs.com/package/@briebug/jest-schematic) 6 | 7 | ## Usage 🚀 8 | 9 | ```shell 10 | ng add @briebug/jest-schematic 11 | ``` 12 | 13 | - install [Jest](https://facebook.github.io/jest/), types and a [builder](https://github.com/just-jeb/angular-builders/tree/master/packages/jest) 14 | - add Jest configuration files 15 | - remove Karma & Jasmine along with their configuration files 16 | 17 | ### Optionally: install globally 18 | 19 | ```shell 20 | npm install -g @briebug/jest-schematic 21 | ``` 22 | 23 | Then in an Angular CLI project run 24 | 25 | ```shell 26 | ng g @briebug/jest-schematic:add 27 | ``` 28 | 29 | ![jest-schematic-demo-500](docs/jest-schematic-demo-500.gif) 30 | 31 | ## Issues 🧐 32 | 33 | If you're experiencing issues when trying to run your tests with Jest, please view the documentation for the [builder](https://github.com/just-jeb/angular-builders/tree/master/packages/jest) which uses [jest-preset-angular](https://github.com/thymikee/jest-preset-angular#troubleshooting). 34 | 35 | A common issues involves library dependencies. For example if your app depends on `NgRx` you'll need to tell Jest to compile the sources [explicitly](https://github.com/thymikee/jest-preset-angular#adjust-your-transformignorepatterns-whitelist) by appending it to the `transformIgnorePatterns` property in the `jest.config.js` file. 36 | 37 | ```js 38 | module.exports = { 39 | transformIgnorePatterns: ['node_modules/(?!(jest-test|@ngrx))'], 40 | }; 41 | ``` 42 | 43 | ## Jest issues 44 | 45 | Issues related to jest, ts-jest, or test execution may be related the installed version of jest and jest-preset-angular. The schematic may install a version of jest other than latest in an attempt to configure package versions that work together correctly. If you experience issues with your tests after running the schematic related to the aforementioned packages, please review the package versions and adjust them as necessary. 46 | 47 | Issues with this schematic can be filed [here](https://github.com/briebug/jest-schematic/issues/new/choose). 48 | 49 | ## Learning Resources 📚 50 | 51 | - [Unit Testing Angular With Jest](https://medium.com/@ole.ersoy/unit-testing-angular-with-jest-b65888ff33f6) 52 | 53 | ## Development 🛠 54 | 55 | ### Getting started 56 | 57 | Clone or fork the repo and install the dependencies with Yarn 58 | 59 | ```shell 60 | yarn 61 | ``` 62 | 63 | ### Test schematic changes against a sandbox app 64 | 65 | When running locally, schematic changes will be applied to a test app in the `/sandboxes` directory. `/sandboxes` contain a single app repo and a workspace repo with an application and library. 66 | 67 | Run the following when a schematic change is made to test: 68 | 69 | ```bash 70 | // runs against /sandboxes/single-app 71 | yarn test single 72 | 73 | // runs against /sandboxes/workspace 74 | yarn test workspace 75 | ``` 76 | 77 | ⚠ **Be careful not to check in changes to the sandbox directory unless necessary.** ⚠ 78 | 79 | ### Reset sandboxes to their version controlled state 80 | 81 | This will reset the sandboxes to their `HEAD` commit and remove un-tracked files. 82 | 83 | ```shell 84 | yarn reset 85 | ``` 86 | 87 | ### Test schematics against a local project 88 | 89 | - run `yarn build` to compile the schematic in watch mode 90 | - open another shell, cd into the local repo you want to run the schematic against, and run `yarn link @briebug/jest-schematic`. This assumes you've run `yarn link` in this repo on your machine. 91 | - this will symlink the projects so that the Jest schematic command runs from you're local filesystem 92 | - in the local repo you want to run the schematic against, run `ng g @briebug/jest-schematic:add` 93 | 94 | ### Update sandboxes 95 | 96 | When a new version of Angular is released, update all the sandbox apps and libs to the latest version. 97 | 98 | _replace `15` with the latest version of Angular_ 99 | 100 | ```shell 101 | cd sandbox 102 | 103 | rm single-app 104 | 105 | npx @angular/cli@15 new single-app --routing --style=css --skip-git --package-manager=yarn 106 | 107 | rm workspace 108 | 109 | npx @angular/cli@15 new workspace --create-application=false --skip-git --package-manager=yarn 110 | 111 | cd workspace 112 | 113 | ng g app app-one --routing --style=css --skip-git 114 | 115 | ng g lib lib-one 116 | ``` 117 | 118 | ### Dev tips 119 | 120 | For faster developing, find and comment out the following line to avoid npm installing dependencies 121 | 122 | ```ts 123 | context.addTask(new NodePackageInstallTask()); 124 | ``` 125 | -------------------------------------------------------------------------------- /docs/edit-github-release.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briebug/jest-schematic/9b95c78b6937b8692ca17e04eaae436f37ddadf0/docs/edit-github-release.png -------------------------------------------------------------------------------- /docs/jest-schematic-demo-500.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briebug/jest-schematic/9b95c78b6937b8692ca17e04eaae436f37ddadf0/docs/jest-schematic-demo-500.gif -------------------------------------------------------------------------------- /docs/np-release.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briebug/jest-schematic/9b95c78b6937b8692ca17e04eaae436f37ddadf0/docs/np-release.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@briebug/jest-schematic", 3 | "version": "6.0.0", 4 | "description": "Schematic to add jest to an Angular CLI project", 5 | "repository": "briebug/jest-schematic", 6 | "bugs": "https://github.com/briebug/jest-schematic/issues", 7 | "homepage": "https://github.com/briebug/jest-schematic", 8 | "main": "src/jest/index.js", 9 | "scripts": { 10 | "build": "tsc -p tsconfig.json", 11 | "reset": "ts-node --project=./scripts/tsconfig.json ./scripts/e2e.ts", 12 | "test": "ts-node --project=./scripts/tsconfig.json ./scripts/e2e.ts", 13 | "test:single": "yarn test single", 14 | "test:workspace": "yarn test workspace", 15 | "test:reset": "yarn test reset", 16 | "release": "np" 17 | }, 18 | "keywords": [ 19 | "schematics", 20 | "jest-schematic", 21 | "jest", 22 | "angular" 23 | ], 24 | "author": "Briebug", 25 | "license": "MIT", 26 | "schematics": "./src/collection.json", 27 | "dependencies": { 28 | "@angular-devkit/core": "^10.0.6", 29 | "@angular-devkit/schematics": "^10.0.6", 30 | "@schematics/angular": "^10.0.6", 31 | "@schuchard/schematics-core": "^0.4.0", 32 | "rxjs": "6.5.5" 33 | }, 34 | "publishConfig": { 35 | "access": "public" 36 | }, 37 | "devDependencies": { 38 | "@types/jasmine": "^3.3.16", 39 | "@types/node": "^12.6.9", 40 | "@types/shelljs": "^0.8.8", 41 | "husky": "^4.2.5", 42 | "jasmine": "^3.4.0", 43 | "lint-staged": "^10.2.13", 44 | "np": "^7.6.3", 45 | "prettier": "^2.1.1", 46 | "shelljs": "^0.8.4", 47 | "ts-node": "^9.0.0", 48 | "typescript": "^4.0.2" 49 | }, 50 | "husky": { 51 | "hooks": { 52 | "pre-commit": "lint-staged" 53 | } 54 | }, 55 | "lint-staged": { 56 | "*.{js,json,md,ts}": [ 57 | "prettier --write", 58 | "git add" 59 | ] 60 | }, 61 | "prettier": { 62 | "printWidth": 100, 63 | "tabWidth": 2, 64 | "semi": true, 65 | "singleQuote": true, 66 | "trailingComma": "es5", 67 | "bracketSpacing": true, 68 | "arrowParens": "always" 69 | }, 70 | "np": { 71 | "anyBranch": true, 72 | "test-script": "yarn test single && yarn test workspace" 73 | }, 74 | "ng-add": { 75 | "save": "devDependencies" 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /sandboxes/single-app/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.ts] 12 | quote_type = single 13 | 14 | [*.md] 15 | max_line_length = off 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /sandboxes/single-app/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # Compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | /bazel-out 8 | 9 | # Node 10 | /node_modules 11 | npm-debug.log 12 | yarn-error.log 13 | 14 | # IDEs and editors 15 | .idea/ 16 | .project 17 | .classpath 18 | .c9/ 19 | *.launch 20 | .settings/ 21 | *.sublime-workspace 22 | 23 | # Visual Studio Code 24 | .vscode/* 25 | !.vscode/settings.json 26 | !.vscode/tasks.json 27 | !.vscode/launch.json 28 | !.vscode/extensions.json 29 | .history/* 30 | 31 | # Miscellaneous 32 | /.angular/cache 33 | .sass-cache/ 34 | /connect.lock 35 | /coverage 36 | /libpeerconnection.log 37 | testem.log 38 | /typings 39 | 40 | # System files 41 | .DS_Store 42 | Thumbs.db 43 | -------------------------------------------------------------------------------- /sandboxes/single-app/README.md: -------------------------------------------------------------------------------- 1 | # SingleApp 2 | 3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 15.0.0. 4 | 5 | ## Development server 6 | 7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files. 8 | 9 | ## Code scaffolding 10 | 11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. 12 | 13 | ## Build 14 | 15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. 16 | 17 | ## Running unit tests 18 | 19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 20 | 21 | ## Running end-to-end tests 22 | 23 | Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities. 24 | 25 | ## Further help 26 | 27 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page. 28 | -------------------------------------------------------------------------------- /sandboxes/single-app/angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "cli": { 5 | "packageManager": "yarn" 6 | }, 7 | "newProjectRoot": "projects", 8 | "projects": { 9 | "single-app": { 10 | "projectType": "application", 11 | "schematics": {}, 12 | "root": "", 13 | "sourceRoot": "src", 14 | "prefix": "app", 15 | "architect": { 16 | "build": { 17 | "builder": "@angular-devkit/build-angular:browser", 18 | "options": { 19 | "outputPath": "dist/single-app", 20 | "index": "src/index.html", 21 | "main": "src/main.ts", 22 | "polyfills": ["zone.js"], 23 | "tsConfig": "tsconfig.app.json", 24 | "assets": ["src/favicon.ico", "src/assets"], 25 | "styles": ["src/styles.css"], 26 | "scripts": [] 27 | }, 28 | "configurations": { 29 | "production": { 30 | "budgets": [ 31 | { 32 | "type": "initial", 33 | "maximumWarning": "500kb", 34 | "maximumError": "1mb" 35 | }, 36 | { 37 | "type": "anyComponentStyle", 38 | "maximumWarning": "2kb", 39 | "maximumError": "4kb" 40 | } 41 | ], 42 | "outputHashing": "all" 43 | }, 44 | "development": { 45 | "buildOptimizer": false, 46 | "optimization": false, 47 | "vendorChunk": true, 48 | "extractLicenses": false, 49 | "sourceMap": true, 50 | "namedChunks": true 51 | } 52 | }, 53 | "defaultConfiguration": "production" 54 | }, 55 | "serve": { 56 | "builder": "@angular-devkit/build-angular:dev-server", 57 | "configurations": { 58 | "production": { 59 | "browserTarget": "single-app:build:production" 60 | }, 61 | "development": { 62 | "browserTarget": "single-app:build:development" 63 | } 64 | }, 65 | "defaultConfiguration": "development" 66 | }, 67 | "extract-i18n": { 68 | "builder": "@angular-devkit/build-angular:extract-i18n", 69 | "options": { 70 | "browserTarget": "single-app:build" 71 | } 72 | }, 73 | "test": { 74 | "builder": "@angular-devkit/build-angular:karma", 75 | "options": { 76 | "polyfills": ["zone.js", "zone.js/testing"], 77 | "tsConfig": "tsconfig.spec.json", 78 | "assets": ["src/favicon.ico", "src/assets"], 79 | "styles": ["src/styles.css"], 80 | "scripts": [] 81 | } 82 | } 83 | } 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /sandboxes/single-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "single-app", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "ng serve", 7 | "build": "ng build", 8 | "watch": "ng build --watch --configuration development", 9 | "test": "ng test" 10 | }, 11 | "private": true, 12 | "dependencies": { 13 | "@angular/animations": "^15.0.0", 14 | "@angular/common": "^15.0.0", 15 | "@angular/compiler": "^15.0.0", 16 | "@angular/core": "^15.0.0", 17 | "@angular/forms": "^15.0.0", 18 | "@angular/platform-browser": "^15.0.0", 19 | "@angular/platform-browser-dynamic": "^15.0.0", 20 | "@angular/router": "^15.0.0", 21 | "rxjs": "~7.5.0", 22 | "tslib": "^2.3.0", 23 | "zone.js": "~0.12.0" 24 | }, 25 | "devDependencies": { 26 | "@angular-devkit/build-angular": "^15.0.0", 27 | "@angular/cli": "~15.0.0", 28 | "@angular/compiler-cli": "^15.0.0", 29 | "@types/jasmine": "~4.3.0", 30 | "jasmine-core": "~4.5.0", 31 | "karma": "~6.4.0", 32 | "karma-chrome-launcher": "~3.1.0", 33 | "karma-coverage": "~2.2.0", 34 | "karma-jasmine": "~5.1.0", 35 | "karma-jasmine-html-reporter": "~2.0.0", 36 | "typescript": "~4.8.2" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /sandboxes/single-app/src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule, Routes } from '@angular/router'; 3 | 4 | const routes: Routes = []; 5 | 6 | @NgModule({ 7 | imports: [RouterModule.forRoot(routes)], 8 | exports: [RouterModule], 9 | }) 10 | export class AppRoutingModule {} 11 | -------------------------------------------------------------------------------- /sandboxes/single-app/src/app/app.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briebug/jest-schematic/9b95c78b6937b8692ca17e04eaae436f37ddadf0/sandboxes/single-app/src/app/app.component.css -------------------------------------------------------------------------------- /sandboxes/single-app/src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 304 | 305 | 306 | 327 | 328 |
329 | 330 | 331 |
332 | 333 | 334 | Rocket Ship 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | {{ title }} app is running! 345 | 346 | 347 | Rocket Ship Smoke 348 | 349 | 350 | 351 |
352 | 353 | 354 |

Resources

355 |

Here are some links to help you get started:

356 | 357 |
358 | 359 | 360 | Learn Angular 361 | 362 | 363 | 364 | 365 | CLI Documentation 366 | 367 | 368 | 369 | 370 | 371 | Angular Material 372 | 373 | 374 | 375 | 376 | 377 | Angular Blog 378 | 379 | 380 | 381 | 382 | 383 | Angular DevTools 384 | 385 | 386 | 387 |
388 | 389 | 390 |

Next Steps

391 |

What do you want to do next with your app?

392 | 393 | 394 | 395 |
396 | 400 | 401 | 405 | 406 | 410 | 411 | 415 | 416 | 420 | 421 | 425 |
426 | 427 | 428 |
429 |
ng generate component xyz
430 |
ng add @angular/material
431 |
ng add @angular/pwa
432 |
ng add _____
433 |
ng test
434 |
ng build
435 |
436 | 437 | 438 |
439 | 440 | 441 | Meetup Logo 442 | 443 | 444 | 445 | 446 | 447 | 448 | Discord Logo 449 | 450 | 451 | 452 | 453 |
454 | 455 | 456 | 468 | 469 | 470 | Gray Clouds Background 471 | 472 | 473 | 474 |
475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | -------------------------------------------------------------------------------- /sandboxes/single-app/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | import { RouterTestingModule } from '@angular/router/testing'; 3 | import { AppComponent } from './app.component'; 4 | 5 | describe('AppComponent', () => { 6 | beforeEach(async () => { 7 | await TestBed.configureTestingModule({ 8 | imports: [RouterTestingModule], 9 | declarations: [AppComponent], 10 | }).compileComponents(); 11 | }); 12 | 13 | it('should create the app', () => { 14 | const fixture = TestBed.createComponent(AppComponent); 15 | const app = fixture.componentInstance; 16 | expect(app).toBeTruthy(); 17 | }); 18 | 19 | it(`should have as title 'single-app'`, () => { 20 | const fixture = TestBed.createComponent(AppComponent); 21 | const app = fixture.componentInstance; 22 | expect(app.title).toEqual('single-app'); 23 | }); 24 | 25 | it('should render title', () => { 26 | const fixture = TestBed.createComponent(AppComponent); 27 | fixture.detectChanges(); 28 | const compiled = fixture.nativeElement as HTMLElement; 29 | expect(compiled.querySelector('.content span')?.textContent).toContain( 30 | 'single-app app is running!' 31 | ); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /sandboxes/single-app/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-root', 5 | templateUrl: './app.component.html', 6 | styleUrls: ['./app.component.css'], 7 | }) 8 | export class AppComponent { 9 | title = 'single-app'; 10 | } 11 | -------------------------------------------------------------------------------- /sandboxes/single-app/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { BrowserModule } from '@angular/platform-browser'; 3 | 4 | import { AppRoutingModule } from './app-routing.module'; 5 | import { AppComponent } from './app.component'; 6 | 7 | @NgModule({ 8 | declarations: [AppComponent], 9 | imports: [BrowserModule, AppRoutingModule], 10 | providers: [], 11 | bootstrap: [AppComponent], 12 | }) 13 | export class AppModule {} 14 | -------------------------------------------------------------------------------- /sandboxes/single-app/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briebug/jest-schematic/9b95c78b6937b8692ca17e04eaae436f37ddadf0/sandboxes/single-app/src/assets/.gitkeep -------------------------------------------------------------------------------- /sandboxes/single-app/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briebug/jest-schematic/9b95c78b6937b8692ca17e04eaae436f37ddadf0/sandboxes/single-app/src/favicon.ico -------------------------------------------------------------------------------- /sandboxes/single-app/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SingleApp 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /sandboxes/single-app/src/main.ts: -------------------------------------------------------------------------------- 1 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 2 | 3 | import { AppModule } from './app/app.module'; 4 | 5 | platformBrowserDynamic() 6 | .bootstrapModule(AppModule) 7 | .catch((err) => console.error(err)); 8 | -------------------------------------------------------------------------------- /sandboxes/single-app/src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | -------------------------------------------------------------------------------- /sandboxes/single-app/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/app", 6 | "types": [] 7 | }, 8 | "files": ["src/main.ts"], 9 | "include": ["src/**/*.d.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /sandboxes/single-app/tsconfig.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "compileOnSave": false, 4 | "compilerOptions": { 5 | "baseUrl": "./", 6 | "outDir": "./dist/out-tsc", 7 | "forceConsistentCasingInFileNames": true, 8 | "strict": true, 9 | "noImplicitOverride": true, 10 | "noPropertyAccessFromIndexSignature": true, 11 | "noImplicitReturns": true, 12 | "noFallthroughCasesInSwitch": true, 13 | "sourceMap": true, 14 | "declaration": false, 15 | "downlevelIteration": true, 16 | "experimentalDecorators": true, 17 | "moduleResolution": "node", 18 | "importHelpers": true, 19 | "target": "ES2022", 20 | "module": "ES2022", 21 | "useDefineForClassFields": false, 22 | "lib": ["ES2022", "dom"] 23 | }, 24 | "angularCompilerOptions": { 25 | "enableI18nLegacyMessageIdFormat": false, 26 | "strictInjectionParameters": true, 27 | "strictInputAccessModifiers": true, 28 | "strictTemplates": true 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /sandboxes/single-app/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/spec", 6 | "types": ["jasmine"] 7 | }, 8 | "include": ["src/**/*.spec.ts", "src/**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /sandboxes/workspace/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.ts] 12 | quote_type = single 13 | 14 | [*.md] 15 | max_line_length = off 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /sandboxes/workspace/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # Compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | /bazel-out 8 | 9 | # Node 10 | /node_modules 11 | npm-debug.log 12 | yarn-error.log 13 | 14 | # IDEs and editors 15 | .idea/ 16 | .project 17 | .classpath 18 | .c9/ 19 | *.launch 20 | .settings/ 21 | *.sublime-workspace 22 | 23 | # Visual Studio Code 24 | .vscode/* 25 | !.vscode/settings.json 26 | !.vscode/tasks.json 27 | !.vscode/launch.json 28 | !.vscode/extensions.json 29 | .history/* 30 | 31 | # Miscellaneous 32 | /.angular/cache 33 | .sass-cache/ 34 | /connect.lock 35 | /coverage 36 | /libpeerconnection.log 37 | testem.log 38 | /typings 39 | 40 | # System files 41 | .DS_Store 42 | Thumbs.db 43 | -------------------------------------------------------------------------------- /sandboxes/workspace/README.md: -------------------------------------------------------------------------------- 1 | # Workspace 2 | 3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 15.0.0. 4 | 5 | ## Development server 6 | 7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files. 8 | 9 | ## Code scaffolding 10 | 11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. 12 | 13 | ## Build 14 | 15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. 16 | 17 | ## Running unit tests 18 | 19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 20 | 21 | ## Running end-to-end tests 22 | 23 | Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities. 24 | 25 | ## Further help 26 | 27 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page. 28 | -------------------------------------------------------------------------------- /sandboxes/workspace/angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "cli": { 5 | "packageManager": "yarn" 6 | }, 7 | "newProjectRoot": "projects", 8 | "projects": { 9 | "app-one": { 10 | "projectType": "application", 11 | "schematics": {}, 12 | "root": "projects/app-one", 13 | "sourceRoot": "projects/app-one/src", 14 | "prefix": "app", 15 | "architect": { 16 | "build": { 17 | "builder": "@angular-devkit/build-angular:browser", 18 | "options": { 19 | "outputPath": "dist/app-one", 20 | "index": "projects/app-one/src/index.html", 21 | "main": "projects/app-one/src/main.ts", 22 | "polyfills": ["zone.js"], 23 | "tsConfig": "projects/app-one/tsconfig.app.json", 24 | "assets": ["projects/app-one/src/favicon.ico", "projects/app-one/src/assets"], 25 | "styles": ["projects/app-one/src/styles.css"], 26 | "scripts": [] 27 | }, 28 | "configurations": { 29 | "production": { 30 | "budgets": [ 31 | { 32 | "type": "initial", 33 | "maximumWarning": "500kb", 34 | "maximumError": "1mb" 35 | }, 36 | { 37 | "type": "anyComponentStyle", 38 | "maximumWarning": "2kb", 39 | "maximumError": "4kb" 40 | } 41 | ], 42 | "outputHashing": "all" 43 | }, 44 | "development": { 45 | "buildOptimizer": false, 46 | "optimization": false, 47 | "vendorChunk": true, 48 | "extractLicenses": false, 49 | "sourceMap": true, 50 | "namedChunks": true 51 | } 52 | }, 53 | "defaultConfiguration": "production" 54 | }, 55 | "serve": { 56 | "builder": "@angular-devkit/build-angular:dev-server", 57 | "configurations": { 58 | "production": { 59 | "browserTarget": "app-one:build:production" 60 | }, 61 | "development": { 62 | "browserTarget": "app-one:build:development" 63 | } 64 | }, 65 | "defaultConfiguration": "development" 66 | }, 67 | "extract-i18n": { 68 | "builder": "@angular-devkit/build-angular:extract-i18n", 69 | "options": { 70 | "browserTarget": "app-one:build" 71 | } 72 | }, 73 | "test": { 74 | "builder": "@angular-devkit/build-angular:karma", 75 | "options": { 76 | "polyfills": ["zone.js", "zone.js/testing"], 77 | "tsConfig": "projects/app-one/tsconfig.spec.json", 78 | "assets": ["projects/app-one/src/favicon.ico", "projects/app-one/src/assets"], 79 | "styles": ["projects/app-one/src/styles.css"], 80 | "scripts": [] 81 | } 82 | } 83 | } 84 | }, 85 | "lib-one": { 86 | "projectType": "library", 87 | "root": "projects/lib-one", 88 | "sourceRoot": "projects/lib-one/src", 89 | "prefix": "lib", 90 | "architect": { 91 | "build": { 92 | "builder": "@angular-devkit/build-angular:ng-packagr", 93 | "options": { 94 | "project": "projects/lib-one/ng-package.json" 95 | }, 96 | "configurations": { 97 | "production": { 98 | "tsConfig": "projects/lib-one/tsconfig.lib.prod.json" 99 | }, 100 | "development": { 101 | "tsConfig": "projects/lib-one/tsconfig.lib.json" 102 | } 103 | }, 104 | "defaultConfiguration": "production" 105 | }, 106 | "test": { 107 | "builder": "@angular-devkit/build-angular:karma", 108 | "options": { 109 | "tsConfig": "projects/lib-one/tsconfig.spec.json", 110 | "polyfills": ["zone.js", "zone.js/testing"] 111 | } 112 | } 113 | } 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /sandboxes/workspace/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "workspace", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "ng serve", 7 | "build": "ng build", 8 | "watch": "ng build --watch --configuration development", 9 | "test": "ng test" 10 | }, 11 | "private": true, 12 | "dependencies": { 13 | "@angular/animations": "^15.0.0", 14 | "@angular/common": "^15.0.0", 15 | "@angular/compiler": "^15.0.0", 16 | "@angular/core": "^15.0.0", 17 | "@angular/forms": "^15.0.0", 18 | "@angular/platform-browser": "^15.0.0", 19 | "@angular/platform-browser-dynamic": "^15.0.0", 20 | "@angular/router": "^15.0.0", 21 | "rxjs": "~7.5.0", 22 | "tslib": "^2.3.0", 23 | "zone.js": "~0.12.0" 24 | }, 25 | "devDependencies": { 26 | "@angular-devkit/build-angular": "^15.0.0", 27 | "@angular/cli": "~15.0.0", 28 | "@angular/compiler-cli": "^15.0.0", 29 | "@types/jasmine": "~4.3.0", 30 | "jasmine-core": "~4.5.0", 31 | "karma": "~6.4.0", 32 | "karma-chrome-launcher": "~3.1.0", 33 | "karma-coverage": "~2.2.0", 34 | "karma-jasmine": "~5.1.0", 35 | "karma-jasmine-html-reporter": "~2.0.0", 36 | "ng-packagr": "^15.0.0", 37 | "typescript": "~4.8.2" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /sandboxes/workspace/projects/app-one/src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule, Routes } from '@angular/router'; 3 | 4 | const routes: Routes = []; 5 | 6 | @NgModule({ 7 | imports: [RouterModule.forRoot(routes)], 8 | exports: [RouterModule], 9 | }) 10 | export class AppRoutingModule {} 11 | -------------------------------------------------------------------------------- /sandboxes/workspace/projects/app-one/src/app/app.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briebug/jest-schematic/9b95c78b6937b8692ca17e04eaae436f37ddadf0/sandboxes/workspace/projects/app-one/src/app/app.component.css -------------------------------------------------------------------------------- /sandboxes/workspace/projects/app-one/src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 304 | 305 | 306 | 327 | 328 |
329 | 330 | 331 |
332 | 333 | 334 | Rocket Ship 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | {{ title }} app is running! 345 | 346 | 347 | Rocket Ship Smoke 348 | 349 | 350 | 351 |
352 | 353 | 354 |

Resources

355 |

Here are some links to help you get started:

356 | 357 |
358 | 359 | 360 | Learn Angular 361 | 362 | 363 | 364 | 365 | CLI Documentation 366 | 367 | 368 | 369 | 370 | 371 | Angular Material 372 | 373 | 374 | 375 | 376 | 377 | Angular Blog 378 | 379 | 380 | 381 | 382 | 383 | Angular DevTools 384 | 385 | 386 | 387 |
388 | 389 | 390 |

Next Steps

391 |

What do you want to do next with your app?

392 | 393 | 394 | 395 |
396 | 400 | 401 | 405 | 406 | 410 | 411 | 415 | 416 | 420 | 421 | 425 |
426 | 427 | 428 |
429 |
ng generate component xyz
430 |
ng add @angular/material
431 |
ng add @angular/pwa
432 |
ng add _____
433 |
ng test
434 |
ng build
435 |
436 | 437 | 438 |
439 | 440 | 441 | Meetup Logo 442 | 443 | 444 | 445 | 446 | 447 | 448 | Discord Logo 449 | 450 | 451 | 452 | 453 |
454 | 455 | 456 | 468 | 469 | 470 | Gray Clouds Background 471 | 472 | 473 | 474 |
475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | -------------------------------------------------------------------------------- /sandboxes/workspace/projects/app-one/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | import { RouterTestingModule } from '@angular/router/testing'; 3 | import { AppComponent } from './app.component'; 4 | 5 | describe('AppComponent', () => { 6 | beforeEach(async () => { 7 | await TestBed.configureTestingModule({ 8 | imports: [RouterTestingModule], 9 | declarations: [AppComponent], 10 | }).compileComponents(); 11 | }); 12 | 13 | it('should create the app', () => { 14 | const fixture = TestBed.createComponent(AppComponent); 15 | const app = fixture.componentInstance; 16 | expect(app).toBeTruthy(); 17 | }); 18 | 19 | it(`should have as title 'app-one'`, () => { 20 | const fixture = TestBed.createComponent(AppComponent); 21 | const app = fixture.componentInstance; 22 | expect(app.title).toEqual('app-one'); 23 | }); 24 | 25 | it('should render title', () => { 26 | const fixture = TestBed.createComponent(AppComponent); 27 | fixture.detectChanges(); 28 | const compiled = fixture.nativeElement as HTMLElement; 29 | expect(compiled.querySelector('.content span')?.textContent).toContain( 30 | 'app-one app is running!' 31 | ); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /sandboxes/workspace/projects/app-one/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-root', 5 | templateUrl: './app.component.html', 6 | styleUrls: ['./app.component.css'], 7 | }) 8 | export class AppComponent { 9 | title = 'app-one'; 10 | } 11 | -------------------------------------------------------------------------------- /sandboxes/workspace/projects/app-one/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { BrowserModule } from '@angular/platform-browser'; 3 | 4 | import { AppRoutingModule } from './app-routing.module'; 5 | import { AppComponent } from './app.component'; 6 | 7 | @NgModule({ 8 | declarations: [AppComponent], 9 | imports: [BrowserModule, AppRoutingModule], 10 | providers: [], 11 | bootstrap: [AppComponent], 12 | }) 13 | export class AppModule {} 14 | -------------------------------------------------------------------------------- /sandboxes/workspace/projects/app-one/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briebug/jest-schematic/9b95c78b6937b8692ca17e04eaae436f37ddadf0/sandboxes/workspace/projects/app-one/src/assets/.gitkeep -------------------------------------------------------------------------------- /sandboxes/workspace/projects/app-one/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briebug/jest-schematic/9b95c78b6937b8692ca17e04eaae436f37ddadf0/sandboxes/workspace/projects/app-one/src/favicon.ico -------------------------------------------------------------------------------- /sandboxes/workspace/projects/app-one/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AppOne 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /sandboxes/workspace/projects/app-one/src/main.ts: -------------------------------------------------------------------------------- 1 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 2 | 3 | import { AppModule } from './app/app.module'; 4 | 5 | platformBrowserDynamic() 6 | .bootstrapModule(AppModule) 7 | .catch((err) => console.error(err)); 8 | -------------------------------------------------------------------------------- /sandboxes/workspace/projects/app-one/src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | -------------------------------------------------------------------------------- /sandboxes/workspace/projects/app-one/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "../../tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "../../out-tsc/app", 6 | "types": [] 7 | }, 8 | "files": ["src/main.ts"], 9 | "include": ["src/**/*.d.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /sandboxes/workspace/projects/app-one/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "../../tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "../../out-tsc/spec", 6 | "types": ["jasmine"] 7 | }, 8 | "include": ["src/**/*.spec.ts", "src/**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /sandboxes/workspace/projects/lib-one/README.md: -------------------------------------------------------------------------------- 1 | # LibOne 2 | 3 | This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 15.0.0. 4 | 5 | ## Code scaffolding 6 | 7 | Run `ng generate component component-name --project lib-one` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project lib-one`. 8 | 9 | > Note: Don't forget to add `--project lib-one` or else it will be added to the default project in your `angular.json` file. 10 | 11 | ## Build 12 | 13 | Run `ng build lib-one` to build the project. The build artifacts will be stored in the `dist/` directory. 14 | 15 | ## Publishing 16 | 17 | After building your library with `ng build lib-one`, go to the dist folder `cd dist/lib-one` and run `npm publish`. 18 | 19 | ## Running unit tests 20 | 21 | Run `ng test lib-one` to execute the unit tests via [Karma](https://karma-runner.github.io). 22 | 23 | ## Further help 24 | 25 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page. 26 | -------------------------------------------------------------------------------- /sandboxes/workspace/projects/lib-one/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", 3 | "dest": "../../dist/lib-one", 4 | "lib": { 5 | "entryFile": "src/public-api.ts" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /sandboxes/workspace/projects/lib-one/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lib-one", 3 | "version": "0.0.1", 4 | "peerDependencies": { 5 | "@angular/common": "^15.0.0", 6 | "@angular/core": "^15.0.0" 7 | }, 8 | "dependencies": { 9 | "tslib": "^2.3.0" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /sandboxes/workspace/projects/lib-one/src/lib/lib-one.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { LibOneComponent } from './lib-one.component'; 4 | 5 | describe('LibOneComponent', () => { 6 | let component: LibOneComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [LibOneComponent], 12 | }).compileComponents(); 13 | 14 | fixture = TestBed.createComponent(LibOneComponent); 15 | component = fixture.componentInstance; 16 | fixture.detectChanges(); 17 | }); 18 | 19 | it('should create', () => { 20 | expect(component).toBeTruthy(); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /sandboxes/workspace/projects/lib-one/src/lib/lib-one.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'lib-lib-one', 5 | template: `

lib-one works!

`, 6 | styles: [], 7 | }) 8 | export class LibOneComponent {} 9 | -------------------------------------------------------------------------------- /sandboxes/workspace/projects/lib-one/src/lib/lib-one.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { LibOneComponent } from './lib-one.component'; 3 | 4 | @NgModule({ 5 | declarations: [LibOneComponent], 6 | imports: [], 7 | exports: [LibOneComponent], 8 | }) 9 | export class LibOneModule {} 10 | -------------------------------------------------------------------------------- /sandboxes/workspace/projects/lib-one/src/lib/lib-one.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { LibOneService } from './lib-one.service'; 4 | 5 | describe('LibOneService', () => { 6 | let service: LibOneService; 7 | 8 | beforeEach(() => { 9 | TestBed.configureTestingModule({}); 10 | service = TestBed.inject(LibOneService); 11 | }); 12 | 13 | it('should be created', () => { 14 | expect(service).toBeTruthy(); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /sandboxes/workspace/projects/lib-one/src/lib/lib-one.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | @Injectable({ 4 | providedIn: 'root', 5 | }) 6 | export class LibOneService { 7 | constructor() {} 8 | } 9 | -------------------------------------------------------------------------------- /sandboxes/workspace/projects/lib-one/src/public-api.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Public API Surface of lib-one 3 | */ 4 | 5 | export * from './lib/lib-one.service'; 6 | export * from './lib/lib-one.component'; 7 | export * from './lib/lib-one.module'; 8 | -------------------------------------------------------------------------------- /sandboxes/workspace/projects/lib-one/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "../../tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "../../out-tsc/lib", 6 | "declaration": true, 7 | "declarationMap": true, 8 | "inlineSources": true, 9 | "types": [] 10 | }, 11 | "exclude": ["**/*.spec.ts"] 12 | } 13 | -------------------------------------------------------------------------------- /sandboxes/workspace/projects/lib-one/tsconfig.lib.prod.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.lib.json", 4 | "compilerOptions": { 5 | "declarationMap": false 6 | }, 7 | "angularCompilerOptions": { 8 | "compilationMode": "partial" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /sandboxes/workspace/projects/lib-one/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "../../tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "../../out-tsc/spec", 6 | "types": ["jasmine"] 7 | }, 8 | "include": ["**/*.spec.ts", "**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /sandboxes/workspace/tsconfig.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "compileOnSave": false, 4 | "compilerOptions": { 5 | "baseUrl": "./", 6 | "outDir": "./dist/out-tsc", 7 | "forceConsistentCasingInFileNames": true, 8 | "strict": true, 9 | "noImplicitOverride": true, 10 | "noPropertyAccessFromIndexSignature": true, 11 | "noImplicitReturns": true, 12 | "paths": { 13 | "lib-one": ["dist/lib-one"] 14 | }, 15 | "noFallthroughCasesInSwitch": true, 16 | "sourceMap": true, 17 | "declaration": false, 18 | "downlevelIteration": true, 19 | "experimentalDecorators": true, 20 | "moduleResolution": "node", 21 | "importHelpers": true, 22 | "target": "ES2022", 23 | "module": "ES2022", 24 | "useDefineForClassFields": false, 25 | "lib": ["ES2022", "dom"] 26 | }, 27 | "angularCompilerOptions": { 28 | "enableI18nLegacyMessageIdFormat": false, 29 | "strictInjectionParameters": true, 30 | "strictInputAccessModifiers": true, 31 | "strictTemplates": true 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /scripts/e2e.ts: -------------------------------------------------------------------------------- 1 | import { exec } from './utils'; 2 | import * as packageJson from '../package.json'; 3 | 4 | enum SandboxType { 5 | single = 'sandboxes/single-app', 6 | workspace = 'sandboxes/workspace', 7 | } 8 | const sandboxList = Object.keys(SandboxType); 9 | 10 | // build the schematic code 11 | const build = async () => { 12 | return await exec('tsc -p tsconfig.json'); 13 | }; 14 | 15 | // reset the sandboxes to their version controlled state 16 | const reset = async (type: SandboxType) => { 17 | return await exec(`git checkout HEAD -- ${type} && git clean -f -d ${type}`); 18 | }; 19 | 20 | // ensure the schematic code is linked so it can be called in a later step 21 | const link = async (type: SandboxType) => { 22 | return await exec(`yarn link && cd ${type} && yarn link ${packageJson.name}`); 23 | }; 24 | 25 | // simulate running the schematic 26 | const runSchematic = async (type: SandboxType) => { 27 | return await exec(`cd ${type} && yarn && ./node_modules/.bin/ng g ${packageJson.name}:jest`); 28 | }; 29 | 30 | // run the app or lib unit tests using jest 31 | const testSchematic = async (type: SandboxType) => { 32 | // Remove yarn lint since in angular 13, we must add eslint but now the schematic for add eslint not works with angular 13 33 | return await exec(`cd ${type} && yarn test`); 34 | }; 35 | 36 | // test that the app and/or lib builds after the schematic is executed 37 | const testProjectBuild = async (type: SandboxType) => { 38 | // Remove yarn lint since in angular 13, we must add eslint but now the schematic for add eslint not works with angular 13 39 | return type === SandboxType.single 40 | ? await exec(`cd ${type} && yarn build`) 41 | : await exec(`cd ${type} && yarn build app-one && yarn build lib-one`); 42 | }; 43 | 44 | const launch = async () => { 45 | let arg = process.argv[2] as string; 46 | 47 | if (arg === 'reset') { 48 | await reset(SandboxType.single); 49 | return await reset(SandboxType.workspace); 50 | } 51 | 52 | if (!arg || !sandboxList.find((t) => t.includes(arg))) { 53 | return [ 54 | `Invalid Sandbox type "${arg}"`, 55 | `Please provide a valid type: ${sandboxList.join(', ')}`, 56 | ].join('\n'); 57 | } 58 | 59 | const type = SandboxType[arg]; 60 | 61 | await build(); 62 | await reset(type); 63 | await link(type); 64 | await runSchematic(type); 65 | await testSchematic(type); 66 | await testProjectBuild(type); 67 | return await reset(type); 68 | }; 69 | 70 | launch() 71 | .then((msg) => { 72 | if (msg) { 73 | console.log(msg); 74 | } 75 | return process.exit(process.exitCode || 0); 76 | }) 77 | .catch((msg) => { 78 | if (msg) { 79 | console.log(msg); 80 | } 81 | return process.exit(process.exitCode || 1); 82 | }); 83 | -------------------------------------------------------------------------------- /scripts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "rootDir": ".", 5 | "lib": ["ESNext"], 6 | "types": ["node"], 7 | "strict": false, 8 | "strictNullChecks": false, 9 | "forceConsistentCasingInFileNames": true, 10 | "noUnusedLocals": true, 11 | "noUnusedParameters": true, 12 | "noImplicitReturns": true, 13 | "noFallthroughCasesInSwitch": true, 14 | "resolveJsonModule": true, 15 | "esModuleInterop": true 16 | }, 17 | "include": ["./**/*"] 18 | } 19 | -------------------------------------------------------------------------------- /scripts/utils.ts: -------------------------------------------------------------------------------- 1 | import { ExecOptions, exec as shellExec } from 'shelljs'; 2 | 3 | export const exec = async (command: string, options: ExecOptions = {}) => 4 | new Promise((resolve, reject) => { 5 | shellExec(command, options, (code) => { 6 | if (code === 0) { 7 | resolve(); 8 | } else { 9 | reject(new Error(`${command}\n exited with code: ${code}`)); 10 | } 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /src/collection.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../node_modules/@angular-devkit/schematics/collection-schema.json", 3 | "schematics": { 4 | "ng-add": { 5 | "description": "Adds Jest to the application", 6 | "factory": "./jest/index", 7 | "schema": "./jest/schema.json" 8 | }, 9 | "jest": { 10 | "description": "Adds Jest to the application", 11 | "factory": "./jest/index", 12 | "schema": "./jest/schema.json" 13 | }, 14 | "add": { 15 | "description": "Adds Jest to the application", 16 | "factory": "./jest/index", 17 | "schema": "./jest/schema.json" 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/interfaces/ts-config-schema.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * tsconfig.json schema: http://json.schemastore.org/tsconfig 3 | * Generated with https://transform.now.sh/json-schema-to-ts/ 4 | */ 5 | 6 | export type TsConfigSchema = (CompilerOptionsDefinition & CompileOnSaveDefinition & TypeAcquisitionDefinition & ExtendsDefinition & (FilesDefinition | ExcludeDefinition | IncludeDefinition | ReferencesDefinition)); 7 | 8 | export interface CompilerOptionsDefinition { 9 | /** 10 | * Instructs the TypeScript compiler how to compile .ts files. 11 | */ 12 | compilerOptions?: { 13 | /** 14 | * The character set of the input files. 15 | */ 16 | charset?: string; 17 | /** 18 | * Enables building for project references. 19 | */ 20 | composite?: boolean; 21 | /** 22 | * Generates corresponding d.ts files. 23 | */ 24 | declaration?: boolean; 25 | /** 26 | * Specify output directory for generated declaration files. Requires TypeScript version 2.0 or later. 27 | */ 28 | declarationDir?: string; 29 | /** 30 | * Show diagnostic information. 31 | */ 32 | diagnostics?: boolean; 33 | /** 34 | * Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. 35 | */ 36 | emitBOM?: boolean; 37 | /** 38 | * Only emit '.d.ts' declaration files. 39 | */ 40 | emitDeclarationOnly?: boolean; 41 | /** 42 | * Enable incremental compilation. 43 | */ 44 | incremental?: boolean; 45 | /** 46 | * Specify file to store incremental compilation information. 47 | */ 48 | tsBuildInfoFile?: string; 49 | /** 50 | * Emit a single file with source maps instead of having a separate file. 51 | */ 52 | inlineSourceMap?: boolean; 53 | /** 54 | * Emit the source alongside the sourcemaps within a single file; requires --inlineSourceMap to be set. 55 | */ 56 | inlineSources?: boolean; 57 | /** 58 | * Specify JSX code generation: 'preserve', 'react', or 'react-native'. 59 | */ 60 | jsx?: ("preserve" | "react" | "react-native"); 61 | /** 62 | * Specifies the object invoked for createElement and __spread when targeting 'react' JSX emit. 63 | */ 64 | reactNamespace?: string; 65 | /** 66 | * Print names of files part of the compilation. 67 | */ 68 | listFiles?: boolean; 69 | /** 70 | * Specifies the location where debugger should locate map files instead of generated locations 71 | */ 72 | mapRoot?: string; 73 | /** 74 | * Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015' or 'esnext'. 75 | */ 76 | module?: (("commonjs" | "amd" | "umd" | "system" | "es6" | "es2015" | "esnext" | "none") | { 77 | [k: string]: any; 78 | }); 79 | /** 80 | * Specifies the end of line sequence to be used when emitting files: 'CRLF' (dos) or 'LF' (unix). 81 | */ 82 | newLine?: (("CRLF" | "LF") | { 83 | [k: string]: any; 84 | }); 85 | /** 86 | * Do not emit output. 87 | */ 88 | noEmit?: boolean; 89 | /** 90 | * Do not generate custom helper functions like __extends in compiled output. 91 | */ 92 | noEmitHelpers?: boolean; 93 | /** 94 | * Do not emit outputs if any type checking errors were reported. 95 | */ 96 | noEmitOnError?: boolean; 97 | /** 98 | * Warn on expressions and declarations with an implied 'any' type. 99 | */ 100 | noImplicitAny?: boolean; 101 | /** 102 | * Raise error on 'this' expressions with an implied any type. 103 | */ 104 | noImplicitThis?: boolean; 105 | /** 106 | * Report errors on unused locals. Requires TypeScript version 2.0 or later. 107 | */ 108 | noUnusedLocals?: boolean; 109 | /** 110 | * Report errors on unused parameters. Requires TypeScript version 2.0 or later. 111 | */ 112 | noUnusedParameters?: boolean; 113 | /** 114 | * Do not include the default library file (lib.d.ts). 115 | */ 116 | noLib?: boolean; 117 | /** 118 | * Do not add triple-slash references or module import targets to the list of compiled files. 119 | */ 120 | noResolve?: boolean; 121 | /** 122 | * Disable strict checking of generic signatures in function types. 123 | */ 124 | noStrictGenericChecks?: boolean; 125 | skipDefaultLibCheck?: boolean; 126 | /** 127 | * Skip type checking of declaration files. Requires TypeScript version 2.0 or later. 128 | */ 129 | skipLibCheck?: boolean; 130 | /** 131 | * Concatenate and emit output to single file. 132 | */ 133 | outFile?: string; 134 | /** 135 | * Redirect output structure to the directory. 136 | */ 137 | outDir?: string; 138 | /** 139 | * Do not erase const enum declarations in generated code. 140 | */ 141 | preserveConstEnums?: boolean; 142 | /** 143 | * Do not resolve symlinks to their real path; treat a symlinked file like a real one. 144 | */ 145 | preserveSymlinks?: boolean; 146 | /** 147 | * Keep outdated console output in watch mode instead of clearing the screen. 148 | */ 149 | preserveWatchOutput?: boolean; 150 | /** 151 | * Stylize errors and messages using color and context (experimental). 152 | */ 153 | pretty?: boolean; 154 | /** 155 | * Do not emit comments to output. 156 | */ 157 | removeComments?: boolean; 158 | /** 159 | * Specifies the root directory of input files. Use to control the output directory structure with --outDir. 160 | */ 161 | rootDir?: string; 162 | /** 163 | * Unconditionally emit imports for unresolved files. 164 | */ 165 | isolatedModules?: boolean; 166 | /** 167 | * Generates corresponding '.map' file. 168 | */ 169 | sourceMap?: boolean; 170 | /** 171 | * Specifies the location where debugger should locate TypeScript files instead of source locations. 172 | */ 173 | sourceRoot?: string; 174 | /** 175 | * Suppress excess property checks for object literals. 176 | */ 177 | suppressExcessPropertyErrors?: boolean; 178 | /** 179 | * Suppress noImplicitAny errors for indexing objects lacking index signatures. 180 | */ 181 | suppressImplicitAnyIndexErrors?: boolean; 182 | /** 183 | * Do not emit declarations for code that has an '@internal' annotation. 184 | */ 185 | stripInternal?: boolean; 186 | /** 187 | * Specify ECMAScript target version. Permitted values are 'es3', 'es5', 'es6', 'es2015', 'es2016', 'es2017', 'es2018' or 'esnext'. 188 | */ 189 | target?: (("es3" | "es5" | "es6" | "es2015" | "es2016" | "es2017" | "es2018" | "esnext") | { 190 | [k: string]: any; 191 | }); 192 | /** 193 | * Watch input files. 194 | */ 195 | watch?: boolean; 196 | /** 197 | * Enables experimental support for ES7 decorators. 198 | */ 199 | experimentalDecorators?: boolean; 200 | /** 201 | * Emit design-type metadata for decorated declarations in source. 202 | */ 203 | emitDecoratorMetadata?: boolean; 204 | /** 205 | * Specifies module resolution strategy: 'node' (Node) or 'classic' (TypeScript pre 1.6) . 206 | */ 207 | moduleResolution?: (("classic" | "node") | { 208 | [k: string]: any; 209 | }); 210 | /** 211 | * Do not report errors on unused labels. 212 | */ 213 | allowUnusedLabels?: boolean; 214 | /** 215 | * Report error when not all code paths in function return a value. 216 | */ 217 | noImplicitReturns?: boolean; 218 | /** 219 | * Report errors for fallthrough cases in switch statement. 220 | */ 221 | noFallthroughCasesInSwitch?: boolean; 222 | /** 223 | * Do not report errors on unreachable code. 224 | */ 225 | allowUnreachableCode?: boolean; 226 | /** 227 | * Disallow inconsistently-cased references to the same file. 228 | */ 229 | forceConsistentCasingInFileNames?: boolean; 230 | /** 231 | * Base directory to resolve non-relative module names. 232 | */ 233 | baseUrl?: string; 234 | /** 235 | * Specify path mapping to be computed relative to baseUrl option. 236 | */ 237 | paths?: { 238 | [k: string]: string[]; 239 | }; 240 | /** 241 | * List of TypeScript language server plugins to load. Requires TypeScript version 2.3 or later. 242 | */ 243 | plugins?: { 244 | /** 245 | * Plugin name. 246 | */ 247 | name?: string; 248 | [k: string]: any; 249 | }[]; 250 | /** 251 | * Specify list of root directories to be used when resolving modules. 252 | */ 253 | rootDirs?: string[]; 254 | /** 255 | * Specify list of directories for type definition files to be included. Requires TypeScript version 2.0 or later. 256 | */ 257 | typeRoots?: string[]; 258 | /** 259 | * Type declaration files to be included in compilation. Requires TypeScript version 2.0 or later. 260 | */ 261 | types?: string[]; 262 | /** 263 | * Enable tracing of the name resolution process. 264 | */ 265 | traceResolution?: boolean; 266 | /** 267 | * Allow javascript files to be compiled. 268 | */ 269 | allowJs?: boolean; 270 | /** 271 | * Do not truncate error messages. 272 | */ 273 | noErrorTruncation?: boolean; 274 | /** 275 | * Allow default imports from modules with no default export. This does not affect code emit, just typechecking. 276 | */ 277 | allowSyntheticDefaultImports?: boolean; 278 | /** 279 | * Do not emit 'use strict' directives in module output. 280 | */ 281 | noImplicitUseStrict?: boolean; 282 | /** 283 | * Enable to list all emitted files. Requires TypeScript version 2.0 or later. 284 | */ 285 | listEmittedFiles?: boolean; 286 | /** 287 | * Disable size limit for JavaScript project. Requires TypeScript version 2.0 or later. 288 | */ 289 | disableSizeLimit?: boolean; 290 | /** 291 | * Specify library file to be included in the compilation. Requires TypeScript version 2.0 or later. 292 | */ 293 | lib?: ("es5" | "es6" | "es2015" | "es7" | "es2016" | "es2017" | "es2018" | "esnext" | "dom" | "dom.iterable" | "webworker" | "scripthost" | "es2015.core" | "es2015.collection" | "es2015.generator" | "es2015.iterable" | "es2015.promise" | "es2015.proxy" | "es2015.reflect" | "es2015.symbol" | "es2015.symbol.wellknown" | "es2016.array.include" | "es2017.object" | "es2017.intl" | "es2017.sharedmemory" | "es2017.string" | "es2017.typedarrays" | "es2018.intl" | "es2018.promise" | "es2018.regexp" | "esnext.asynciterable" | "esnext.array" | "esnext.intl" | "esnext.symbol")[]; 294 | /** 295 | * Enable strict null checks. Requires TypeScript version 2.0 or later. 296 | */ 297 | strictNullChecks?: boolean; 298 | /** 299 | * The maximum dependency depth to search under node_modules and load JavaScript files. Only applicable with --allowJs. 300 | */ 301 | maxNodeModuleJsDepth?: number; 302 | /** 303 | * Import emit helpers (e.g. '__extends', '__rest', etc..) from tslib. Requires TypeScript version 2.1 or later. 304 | */ 305 | importHelpers?: boolean; 306 | /** 307 | * Specify the JSX factory function to use when targeting react JSX emit, e.g. 'React.createElement' or 'h'. Requires TypeScript version 2.1 or later. 308 | */ 309 | jsxFactory?: string; 310 | /** 311 | * Parse in strict mode and emit 'use strict' for each source file. Requires TypeScript version 2.1 or later. 312 | */ 313 | alwaysStrict?: boolean; 314 | /** 315 | * Enable all strict type checking options. Requires TypeScript version 2.3 or later. 316 | */ 317 | strict?: boolean; 318 | /** 319 | * Enable stricter checking of of the `bind`, `call`, and `apply` methods on functions. 320 | */ 321 | strictBindCallApply?: boolean; 322 | /** 323 | * Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. Requires TypeScript version 2.3 or later. 324 | */ 325 | downlevelIteration?: boolean; 326 | /** 327 | * Report errors in .js files. Requires TypeScript version 2.3 or later. 328 | */ 329 | checkJs?: boolean; 330 | /** 331 | * Disable bivariant parameter checking for function types. Requires TypeScript version 2.6 or later. 332 | */ 333 | strictFunctionTypes?: boolean; 334 | /** 335 | * Ensure non-undefined class properties are initialized in the constructor. Requires TypeScript version 2.7 or later. 336 | */ 337 | strictPropertyInitialization?: boolean; 338 | /** 339 | * Emit '__importStar' and '__importDefault' helpers for runtime babel ecosystem compatibility and enable '--allowSyntheticDefaultImports' for typesystem compatibility. Requires TypeScript version 2.7 or later. 340 | */ 341 | esModuleInterop?: boolean; 342 | /** 343 | * Resolve 'keyof' to string valued property names only (no numbers or symbols). Requires TypeScript version 2.9 or later. 344 | */ 345 | keyofStringsOnly?: boolean; 346 | /** 347 | * Generates a sourcemap for each corresponding '.d.ts' file. Requires TypeScript version 2.9 or later. 348 | */ 349 | declarationMap?: boolean; 350 | /** 351 | * Include modules imported with '.json' extension. Requires TypeScript version 2.9 or later. 352 | */ 353 | resolveJsonModule?: boolean; 354 | [k: string]: any; 355 | }; 356 | [k: string]: any; 357 | } 358 | export interface CompileOnSaveDefinition { 359 | /** 360 | * Enable Compile-on-Save for this project. 361 | */ 362 | compileOnSave?: boolean; 363 | [k: string]: any; 364 | } 365 | export interface TypeAcquisitionDefinition { 366 | /** 367 | * Auto type (.d.ts) acquisition options for this project. Requires TypeScript version 2.1 or later. 368 | */ 369 | typeAcquisition?: { 370 | /** 371 | * Enable auto type acquisition 372 | */ 373 | enable?: boolean; 374 | /** 375 | * Specifies a list of type declarations to be included in auto type acquisition. Ex. ["jquery", "lodash"] 376 | */ 377 | include?: string[]; 378 | /** 379 | * Specifies a list of type declarations to be excluded from auto type acquisition. Ex. ["jquery", "lodash"] 380 | */ 381 | exclude?: string[]; 382 | [k: string]: any; 383 | }; 384 | [k: string]: any; 385 | } 386 | export interface ExtendsDefinition { 387 | /** 388 | * Path to base configuration file to inherit from. Requires TypeScript version 2.1 or later. 389 | */ 390 | extends?: string; 391 | [k: string]: any; 392 | } 393 | export interface FilesDefinition { 394 | /** 395 | * If no 'files' or 'include' property is present in a tsconfig.json, the compiler defaults to including all files in the containing directory and subdirectories except those specified by 'exclude'. When a 'files' property is specified, only those files and those specified by 'include' are included. 396 | */ 397 | files?: string[]; 398 | [k: string]: any; 399 | } 400 | export interface ExcludeDefinition { 401 | /** 402 | * Specifies a list of files to be excluded from compilation. The 'exclude' property only affects the files included via the 'include' property and not the 'files' property. Glob patterns require TypeScript version 2.0 or later. 403 | */ 404 | exclude?: string[]; 405 | [k: string]: any; 406 | } 407 | export interface IncludeDefinition { 408 | /** 409 | * Specifies a list of glob patterns that match files to be included in compilation. If no 'files' or 'include' property is present in a tsconfig.json, the compiler defaults to including all files in the containing directory and subdirectories except those specified by 'exclude'. Requires TypeScript version 2.0 or later. 410 | */ 411 | include?: string[]; 412 | [k: string]: any; 413 | } 414 | export interface ReferencesDefinition { 415 | /** 416 | * Referenced projects. Requires TypeScript version 3.0 or later. 417 | */ 418 | references?: { 419 | /** 420 | * Path to referenced tsconfig or to folder containing tsconfig. 421 | */ 422 | path?: string; 423 | [k: string]: any; 424 | }[]; 425 | [k: string]: any; 426 | } 427 | -------------------------------------------------------------------------------- /src/jest/files/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'jest-preset-angular', 3 | globalSetup: 'jest-preset-angular/global-setup', 4 | }; 5 | -------------------------------------------------------------------------------- /src/jest/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Rule, 3 | SchematicContext, 4 | Tree, 5 | chain, 6 | url, 7 | apply, 8 | move, 9 | mergeWith, 10 | template, 11 | } from '@angular-devkit/schematics'; 12 | import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks'; 13 | 14 | import { 15 | removePackageJsonDependency, 16 | JestOptions, 17 | safeFileDelete, 18 | getAngularVersion, 19 | getLatestNodeVersion, 20 | NodePackage, 21 | } from '../utility/util'; 22 | 23 | import { addPackageJsonDependency, NodeDependencyType } from '../utility/dependencies'; 24 | 25 | import { Observable, of, concat } from 'rxjs'; 26 | import { map, concatMap } from 'rxjs/operators'; 27 | import { TsConfigSchema } from '../interfaces/ts-config-schema'; 28 | 29 | import { getWorkspaceConfig, readJsonInTree } from '@schuchard/schematics-core'; 30 | 31 | export default function (options: JestOptions): Rule { 32 | return (tree: Tree, context: SchematicContext) => { 33 | options = { ...options, __version__: getAngularVersion(tree) }; 34 | 35 | return chain([ 36 | updateDependencies(), 37 | removeFiles(), 38 | updateAngularJson(), 39 | addRootFiles(), 40 | addWorkspaceFiles(), 41 | configureTsConfig(), 42 | ])(tree, context); 43 | }; 44 | } 45 | 46 | function updateDependencies(): Rule { 47 | return (tree: Tree, context: SchematicContext): Observable => { 48 | context.logger.debug('Updating dependencies...'); 49 | context.addTask(new NodePackageInstallTask()); 50 | 51 | const removeDependencies = of( 52 | '@types/jasmine', 53 | 'jasmine-core', 54 | 'karma-chrome-launcher', 55 | 'karma-coverage-istanbul-reporter', 56 | 'karma-coverage', 57 | 'karma-jasmine-html-reporter', 58 | 'karma-jasmine', 59 | 'karma' 60 | ).pipe( 61 | map((packageName: string) => { 62 | context.logger.debug(`Removing ${packageName} dependency`); 63 | 64 | removePackageJsonDependency(tree, { 65 | type: NodeDependencyType.Dev, 66 | name: packageName, 67 | }); 68 | 69 | return tree; 70 | }) 71 | ); 72 | 73 | const addDependencies = of(['jest', '28'], ['@types/jest'], ['@angular-builders/jest']).pipe( 74 | concatMap((dependency) => getLatestNodeVersion(dependency)), 75 | map((packageFromRegistry: NodePackage) => { 76 | const { name, version } = packageFromRegistry; 77 | context.logger.debug(`Adding ${name}:${version} to ${NodeDependencyType.Dev}`); 78 | 79 | addPackageJsonDependency(tree, { 80 | type: NodeDependencyType.Dev, 81 | name, 82 | version, 83 | }); 84 | 85 | return tree; 86 | }) 87 | ); 88 | 89 | return concat(removeDependencies, addDependencies); 90 | }; 91 | } 92 | 93 | function removeFiles(): Rule { 94 | return (tree: Tree, context: SchematicContext) => { 95 | const angularProjects = Object.values( 96 | (getWorkspaceConfig(tree)?.projects as Record) ?? {} 97 | ).map((o) => o.root); 98 | 99 | const deleteFiles = [ 100 | 'src/karma.conf.js', 101 | 'karma.conf.js', 102 | 103 | // unable to overwrite these with the url() approach. 104 | 'jest.config.js', 105 | 'src/setup-jest.ts', 106 | 'src/test-config.helper.ts', 107 | ]; 108 | 109 | const projects = angularProjects.map((root: string) => 110 | deleteFiles.map((deletePath) => `${root}/${deletePath}`) 111 | ); 112 | 113 | projects.forEach((paths) => { 114 | paths.forEach((path) => { 115 | context.logger.debug(`removing ${path}`); 116 | 117 | safeFileDelete(tree, path); 118 | }); 119 | }); 120 | 121 | return tree; 122 | }; 123 | } 124 | 125 | function updateAngularJson(): Rule { 126 | return (tree: Tree) => { 127 | const angularJson = getWorkspaceConfig(tree); 128 | 129 | Object.values(angularJson.projects as Record).forEach((o) => { 130 | const { test } = o?.architect; 131 | 132 | if (test?.builder) { 133 | test.builder = '@angular-builders/jest:run'; 134 | delete test.options.main; 135 | delete test.options.polyfills; 136 | delete test.options.inlineStyleLanguage; 137 | delete test.options.karmaConfig; 138 | } 139 | }); 140 | 141 | // todo use project formatter or an ast update strategy to avoid formatting irrelevant fields 142 | tree.overwrite('angular.json', JSON.stringify(angularJson, null, 2)); 143 | return tree; 144 | }; 145 | } 146 | 147 | function addRootFiles(): Rule { 148 | return (tree: Tree, context: SchematicContext) => { 149 | context.logger.debug('adding jest files to host dir'); 150 | 151 | return chain([mergeWith(apply(url('./files'), [move('./')]))])(tree, context); 152 | }; 153 | } 154 | 155 | function addWorkspaceFiles(): Rule { 156 | return (tree: Tree, context: SchematicContext) => { 157 | context.logger.debug('adding jest files to workspace projects'); 158 | const { projects } = getWorkspaceConfig(tree); 159 | 160 | if (!projects || !Object.keys(projects).length) { 161 | return tree; 162 | } 163 | 164 | const paths = Object.values(projects) 165 | .map((proj: any) => proj?.root as string) 166 | .filter((path) => !!path) 167 | .map((path) => mergeWith(apply(url('./workspace-files'), [template({ path }), move(path)]))); 168 | 169 | return chain(paths)(tree, context); 170 | }; 171 | } 172 | 173 | function configureTsConfig(): Rule { 174 | return (tree: Tree) => { 175 | const angularJson = getWorkspaceConfig(tree); 176 | 177 | Object.values(angularJson.projects as Record) 178 | .map((o) => o?.architect?.test?.options?.tsConfig as string) 179 | .filter((path) => !!path) 180 | .forEach((path) => { 181 | const json = readJsonInTree(tree, path); 182 | 183 | json.compilerOptions = { 184 | ...json.compilerOptions, 185 | }; 186 | 187 | json.compilerOptions.types = (json.compilerOptions?.types ?? []) 188 | .filter((type: string) => !['jasmine'].some((jasmineType) => jasmineType === type)) 189 | .concat('jest'); 190 | 191 | tree.overwrite(path, JSON.stringify(json, null, 2) + '\n'); 192 | }); 193 | 194 | return tree; 195 | }; 196 | } 197 | -------------------------------------------------------------------------------- /src/jest/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/schema", 3 | "$id": "BriebugSchematicsJest", 4 | "title": "Jest Install Options Schema", 5 | "type": "object", 6 | "properties": {}, 7 | "required": [] 8 | } 9 | -------------------------------------------------------------------------------- /src/jest/workspace-files/jest.config.js: -------------------------------------------------------------------------------- 1 | const baseConfig = require('../../jest.config'); 2 | 3 | module.exports = { 4 | ...baseConfig, 5 | moduleNameMapper: { 6 | '@core/(.*)': '/src/app/core/$1', 7 | }, 8 | preset: 'jest-preset-angular', 9 | globals: { 10 | 'ts-jest': { 11 | tsconfig: '/<%= path %>/tsconfig.spec.json', 12 | }, 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /src/utility/dependencies.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright Google Inc. All Rights Reserved. 4 | * 5 | * Use of this source code is governed by an MIT-style license that can be 6 | * found in the LICENSE file at https://angular.io/license 7 | * https://github.com/angular/angular-cli/blob/master/packages/schematics/angular/utility/dependencies.ts 8 | */ 9 | 10 | import { Tree } from '@angular-devkit/schematics'; 11 | import { 12 | appendPropertyInAstObject, 13 | findPropertyInAstObject, 14 | insertPropertyInAstObjectInOrder, 15 | } from './json-utils'; 16 | import { parseJsonAtPath } from './util'; 17 | 18 | export enum pkgJson { 19 | Path = '/package.json' 20 | } 21 | 22 | export enum NodeDependencyType { 23 | Default = 'dependencies', 24 | Dev = 'devDependencies', 25 | Peer = 'peerDependencies', 26 | Optional = 'optionalDependencies', 27 | } 28 | 29 | export interface NodeDependency { 30 | type: NodeDependencyType; 31 | name: string; 32 | version: string; 33 | overwrite?: boolean; 34 | } 35 | 36 | export interface DeleteNodeDependency { 37 | type: NodeDependencyType, 38 | name: string 39 | } 40 | 41 | export function addPackageJsonDependency( 42 | tree: Tree, 43 | dependency: NodeDependency 44 | ): void { 45 | const packageJsonAst = parseJsonAtPath(tree, pkgJson.Path); 46 | const depsNode = findPropertyInAstObject(packageJsonAst, dependency.type); 47 | const recorder = tree.beginUpdate(pkgJson.Path); 48 | 49 | if (!depsNode) { 50 | // Haven't found the dependencies key, add it to the root of the package.json. 51 | appendPropertyInAstObject( 52 | recorder, 53 | packageJsonAst, 54 | dependency.type, 55 | { 56 | [dependency.name]: dependency.version, 57 | }, 58 | 4 59 | ); 60 | } else if (depsNode.kind === 'object') { 61 | // check if package already added 62 | const depNode = findPropertyInAstObject(depsNode, dependency.name); 63 | 64 | if (!depNode) { 65 | // Package not found, add it. 66 | insertPropertyInAstObjectInOrder( 67 | recorder, 68 | depsNode, 69 | dependency.name, 70 | dependency.version, 71 | 4 72 | ); 73 | } else if (dependency.overwrite) { 74 | // Package found, update version if overwrite. 75 | const { end, start } = depNode; 76 | recorder.remove(start.offset, end.offset - start.offset); 77 | recorder.insertRight(start.offset, JSON.stringify(dependency.version)); 78 | } 79 | } 80 | 81 | tree.commitUpdate(recorder); 82 | } 83 | 84 | export function getPackageJsonDependency( 85 | tree: Tree, 86 | name: string 87 | ): NodeDependency | null { 88 | const packageJson = parseJsonAtPath(tree, pkgJson.Path); 89 | let dep: NodeDependency | null = null; 90 | [ 91 | NodeDependencyType.Default, 92 | NodeDependencyType.Dev, 93 | NodeDependencyType.Optional, 94 | NodeDependencyType.Peer, 95 | ].forEach((depType) => { 96 | if (dep !== null) { 97 | return; 98 | } 99 | const depsNode = findPropertyInAstObject(packageJson, depType); 100 | if (depsNode !== null && depsNode.kind === 'object') { 101 | const depNode = findPropertyInAstObject(depsNode, name); 102 | if (depNode !== null && depNode.kind === 'string') { 103 | const version = depNode.value; 104 | dep = { 105 | type: depType, 106 | name: name, 107 | version: version, 108 | }; 109 | } 110 | } 111 | }); 112 | 113 | return dep; 114 | } 115 | -------------------------------------------------------------------------------- /src/utility/json-utils.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright Google Inc. All Rights Reserved. 4 | * 5 | * Use of this source code is governed by an MIT-style license that can be 6 | * found in the LICENSE file at https://angular.io/license 7 | * https://github.com/angular/angular-cli/blob/master/packages/schematics/angular/utility/json-utils.ts 8 | */ 9 | import { 10 | JsonAstArray, 11 | JsonAstKeyValue, 12 | JsonAstNode, 13 | JsonAstObject, 14 | JsonValue, 15 | } from '@angular-devkit/core'; 16 | import { UpdateRecorder } from '@angular-devkit/schematics'; 17 | 18 | export function appendPropertyInAstObject( 19 | recorder: UpdateRecorder, 20 | node: JsonAstObject, 21 | propertyName: string, 22 | value: JsonValue, 23 | indent: number, 24 | ) { 25 | const indentStr = _buildIndent(indent); 26 | 27 | if (node.properties.length > 0) { 28 | // Insert comma. 29 | const last = node.properties[node.properties.length - 1]; 30 | recorder.insertRight(last.start.offset + last.text.replace(/\s+$/, '').length, ','); 31 | } 32 | 33 | recorder.insertLeft( 34 | node.end.offset - 1, 35 | ' ' 36 | + `"${propertyName}": ${JSON.stringify(value, null, 2).replace(/\n/g, indentStr)}` 37 | + indentStr.slice(0, -2), 38 | ); 39 | } 40 | 41 | export function insertPropertyInAstObjectInOrder( 42 | recorder: UpdateRecorder, 43 | node: JsonAstObject, 44 | propertyName: string, 45 | value: JsonValue, 46 | indent: number, 47 | ) { 48 | 49 | if (node.properties.length === 0) { 50 | appendPropertyInAstObject(recorder, node, propertyName, value, indent); 51 | 52 | return; 53 | } 54 | 55 | // Find insertion info. 56 | let insertAfterProp: JsonAstKeyValue | null = null; 57 | let prev: JsonAstKeyValue | null = null; 58 | let isLastProp = false; 59 | const last = node.properties[node.properties.length - 1]; 60 | for (const prop of node.properties) { 61 | if (prop.key.value > propertyName) { 62 | if (prev) { 63 | insertAfterProp = prev; 64 | } 65 | break; 66 | } 67 | if (prop === last) { 68 | isLastProp = true; 69 | insertAfterProp = last; 70 | } 71 | prev = prop; 72 | } 73 | 74 | if (isLastProp) { 75 | appendPropertyInAstObject(recorder, node, propertyName, value, indent); 76 | 77 | return; 78 | } 79 | 80 | const indentStr = _buildIndent(indent); 81 | 82 | const insertIndex = insertAfterProp === null 83 | ? node.start.offset + 1 84 | : insertAfterProp.end.offset + 1; 85 | 86 | recorder.insertRight( 87 | insertIndex, 88 | `${indentStr}` 89 | + `"${propertyName}": ${JSON.stringify(value, null, 2).replace(/\n/g, indentStr)}` 90 | + ',', 91 | ); 92 | } 93 | 94 | 95 | export function appendValueInAstArray( 96 | recorder: UpdateRecorder, 97 | node: JsonAstArray, 98 | value: JsonValue, 99 | indent = 4, 100 | ) { 101 | const indentStr = _buildIndent(indent); 102 | 103 | if (node.elements.length > 0) { 104 | // Insert comma. 105 | const last = node.elements[node.elements.length - 1]; 106 | recorder.insertRight(last.start.offset + last.text.replace(/\s+$/, '').length, ','); 107 | } 108 | 109 | recorder.insertLeft( 110 | node.end.offset - 1, 111 | ' ' 112 | + JSON.stringify(value, null, 2).replace(/\n/g, indentStr) 113 | + indentStr.slice(0, -2), 114 | ); 115 | } 116 | 117 | 118 | export function findPropertyInAstObject( 119 | node: JsonAstObject, 120 | propertyName: string, 121 | ): JsonAstNode | null { 122 | let maybeNode: JsonAstNode | null = null; 123 | for (const property of node.properties) { 124 | if (property.key.value == propertyName) { 125 | maybeNode = property.value; 126 | } 127 | } 128 | 129 | return maybeNode; 130 | } 131 | 132 | function _buildIndent(count: number): string { 133 | return '\n' + new Array(count + 1).join(' '); 134 | } 135 | -------------------------------------------------------------------------------- /src/utility/util.ts: -------------------------------------------------------------------------------- 1 | import { JsonParseMode, parseJsonAst, JsonAstObject } from '@angular-devkit/core'; 2 | 3 | import { SchematicsException, Tree } from '@angular-devkit/schematics'; 4 | 5 | import { pkgJson, DeleteNodeDependency, getPackageJsonDependency } from './dependencies'; 6 | 7 | import { findPropertyInAstObject } from './json-utils'; 8 | 9 | import { get } from 'https'; 10 | 11 | export interface NodePackage { 12 | name: string; 13 | version: string; 14 | } 15 | 16 | export enum Configs { 17 | JsonIndentLevel = 4, 18 | } 19 | 20 | export interface JestOptions { 21 | updateTests?: boolean; 22 | project?: string; 23 | config?: 'file' | 'packagejson' | string; 24 | overwrite?: boolean; 25 | __version__: number; 26 | } 27 | 28 | export function getAngularVersion(tree: Tree): number { 29 | const packageNode = getPackageJsonDependency(tree, '@angular/core'); 30 | const version = 31 | packageNode && 32 | packageNode.version 33 | .replace(/[~^]/, '') 34 | .split('.') 35 | .find((x) => !!parseInt(x, 10)); 36 | 37 | return version ? +version : 0; 38 | } 39 | 40 | // modified version from utility/dependencies/getPackageJsonDependency 41 | export function removePackageJsonDependency(tree: Tree, dependency: DeleteNodeDependency): void { 42 | const packageJsonAst = parseJsonAtPath(tree, pkgJson.Path); 43 | const depsNode = findPropertyInAstObject(packageJsonAst, dependency.type); 44 | const recorder = tree.beginUpdate(pkgJson.Path); 45 | 46 | if (!depsNode) { 47 | // Haven't found the dependencies key. 48 | new SchematicsException('Could not find the package.json dependency'); 49 | } else if (depsNode.kind === 'object') { 50 | const fullPackageString = depsNode.text.split('\n').filter((pkg) => { 51 | return pkg.includes(`"${dependency.name}"`); 52 | })[0]; 53 | 54 | const commaDangle = fullPackageString && fullPackageString.trim().slice(-1) === ',' ? 1 : 0; 55 | 56 | const packageAst = depsNode.properties.find((node) => { 57 | return node.key.value.toLowerCase() === dependency.name.toLowerCase(); 58 | }); 59 | 60 | // TODO: does this work for the last dependency? 61 | const newLineIndentation = 5; 62 | 63 | if (packageAst) { 64 | // Package found, remove it. 65 | const end = packageAst.end.offset + commaDangle; 66 | 67 | recorder.remove( 68 | packageAst.key.start.offset, 69 | end - packageAst.start.offset + newLineIndentation 70 | ); 71 | } 72 | } 73 | 74 | tree.commitUpdate(recorder); 75 | } 76 | 77 | export function safeFileDelete(tree: Tree, path: string): boolean { 78 | if (tree.exists(path)) { 79 | tree.delete(path); 80 | return true; 81 | } else { 82 | return false; 83 | } 84 | } 85 | 86 | /** 87 | * Attempt to retrieve the latest package version from NPM 88 | * Return an optional "latest" version in case of error 89 | * @param packageName 90 | */ 91 | export function getLatestNodeVersion([packageName, ceiling]: string[]): Promise { 92 | const DEFAULT_VERSION = 'latest'; 93 | 94 | return new Promise((resolve) => { 95 | return get(`https://registry.npmjs.org/${packageName}`, (res) => { 96 | let rawData = ''; 97 | res.on('data', (chunk) => (rawData += chunk)); 98 | res.on('end', () => { 99 | try { 100 | if (rawData) { 101 | const response = JSON.parse(rawData); 102 | const version = ceiling 103 | ? Object.keys(response?.versions) 104 | .filter((v) => !v.includes('-')) 105 | .filter((v) => v.startsWith(ceiling)) 106 | .pop() 107 | : (response && response['dist-tags']).latest || {}; 108 | 109 | resolve(buildPackage(packageName, version)); 110 | } else { 111 | resolve(buildPackage(packageName)); 112 | } 113 | } catch (e) { 114 | console.log('ERROR', e); 115 | resolve(buildPackage(packageName)); 116 | } 117 | }); 118 | }).on('error', () => resolve(buildPackage(packageName))); 119 | }); 120 | 121 | function buildPackage(name: string, version: string = DEFAULT_VERSION): NodePackage { 122 | return { name, version }; 123 | } 124 | } 125 | 126 | export function parseJsonAtPath(tree: Tree, path: string): JsonAstObject { 127 | const buffer = tree.read(path); 128 | 129 | if (buffer === null) { 130 | throw new SchematicsException('Could not read package.json.'); 131 | } 132 | 133 | const content = buffer.toString(); 134 | 135 | const json = parseJsonAst(content, JsonParseMode.CommentsAllowed); 136 | 137 | if (json.kind != 'object') { 138 | throw new SchematicsException('Invalid package.json. Was expecting an object'); 139 | } 140 | 141 | return json; 142 | } 143 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "./", 4 | "lib": ["es2017", "dom"], 5 | "declaration": true, 6 | "module": "commonjs", 7 | "moduleResolution": "node", 8 | "noEmitOnError": true, 9 | "noFallthroughCasesInSwitch": true, 10 | "noImplicitAny": true, 11 | "noImplicitThis": true, 12 | "noUnusedParameters": true, 13 | "noUnusedLocals": true, 14 | "rootDir": "src/", 15 | "skipDefaultLibCheck": true, 16 | "skipLibCheck": true, 17 | "sourceMap": true, 18 | "strictNullChecks": true, 19 | "target": "es6", 20 | "types": ["jasmine", "node"] 21 | }, 22 | "include": ["src/**/*"], 23 | "exclude": ["node_modules", "src/*/files/**/*"] 24 | } 25 | --------------------------------------------------------------------------------