├── .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 | [](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 | 
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 |
343 |
344 |
{{ title }} app is running!
345 |
346 |
350 |
351 |
352 |
353 |
354 |
Resources
355 |
Here are some links to help you get started:
356 |
357 |
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 |
454 |
455 |
456 |
468 |
469 |
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 |
343 |
344 |
{{ title }} app is running!
345 |
346 |
350 |
351 |
352 |
353 |
354 |
Resources
355 |
Here are some links to help you get started:
356 |
357 |
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 |
454 |
455 |
456 |
468 |
469 |
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 |
--------------------------------------------------------------------------------