├── .editorconfig
├── .eslintignore
├── .eslintrc.json
├── .gitignore
├── .prettierignore
├── .prettierrc
├── .vscode
├── extensions.json
└── settings.json
├── README.md
├── apps
└── examples
│ ├── angular
│ ├── list-e2e
│ │ ├── .eslintrc.json
│ │ ├── cypress.json
│ │ ├── project.json
│ │ ├── src
│ │ │ ├── fixtures
│ │ │ │ └── example.json
│ │ │ ├── integration
│ │ │ │ └── app.spec.ts
│ │ │ └── support
│ │ │ │ ├── app.po.ts
│ │ │ │ ├── commands.ts
│ │ │ │ └── index.ts
│ │ └── tsconfig.json
│ ├── list
│ │ ├── .eslintrc.json
│ │ ├── jest.config.ts
│ │ ├── project.json
│ │ ├── src
│ │ │ ├── app
│ │ │ │ ├── app.component.html
│ │ │ │ ├── app.component.scss
│ │ │ │ ├── app.component.spec.ts
│ │ │ │ ├── app.component.ts
│ │ │ │ └── app.module.ts
│ │ │ ├── assets
│ │ │ │ └── .gitkeep
│ │ │ ├── environments
│ │ │ │ ├── environment.prod.ts
│ │ │ │ └── environment.ts
│ │ │ ├── favicon.ico
│ │ │ ├── index.html
│ │ │ ├── main.ts
│ │ │ ├── polyfills.ts
│ │ │ ├── styles.scss
│ │ │ └── test-setup.ts
│ │ ├── tsconfig.app.json
│ │ ├── tsconfig.editor.json
│ │ ├── tsconfig.json
│ │ └── tsconfig.spec.json
│ ├── tic-tac-toe-e2e
│ │ ├── .eslintrc.json
│ │ ├── cypress.json
│ │ ├── project.json
│ │ ├── src
│ │ │ ├── fixtures
│ │ │ │ └── example.json
│ │ │ ├── integration
│ │ │ │ └── app.spec.ts
│ │ │ └── support
│ │ │ │ ├── app.po.ts
│ │ │ │ ├── commands.ts
│ │ │ │ └── index.ts
│ │ └── tsconfig.json
│ └── tic-tac-toe
│ │ ├── .eslintrc.json
│ │ ├── jest.config.ts
│ │ ├── project.json
│ │ ├── src
│ │ ├── app
│ │ │ ├── app.component.html
│ │ │ ├── app.component.scss
│ │ │ ├── app.component.spec.ts
│ │ │ ├── app.component.ts
│ │ │ └── app.module.ts
│ │ ├── assets
│ │ │ └── .gitkeep
│ │ ├── environments
│ │ │ ├── environment.prod.ts
│ │ │ └── environment.ts
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ ├── main.ts
│ │ ├── polyfills.ts
│ │ ├── styles.scss
│ │ └── test-setup.ts
│ │ ├── tsconfig.app.json
│ │ ├── tsconfig.editor.json
│ │ ├── tsconfig.json
│ │ └── tsconfig.spec.json
│ ├── react
│ ├── list-e2e
│ │ ├── .eslintrc.json
│ │ ├── cypress.json
│ │ ├── project.json
│ │ ├── src
│ │ │ ├── fixtures
│ │ │ │ └── example.json
│ │ │ ├── integration
│ │ │ │ └── app.spec.ts
│ │ │ └── support
│ │ │ │ ├── app.po.ts
│ │ │ │ ├── commands.ts
│ │ │ │ └── index.ts
│ │ └── tsconfig.json
│ ├── list
│ │ ├── .babelrc
│ │ ├── .eslintrc.json
│ │ ├── jest.config.ts
│ │ ├── project.json
│ │ ├── src
│ │ │ ├── app
│ │ │ │ ├── CustomListImpl.tsx
│ │ │ │ ├── List.tsx
│ │ │ │ ├── app.module.scss
│ │ │ │ ├── app.spec.tsx
│ │ │ │ ├── app.tsx
│ │ │ │ ├── logo.svg
│ │ │ │ └── star.svg
│ │ │ ├── assets
│ │ │ │ └── .gitkeep
│ │ │ ├── environments
│ │ │ │ ├── environment.prod.ts
│ │ │ │ └── environment.ts
│ │ │ ├── favicon.ico
│ │ │ ├── index.html
│ │ │ ├── main.tsx
│ │ │ ├── polyfills.ts
│ │ │ └── styles.scss
│ │ ├── tsconfig.app.json
│ │ ├── tsconfig.json
│ │ └── tsconfig.spec.json
│ ├── tic-tac-toe-e2e
│ │ ├── .eslintrc.json
│ │ ├── cypress.json
│ │ ├── project.json
│ │ ├── src
│ │ │ ├── fixtures
│ │ │ │ └── example.json
│ │ │ ├── integration
│ │ │ │ └── app.spec.ts
│ │ │ └── support
│ │ │ │ ├── app.po.ts
│ │ │ │ ├── commands.ts
│ │ │ │ └── index.ts
│ │ └── tsconfig.json
│ └── tic-tac-toe
│ │ ├── .babelrc
│ │ ├── .eslintrc.json
│ │ ├── jest.config.ts
│ │ ├── project.json
│ │ ├── src
│ │ ├── app
│ │ │ ├── TicTacToe.tsx
│ │ │ ├── app.module.scss
│ │ │ ├── app.spec.tsx
│ │ │ ├── app.tsx
│ │ │ ├── logo.svg
│ │ │ └── star.svg
│ │ ├── assets
│ │ │ └── .gitkeep
│ │ ├── environments
│ │ │ ├── environment.prod.ts
│ │ │ └── environment.ts
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ ├── main.tsx
│ │ ├── polyfills.ts
│ │ └── styles.scss
│ │ ├── tsconfig.app.json
│ │ ├── tsconfig.json
│ │ └── tsconfig.spec.json
│ └── vanilla
│ ├── list-e2e
│ ├── .eslintrc.json
│ ├── cypress.json
│ ├── project.json
│ ├── src
│ │ ├── fixtures
│ │ │ └── example.json
│ │ ├── integration
│ │ │ └── app.spec.ts
│ │ └── support
│ │ │ ├── app.po.ts
│ │ │ ├── commands.ts
│ │ │ └── index.ts
│ └── tsconfig.json
│ ├── list
│ ├── .babelrc
│ ├── .eslintrc.json
│ ├── jest.config.ts
│ ├── project.json
│ ├── src
│ │ ├── app
│ │ │ ├── app.element.css
│ │ │ └── app.element.ts
│ │ ├── assets
│ │ │ └── .gitkeep
│ │ ├── environments
│ │ │ ├── environment.prod.ts
│ │ │ └── environment.ts
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ ├── main.ts
│ │ ├── polyfills.ts
│ │ ├── styles.css
│ │ └── test-setup.ts
│ ├── tsconfig.app.json
│ ├── tsconfig.json
│ └── tsconfig.spec.json
│ ├── tic-tac-toe-e2e
│ ├── .eslintrc.json
│ ├── cypress.json
│ ├── project.json
│ ├── src
│ │ ├── fixtures
│ │ │ └── example.json
│ │ ├── integration
│ │ │ └── app.spec.ts
│ │ └── support
│ │ │ ├── app.po.ts
│ │ │ ├── commands.ts
│ │ │ └── index.ts
│ └── tsconfig.json
│ └── tic-tac-toe
│ ├── .babelrc
│ ├── .eslintrc.json
│ ├── jest.config.ts
│ ├── project.json
│ ├── src
│ ├── app
│ │ ├── app.element.scss
│ │ └── app.element.ts
│ ├── assets
│ │ └── .gitkeep
│ ├── environments
│ │ ├── environment.prod.ts
│ │ └── environment.ts
│ ├── favicon.ico
│ ├── index.html
│ ├── main.ts
│ ├── polyfills.ts
│ ├── styles.scss
│ └── test-setup.ts
│ ├── tsconfig.app.json
│ ├── tsconfig.json
│ └── tsconfig.spec.json
├── babel.config.json
├── derxjs-logo.jpg
├── derxjs-react-logo.png
├── jest.config.ts
├── jest.preset.js
├── libs
└── examples
│ ├── list-view-model-implementation
│ ├── .babelrc
│ ├── .eslintrc.json
│ ├── README.md
│ ├── jest.config.ts
│ ├── project.json
│ ├── src
│ │ ├── index.ts
│ │ └── lib
│ │ │ ├── custom-implementation.ts
│ │ │ ├── implementation-using-reducer-package.ts
│ │ │ ├── list-view-model-implementation.spec.ts
│ │ │ └── types.ts
│ ├── tsconfig.json
│ ├── tsconfig.lib.json
│ └── tsconfig.spec.json
│ └── tic-tac-toe-view-model-implementation
│ ├── .babelrc
│ ├── .eslintrc.json
│ ├── README.md
│ ├── jest.config.ts
│ ├── project.json
│ ├── src
│ ├── index.ts
│ └── lib
│ │ ├── random-ai.ts
│ │ ├── types.ts
│ │ ├── using-reducer-package.spec.ts
│ │ ├── using-reducer-package.ts
│ │ ├── view-model.spec.ts
│ │ └── view-model.ts
│ ├── tsconfig.json
│ ├── tsconfig.lib.json
│ └── tsconfig.spec.json
├── migrations.json
├── nx.json
├── package-lock.json
├── package.json
├── packages
├── .gitkeep
├── react
│ ├── .babelrc
│ ├── .eslintrc.json
│ ├── LICENSE.md
│ ├── README.md
│ ├── jest.config.ts
│ ├── package.json
│ ├── project.json
│ ├── src
│ │ ├── index.ts
│ │ └── lib
│ │ │ └── DeRxJSComponent.tsx
│ ├── tsconfig.json
│ ├── tsconfig.lib.json
│ └── tsconfig.spec.json
├── reducer
│ ├── .babelrc
│ ├── .eslintrc.json
│ ├── .gitignore
│ ├── .npmignore
│ ├── LICENSE.md
│ ├── README.md
│ ├── jest.config.ts
│ ├── package.json
│ ├── project.json
│ ├── src
│ │ ├── index.ts
│ │ └── lib
│ │ │ └── reducer.ts
│ ├── tsconfig.json
│ ├── tsconfig.lib.json
│ └── tsconfig.spec.json
└── view-model
│ ├── .babelrc
│ ├── .eslintrc.json
│ ├── .npmignore
│ ├── LICENSE.md
│ ├── README.md
│ ├── jest.config.ts
│ ├── package.json
│ ├── project.json
│ ├── src
│ ├── index.ts
│ └── lib
│ │ └── view-model.ts
│ ├── tsconfig.json
│ ├── tsconfig.lib.json
│ └── tsconfig.spec.json
├── separation-of-tasks.png
├── the-derxjs-view-model-pattern.png
├── tools
├── scripts
│ ├── bump-version.ts
│ ├── publish-all.ts
│ └── publish.ts
├── tsconfig.tools.json
└── workspace-plugin
│ ├── .eslintrc.json
│ ├── generators.json
│ ├── jest.config.ts
│ ├── package.json
│ ├── project.json
│ ├── src
│ ├── generators
│ │ └── package
│ │ │ ├── index.ts
│ │ │ └── schema.json
│ └── index.ts
│ ├── tsconfig.json
│ ├── tsconfig.lib.json
│ └── tsconfig.spec.json
└── tsconfig.base.json
/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see http://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 | [*.md]
12 | max_line_length = off
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "ignorePatterns": ["**/*"],
4 | "plugins": ["@nx"],
5 | "overrides": [
6 | {
7 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
8 | "rules": {
9 | "@nx/enforce-module-boundaries": [
10 | "error",
11 | {
12 | "enforceBuildableLibDependency": true,
13 | "allow": [],
14 | "depConstraints": [
15 | {
16 | "sourceTag": "*",
17 | "onlyDependOnLibsWithTags": ["*"]
18 | }
19 | ]
20 | }
21 | ]
22 | }
23 | },
24 | {
25 | "files": ["*.ts", "*.tsx"],
26 | "extends": ["plugin:@nx/typescript"],
27 | "rules": {}
28 | },
29 | {
30 | "files": ["*.js", "*.jsx"],
31 | "extends": ["plugin:@nx/javascript"],
32 | "rules": {}
33 | },
34 | {
35 | "files": "*.json",
36 | "parser": "jsonc-eslint-parser",
37 | "rules": {}
38 | }
39 | ]
40 | }
41 |
--------------------------------------------------------------------------------
/.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 |
8 | # dependencies
9 | /node_modules
10 |
11 | # IDEs and editors
12 | /.idea
13 | .project
14 | .classpath
15 | .c9/
16 | *.launch
17 | .settings/
18 | *.sublime-workspace
19 |
20 | # IDE - VSCode
21 | .vscode/*
22 | !.vscode/settings.json
23 | !.vscode/tasks.json
24 | !.vscode/launch.json
25 | !.vscode/extensions.json
26 |
27 | # misc
28 | /.sass-cache
29 | /connect.lock
30 | /coverage
31 | /libpeerconnection.log
32 | npm-debug.log
33 | yarn-error.log
34 | testem.log
35 | /typings
36 |
37 | # System Files
38 | .DS_Store
39 | Thumbs.db
40 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | # Add files here to ignore them from prettier formatting
2 |
3 | /dist
4 | /coverage
5 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true
3 | }
4 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "nrwl.angular-console",
4 | "esbenp.prettier-vscode",
5 | "dbaeumer.vscode-eslint",
6 | "firsttris.vscode-jest-runner"
7 | ]
8 | }
9 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "eslint.validate": ["json"]
3 | }
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # @derxjs
2 |
3 | Because your state management code should be domain-agnostic.
4 |
5 |
6 |
7 |
8 |
9 | ## Packages
10 |
11 | | Package | Installation |
12 | | ------------------ | --------------------------- |
13 | | @derxjs/view-model | `npm i @derxjs/view-model ` |
14 | | @derxjs/reducer | `npm i @derxjs/reducer ` |
15 | | @derxjs/react | `npm i @derxjs/react ` |
16 |
17 | ## Usage
18 |
19 |
20 |
21 | Example
22 | Vanilla
23 | React
24 | Angular
25 |
26 |
27 | Simple List Component
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | Intermediate Tic Tac Toe Component
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 | ## Why @derxjs
65 |
66 | ### Domain-agnostic state-management
67 |
68 | Your state management code should not depend on which framework or tools your project happens to be using at the time.
69 |
70 | `@derxjs/view-model` is all about first-principles thinking and problem-solving. The pattern enforced by this package requires you to break down your system - regardless of scope - to some set of inputs (preferably represented as RxJS Observables!) and expose a single Observable of your ViewModel as an output.
71 |
72 | Future packages on the roadmap will provide utilities for implementing this pattern (`@derxjs/reducer` 👀), as well as utilities for plugging it into popular front-end frameworks (`@derxjs/react` 👀).
73 |
74 | ### Separation of concerns
75 |
76 | We solved this a long time ago. Programming to interfaces lets us put a joint in our workflow that allows for parallel work to be completed by multiple developers and lets your team play to their strengths.
77 |
78 |
79 |
80 |
81 |
82 | This allows for easy transitions into other implementations, frameworks, as well as implementing the facade, adapter, and proxy patterns from the Gang of Four.
83 |
84 | ### Complimentary to all existing state-management libraries
85 |
86 | We're not here to take a shot at the king ([👑](https://ngrx.io/)👀) - we're just here to help out where we can!
87 |
88 | The `@derxjs/view-model` package is designed to work with any other state management frameworks that can expose state or events as an Observable, making it a great compliment to any existing code in your codebase.
89 |
90 | ### Future-Proof Code
91 |
92 | Domain-agnostic first-principles-based code will never go out of style 🌲.
93 |
94 | As long as JavaScript is the language of the web, your state-management code will be valid.
95 |
96 | Go ahead, change to that trendy new framework. Your @derxjs code will still work just fine :).
97 |
98 | ### Simplicity && Elegence
99 |
100 | The `DeRxJSViewModel` type is the `E = mc^2` of state management.
101 |
102 | Deceptively simple, but elegant enough to encompass any && all of your state management requirements.
103 |
104 |
105 |
106 |
107 |
108 | ### TDD made awesome with timeline testing
109 |
110 | Embrace TDD, using timeline testing to test your code with a whole new dimension of precision.
111 |
112 | On the roadmap for `@derxjs` is a timeline test generation GUI tool that will take your Typescript interface code, and allow you to "draw" hypothetical timelines of events from your inputs - specifying what the output timeline for each hypothetical should look like.
113 |
114 | This tool will generate `.spec.ts` files that you can paste directly into your repos for easy TDD, and coding the way we were meant to.
115 |
116 | ## @derxjs Roadmap
117 |
118 | - @derxjs/view-model package ✅
119 | - [Article on TDD and implementing DeRxJS View Models](https://dev.to/zackderose/the-derxjsviewmodel-pattern-the-e-mc-2-of-state-management-part-1-3dka) ✅
120 | - [Article on using DeRxJS View Models in different Frameworks](https://dev.to/zackderose/the-derxjsviewmodel-pattern-the-emc2-of-state-management-part-2-2i73) ✅
121 | - @derxjs/reducer package (TBD; [beta available now](https://www.npmjs.com/package/@derxjs/reducer)) 🚧
122 | - @derxjs/react package (TBD; [beta available now](https://www.npmjs.com/package/@derxjs/react)) 🚧
123 | - Timeline Test Code Generation Tool (TBD)
124 | - @derxjs/selector package (TBD)
125 | - Ai-Driven DeRxJS Code Generation (TBD)
126 |
--------------------------------------------------------------------------------
/apps/examples/angular/list-e2e/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["plugin:cypress/recommended", "../../../../.eslintrc.json"],
3 | "ignorePatterns": ["!**/*"],
4 | "overrides": [
5 | {
6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
7 | "rules": {}
8 | }
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/apps/examples/angular/list-e2e/cypress.json:
--------------------------------------------------------------------------------
1 | {
2 | "fileServerFolder": ".",
3 | "fixturesFolder": "./src/fixtures",
4 | "integrationFolder": "./src/integration",
5 | "modifyObstructiveCode": false,
6 | "supportFile": "./src/support/index.ts",
7 | "pluginsFile": false,
8 | "video": true,
9 | "videosFolder": "../../../../dist/cypress/apps/examples/angular/list-e2e/videos",
10 | "screenshotsFolder": "../../../../dist/cypress/apps/examples/angular/list-e2e/screenshots",
11 | "chromeWebSecurity": false
12 | }
13 |
--------------------------------------------------------------------------------
/apps/examples/angular/list-e2e/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "examples-angular-list-e2e",
3 | "$schema": "../../../../node_modules/nx/schemas/project-schema.json",
4 | "sourceRoot": "apps/examples/angular/list-e2e/src",
5 | "projectType": "application",
6 | "targets": {
7 | "e2e": {
8 | "executor": "@nx/cypress:cypress",
9 | "options": {
10 | "cypressConfig": "apps/examples/angular/list-e2e/cypress.json",
11 | "devServerTarget": "examples-angular-list:serve:development"
12 | },
13 | "configurations": {
14 | "production": {
15 | "devServerTarget": "examples-angular-list:serve:production"
16 | }
17 | }
18 | },
19 | "lint": {
20 | "executor": "@nx/linter:eslint",
21 | "outputs": ["{options.outputFile}"],
22 | "options": {
23 | "lintFilePatterns": ["apps/examples/angular/list-e2e/**/*.{js,ts}"]
24 | }
25 | }
26 | },
27 | "tags": [],
28 | "implicitDependencies": ["examples-angular-list"]
29 | }
30 |
--------------------------------------------------------------------------------
/apps/examples/angular/list-e2e/src/fixtures/example.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Using fixtures to represent data",
3 | "email": "hello@cypress.io"
4 | }
5 |
--------------------------------------------------------------------------------
/apps/examples/angular/list-e2e/src/integration/app.spec.ts:
--------------------------------------------------------------------------------
1 | import { getGreeting } from '../support/app.po';
2 |
3 | describe('examples-angular-list', () => {
4 | beforeEach(() => cy.visit('/'));
5 |
6 | it('should display welcome message', () => {
7 | // Custom command example, see `../support/commands.ts` file
8 | cy.login('my-email@something.com', 'myPassword');
9 |
10 | // Function helper example, see `../support/app.po.ts` file
11 | getGreeting().contains('Welcome to examples-angular-list!');
12 | });
13 | });
14 |
--------------------------------------------------------------------------------
/apps/examples/angular/list-e2e/src/support/app.po.ts:
--------------------------------------------------------------------------------
1 | export const getGreeting = () => cy.get('h1');
2 |
--------------------------------------------------------------------------------
/apps/examples/angular/list-e2e/src/support/commands.ts:
--------------------------------------------------------------------------------
1 | // ***********************************************
2 | // This example commands.js shows you how to
3 | // create various custom commands and overwrite
4 | // existing commands.
5 | //
6 | // For more comprehensive examples of custom
7 | // commands please read more here:
8 | // https://on.cypress.io/custom-commands
9 | // ***********************************************
10 |
11 | // eslint-disable-next-line @typescript-eslint/no-namespace
12 | declare namespace Cypress {
13 | // eslint-disable-next-line @typescript-eslint/no-unused-vars
14 | interface Chainable {
15 | login(email: string, password: string): void;
16 | }
17 | }
18 | //
19 | // -- This is a parent command --
20 | Cypress.Commands.add('login', (email, password) => {
21 | console.log('Custom command example: Login', email, password);
22 | });
23 | //
24 | // -- This is a child command --
25 | // Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
26 | //
27 | //
28 | // -- This is a dual command --
29 | // Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
30 | //
31 | //
32 | // -- This will overwrite an existing command --
33 | // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
34 |
--------------------------------------------------------------------------------
/apps/examples/angular/list-e2e/src/support/index.ts:
--------------------------------------------------------------------------------
1 | // ***********************************************************
2 | // This example support/index.js is processed and
3 | // loaded automatically before your test files.
4 | //
5 | // This is a great place to put global configuration and
6 | // behavior that modifies Cypress.
7 | //
8 | // You can change the location of this file or turn off
9 | // automatically serving support files with the
10 | // 'supportFile' configuration option.
11 | //
12 | // You can read more here:
13 | // https://on.cypress.io/configuration
14 | // ***********************************************************
15 |
16 | // Import commands.js using ES2015 syntax:
17 | import './commands';
18 |
--------------------------------------------------------------------------------
/apps/examples/angular/list-e2e/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "sourceMap": false,
5 | "outDir": "../../../../dist/out-tsc",
6 | "allowJs": true,
7 | "types": ["cypress", "node"],
8 | "forceConsistentCasingInFileNames": true,
9 | "strict": true,
10 | "noImplicitReturns": true,
11 | "noFallthroughCasesInSwitch": true
12 | },
13 | "include": ["src/**/*.ts", "src/**/*.js"],
14 | "angularCompilerOptions": {
15 | "strictInjectionParameters": true,
16 | "strictInputAccessModifiers": true,
17 | "strictTemplates": true
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/apps/examples/angular/list/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["../../../../.eslintrc.json"],
3 | "ignorePatterns": ["!**/*"],
4 | "overrides": [
5 | {
6 | "files": ["*.ts"],
7 | "extends": [
8 | "plugin:@nx/angular",
9 | "plugin:@angular-eslint/template/process-inline-templates"
10 | ],
11 | "rules": {
12 | "@angular-eslint/directive-selector": [
13 | "error",
14 | {
15 | "type": "attribute",
16 | "prefix": "derxjs",
17 | "style": "camelCase"
18 | }
19 | ],
20 | "@angular-eslint/component-selector": [
21 | "error",
22 | {
23 | "type": "element",
24 | "prefix": "derxjs",
25 | "style": "kebab-case"
26 | }
27 | ]
28 | }
29 | },
30 | {
31 | "files": ["*.html"],
32 | "extends": ["plugin:@nx/angular-template"],
33 | "rules": {}
34 | }
35 | ]
36 | }
37 |
--------------------------------------------------------------------------------
/apps/examples/angular/list/jest.config.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | export default {
3 | displayName: 'examples-angular-list',
4 | preset: '../../../../jest.preset.js',
5 | setupFilesAfterEnv: ['/src/test-setup.ts'],
6 | globals: {},
7 | coverageDirectory: '../../../../coverage/apps/examples/angular/list',
8 | transform: {
9 | '^.+.(ts|mjs|js|html)$': [
10 | 'jest-preset-angular',
11 | {
12 | tsconfig: '/tsconfig.spec.json',
13 | stringifyContentPathRegex: '\\.(html|svg)$',
14 | },
15 | ],
16 | },
17 | transformIgnorePatterns: ['node_modules/(?!.*.mjs$)'],
18 | snapshotSerializers: [
19 | 'jest-preset-angular/build/serializers/no-ng-attributes',
20 | 'jest-preset-angular/build/serializers/ng-snapshot',
21 | 'jest-preset-angular/build/serializers/html-comment',
22 | ],
23 | };
24 |
--------------------------------------------------------------------------------
/apps/examples/angular/list/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "examples-angular-list",
3 | "$schema": "../../../../node_modules/nx/schemas/project-schema.json",
4 | "projectType": "application",
5 | "sourceRoot": "apps/examples/angular/list/src",
6 | "prefix": "derxjs",
7 | "targets": {
8 | "build": {
9 | "executor": "@angular-devkit/build-angular:browser",
10 | "outputs": ["{options.outputPath}"],
11 | "options": {
12 | "outputPath": "dist/apps/examples/angular/list",
13 | "index": "apps/examples/angular/list/src/index.html",
14 | "main": "apps/examples/angular/list/src/main.ts",
15 | "polyfills": "apps/examples/angular/list/src/polyfills.ts",
16 | "tsConfig": "apps/examples/angular/list/tsconfig.app.json",
17 | "inlineStyleLanguage": "scss",
18 | "assets": [
19 | "apps/examples/angular/list/src/favicon.ico",
20 | "apps/examples/angular/list/src/assets"
21 | ],
22 | "styles": ["apps/examples/angular/list/src/styles.scss"],
23 | "scripts": []
24 | },
25 | "configurations": {
26 | "production": {
27 | "budgets": [
28 | {
29 | "type": "initial",
30 | "maximumWarning": "500kb",
31 | "maximumError": "1mb"
32 | },
33 | {
34 | "type": "anyComponentStyle",
35 | "maximumWarning": "2kb",
36 | "maximumError": "4kb"
37 | }
38 | ],
39 | "fileReplacements": [
40 | {
41 | "replace": "apps/examples/angular/list/src/environments/environment.ts",
42 | "with": "apps/examples/angular/list/src/environments/environment.prod.ts"
43 | }
44 | ],
45 | "outputHashing": "all"
46 | },
47 | "development": {
48 | "buildOptimizer": false,
49 | "optimization": false,
50 | "vendorChunk": true,
51 | "extractLicenses": false,
52 | "sourceMap": true,
53 | "namedChunks": true
54 | }
55 | },
56 | "defaultConfiguration": "production"
57 | },
58 | "serve": {
59 | "executor": "@angular-devkit/build-angular:dev-server",
60 | "configurations": {
61 | "production": {
62 | "browserTarget": "examples-angular-list:build:production"
63 | },
64 | "development": {
65 | "browserTarget": "examples-angular-list:build:development"
66 | }
67 | },
68 | "defaultConfiguration": "development"
69 | },
70 | "extract-i18n": {
71 | "executor": "@angular-devkit/build-angular:extract-i18n",
72 | "options": {
73 | "browserTarget": "examples-angular-list:build"
74 | }
75 | },
76 | "lint": {
77 | "executor": "@nx/linter:eslint",
78 | "options": {
79 | "lintFilePatterns": [
80 | "apps/examples/angular/list/src/**/*.ts",
81 | "apps/examples/angular/list/src/**/*.html"
82 | ]
83 | }
84 | },
85 | "test": {
86 | "executor": "@nx/jest:jest",
87 | "outputs": ["{workspaceRoot}/coverage/apps/examples/angular/list"],
88 | "options": {
89 | "jestConfig": "apps/examples/angular/list/jest.config.ts",
90 | "passWithNoTests": true
91 | }
92 | }
93 | },
94 | "tags": []
95 | }
96 |
--------------------------------------------------------------------------------
/apps/examples/angular/list/src/app/app.component.html:
--------------------------------------------------------------------------------
1 |
2 | @derxj/view-model Angular Usage Example
3 |
4 |
8 |
9 |
12 |
13 | Pop
14 |
15 |
--------------------------------------------------------------------------------
/apps/examples/angular/list/src/app/app.component.scss:
--------------------------------------------------------------------------------
1 | /*
2 | * Remove template code below
3 | */
4 | :host {
5 | display: block;
6 | font-family: sans-serif;
7 | min-width: 300px;
8 | max-width: 600px;
9 | margin: 50px auto;
10 | }
11 |
12 | .gutter-left {
13 | margin-left: 9px;
14 | }
15 |
16 | .col-span-2 {
17 | grid-column: span 2;
18 | }
19 |
20 | .flex {
21 | display: flex;
22 | align-items: center;
23 | justify-content: center;
24 | }
25 |
26 | header {
27 | background-color: #143055;
28 | color: white;
29 | padding: 5px;
30 | border-radius: 3px;
31 | }
32 |
33 | main {
34 | padding: 0 36px;
35 | }
36 |
37 | p {
38 | text-align: center;
39 | }
40 |
41 | h1 {
42 | text-align: center;
43 | margin-left: 18px;
44 | font-size: 24px;
45 | }
46 |
47 | h2 {
48 | text-align: center;
49 | font-size: 20px;
50 | margin: 40px 0 10px 0;
51 | }
52 |
53 | .resources {
54 | text-align: center;
55 | list-style: none;
56 | padding: 0;
57 | display: grid;
58 | grid-gap: 9px;
59 | grid-template-columns: 1fr 1fr;
60 | }
61 |
62 | .resource {
63 | color: #0094ba;
64 | height: 36px;
65 | background-color: rgba(0, 0, 0, 0);
66 | border: 1px solid rgba(0, 0, 0, 0.12);
67 | border-radius: 4px;
68 | padding: 3px 9px;
69 | text-decoration: none;
70 | }
71 |
72 | .resource:hover {
73 | background-color: rgba(68, 138, 255, 0.04);
74 | }
75 |
76 | pre {
77 | padding: 9px;
78 | border-radius: 4px;
79 | background-color: black;
80 | color: #eee;
81 | }
82 |
83 | details {
84 | border-radius: 4px;
85 | color: #333;
86 | background-color: rgba(0, 0, 0, 0);
87 | border: 1px solid rgba(0, 0, 0, 0.12);
88 | padding: 3px 9px;
89 | margin-bottom: 9px;
90 | }
91 |
92 | summary {
93 | cursor: pointer;
94 | outline: none;
95 | height: 36px;
96 | line-height: 36px;
97 | }
98 |
99 | .github-star-container {
100 | margin-top: 12px;
101 | line-height: 20px;
102 | }
103 |
104 | .github-star-container a {
105 | display: flex;
106 | align-items: center;
107 | text-decoration: none;
108 | color: #333;
109 | }
110 |
111 | .github-star-badge {
112 | color: #24292e;
113 | display: flex;
114 | align-items: center;
115 | font-size: 12px;
116 | padding: 3px 10px;
117 | border: 1px solid rgba(27, 31, 35, 0.2);
118 | border-radius: 3px;
119 | background-image: linear-gradient(-180deg, #fafbfc, #eff3f6 90%);
120 | margin-left: 4px;
121 | font-weight: 600;
122 | }
123 |
124 | .github-star-badge:hover {
125 | background-image: linear-gradient(-180deg, #f0f3f6, #e6ebf1 90%);
126 | border-color: rgba(27, 31, 35, 0.35);
127 | background-position: -0.5em;
128 | }
129 | .github-star-badge .material-icons {
130 | height: 16px;
131 | width: 16px;
132 | margin-right: 4px;
133 | }
134 |
--------------------------------------------------------------------------------
/apps/examples/angular/list/src/app/app.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed } from '@angular/core/testing';
2 | import { AppComponent } from './app.component';
3 |
4 | describe('AppComponent', () => {
5 | beforeEach(async () => {
6 | await TestBed.configureTestingModule({
7 | declarations: [AppComponent],
8 | }).compileComponents();
9 | });
10 |
11 | it('should create the app', () => {
12 | const fixture = TestBed.createComponent(AppComponent);
13 | const app = fixture.componentInstance;
14 | expect(app).toBeTruthy();
15 | });
16 | });
17 |
--------------------------------------------------------------------------------
/apps/examples/angular/list/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | import { UntypedFormControl } from '@angular/forms';
3 | import {
4 | customListImpl$ as listViewModel$,
5 | // reducerListImpl$ as listViewModel$,
6 | ListViewModel,
7 | } from '@derxjs/examples/list-view-model-implementation';
8 | import { Observable, Observer } from 'rxjs';
9 |
10 | @Component({
11 | selector: 'derxjs-root',
12 | templateUrl: './app.component.html',
13 | styleUrls: ['./app.component.scss'],
14 | })
15 | export class AppComponent {
16 | textInputFormControl = new UntypedFormControl();
17 | private _pushObserver!: Observer;
18 | private _popObserver!: Observer;
19 | viewModel$: Observable;
20 |
21 | constructor() {
22 | const push$ = new Observable(
23 | (observer) => (this._pushObserver = observer)
24 | );
25 | const pop$ = new Observable(
26 | (observer) => (this._popObserver = observer)
27 | );
28 | this.viewModel$ = listViewModel$({ push$, pop$, initialValue: [] }) as any;
29 | }
30 |
31 | push() {
32 | this._pushObserver.next(this.textInputFormControl.value);
33 | this.textInputFormControl.reset();
34 | }
35 |
36 | pop() {
37 | this._popObserver.next();
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/apps/examples/angular/list/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { FormsModule, ReactiveFormsModule } from '@angular/forms';
3 | import { BrowserModule } from '@angular/platform-browser';
4 |
5 | import { AppComponent } from './app.component';
6 |
7 | @NgModule({
8 | declarations: [AppComponent],
9 | imports: [BrowserModule, FormsModule, ReactiveFormsModule],
10 | providers: [],
11 | bootstrap: [AppComponent],
12 | })
13 | export class AppModule {}
14 |
--------------------------------------------------------------------------------
/apps/examples/angular/list/src/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZackDeRose/derxjs/96e2497bf36e4bb110c9421b0f770361155df6d0/apps/examples/angular/list/src/assets/.gitkeep
--------------------------------------------------------------------------------
/apps/examples/angular/list/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true,
3 | };
4 |
--------------------------------------------------------------------------------
/apps/examples/angular/list/src/environments/environment.ts:
--------------------------------------------------------------------------------
1 | // This file can be replaced during build by using the `fileReplacements` array.
2 | // `ng build` replaces `environment.ts` with `environment.prod.ts`.
3 | // The list of file replacements can be found in `angular.json`.
4 |
5 | export const environment = {
6 | production: false,
7 | };
8 |
9 | /*
10 | * For easier debugging in development mode, you can import the following file
11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
12 | *
13 | * This import should be commented out in production mode because it will have a negative impact
14 | * on performance if an error is thrown.
15 | */
16 | // import 'zone.js/plugins/zone-error'; // Included with Angular CLI.
17 |
--------------------------------------------------------------------------------
/apps/examples/angular/list/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZackDeRose/derxjs/96e2497bf36e4bb110c9421b0f770361155df6d0/apps/examples/angular/list/src/favicon.ico
--------------------------------------------------------------------------------
/apps/examples/angular/list/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ExamplesAngularList
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/apps/examples/angular/list/src/main.ts:
--------------------------------------------------------------------------------
1 | import { enableProdMode } from '@angular/core';
2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
3 |
4 | import { AppModule } from './app/app.module';
5 | import { environment } from './environments/environment';
6 |
7 | if (environment.production) {
8 | enableProdMode();
9 | }
10 |
11 | platformBrowserDynamic()
12 | .bootstrapModule(AppModule)
13 | .catch((err) => console.error(err));
14 |
--------------------------------------------------------------------------------
/apps/examples/angular/list/src/polyfills.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This file includes polyfills needed by Angular and is loaded before the app.
3 | * You can add your own extra polyfills to this file.
4 | *
5 | * This file is divided into 2 sections:
6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
8 | * file.
9 | *
10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
13 | *
14 | * Learn more in https://angular.io/guide/browser-support
15 | */
16 |
17 | /***************************************************************************************************
18 | * BROWSER POLYFILLS
19 | */
20 |
21 | /**
22 | * IE11 requires the following for NgClass support on SVG elements
23 | */
24 | // import 'classlist.js'; // Run `npm install --save classlist.js`.
25 |
26 | /**
27 | * Web Animations `@angular/platform-browser/animations`
28 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
29 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
30 | */
31 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`.
32 |
33 | /**
34 | * By default, zone.js will patch all possible macroTask and DomEvents
35 | * user can disable parts of macroTask/DomEvents patch by setting following flags
36 | * because those flags need to be set before `zone.js` being loaded, and webpack
37 | * will put import in the top of bundle, so user need to create a separate file
38 | * in this directory (for example: zone-flags.ts), and put the following flags
39 | * into that file, and then add the following code before importing zone.js.
40 | * import './zone-flags';
41 | *
42 | * The flags allowed in zone-flags.ts are listed here.
43 | *
44 | * The following flags will work for all browsers.
45 | *
46 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
47 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
48 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
49 | *
50 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
51 | * with the following flag, it will bypass `zone.js` patch for IE/Edge
52 | *
53 | * (window as any).__Zone_enable_cross_context_check = true;
54 | *
55 | */
56 |
57 | /***************************************************************************************************
58 | * Zone JS is required by default for Angular itself.
59 | */
60 | import 'zone.js'; // Included with Angular CLI.
61 |
62 | /***************************************************************************************************
63 | * APPLICATION IMPORTS
64 | */
65 |
--------------------------------------------------------------------------------
/apps/examples/angular/list/src/styles.scss:
--------------------------------------------------------------------------------
1 | /* You can add global styles to this file, and also import other style files */
2 |
--------------------------------------------------------------------------------
/apps/examples/angular/list/src/test-setup.ts:
--------------------------------------------------------------------------------
1 | import 'jest-preset-angular/setup-jest';
2 |
3 | import { getTestBed } from '@angular/core/testing';
4 | import {
5 | BrowserDynamicTestingModule,
6 | platformBrowserDynamicTesting,
7 | } from '@angular/platform-browser-dynamic/testing';
8 |
9 | getTestBed().resetTestEnvironment();
10 | getTestBed().initTestEnvironment(
11 | BrowserDynamicTestingModule,
12 | platformBrowserDynamicTesting(),
13 | { teardown: { destroyAfterEach: false } }
14 | );
15 |
--------------------------------------------------------------------------------
/apps/examples/angular/list/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../../../dist/out-tsc",
5 | "types": [],
6 | "target": "ES2022",
7 | "useDefineForClassFields": false
8 | },
9 | "files": ["src/main.ts", "src/polyfills.ts"],
10 | "include": ["src/**/*.d.ts"],
11 | "exclude": ["jest.config.ts"]
12 | }
13 |
--------------------------------------------------------------------------------
/apps/examples/angular/list/tsconfig.editor.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "include": ["**/*.ts"],
4 | "compilerOptions": {
5 | "types": ["jest", "node"]
6 | },
7 | "exclude": ["jest.config.ts"]
8 | }
9 |
--------------------------------------------------------------------------------
/apps/examples/angular/list/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../../../tsconfig.base.json",
3 | "files": [],
4 | "include": [],
5 | "references": [
6 | {
7 | "path": "./tsconfig.app.json"
8 | },
9 | {
10 | "path": "./tsconfig.spec.json"
11 | },
12 | {
13 | "path": "./tsconfig.editor.json"
14 | }
15 | ],
16 | "compilerOptions": {
17 | "forceConsistentCasingInFileNames": true,
18 | "strict": true,
19 | "noImplicitReturns": true,
20 | "noFallthroughCasesInSwitch": true,
21 | "target": "es2020"
22 | },
23 | "angularCompilerOptions": {
24 | "strictInjectionParameters": true,
25 | "strictInputAccessModifiers": true,
26 | "strictTemplates": true
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/apps/examples/angular/list/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../../../dist/out-tsc",
5 | "module": "commonjs",
6 | "types": ["jest", "node"]
7 | },
8 | "files": ["src/test-setup.ts"],
9 | "include": ["**/*.spec.ts", "**/*.test.ts", "**/*.d.ts", "jest.config.ts"]
10 | }
11 |
--------------------------------------------------------------------------------
/apps/examples/angular/tic-tac-toe-e2e/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["plugin:cypress/recommended", "../../../../.eslintrc.json"],
3 | "ignorePatterns": ["!**/*"],
4 | "overrides": [
5 | {
6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
7 | "rules": {}
8 | }
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/apps/examples/angular/tic-tac-toe-e2e/cypress.json:
--------------------------------------------------------------------------------
1 | {
2 | "fileServerFolder": ".",
3 | "fixturesFolder": "./src/fixtures",
4 | "integrationFolder": "./src/integration",
5 | "modifyObstructiveCode": false,
6 | "supportFile": "./src/support/index.ts",
7 | "pluginsFile": false,
8 | "video": true,
9 | "videosFolder": "../../../../dist/cypress/apps/examples/angular/tic-tac-toe-e2e/videos",
10 | "screenshotsFolder": "../../../../dist/cypress/apps/examples/angular/tic-tac-toe-e2e/screenshots",
11 | "chromeWebSecurity": false
12 | }
13 |
--------------------------------------------------------------------------------
/apps/examples/angular/tic-tac-toe-e2e/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "examples-angular-tic-tac-toe-e2e",
3 | "$schema": "../../../../node_modules/nx/schemas/project-schema.json",
4 | "sourceRoot": "apps/examples/angular/tic-tac-toe-e2e/src",
5 | "projectType": "application",
6 | "targets": {
7 | "e2e": {
8 | "executor": "@nx/cypress:cypress",
9 | "options": {
10 | "cypressConfig": "apps/examples/angular/tic-tac-toe-e2e/cypress.json",
11 | "devServerTarget": "examples-angular-tic-tac-toe:serve:development"
12 | },
13 | "configurations": {
14 | "production": {
15 | "devServerTarget": "examples-angular-tic-tac-toe:serve:production"
16 | }
17 | }
18 | },
19 | "lint": {
20 | "executor": "@nx/linter:eslint",
21 | "outputs": ["{options.outputFile}"],
22 | "options": {
23 | "lintFilePatterns": [
24 | "apps/examples/angular/tic-tac-toe-e2e/**/*.{js,ts}"
25 | ]
26 | }
27 | }
28 | },
29 | "tags": [],
30 | "implicitDependencies": ["examples-angular-tic-tac-toe"]
31 | }
32 |
--------------------------------------------------------------------------------
/apps/examples/angular/tic-tac-toe-e2e/src/fixtures/example.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Using fixtures to represent data",
3 | "email": "hello@cypress.io"
4 | }
5 |
--------------------------------------------------------------------------------
/apps/examples/angular/tic-tac-toe-e2e/src/integration/app.spec.ts:
--------------------------------------------------------------------------------
1 | import { getGreeting } from '../support/app.po';
2 |
3 | describe('examples-angular-tic-tac-toe', () => {
4 | beforeEach(() => cy.visit('/'));
5 |
6 | it('should display welcome message', () => {
7 | // Custom command example, see `../support/commands.ts` file
8 | cy.login('my-email@something.com', 'myPassword');
9 |
10 | // Function helper example, see `../support/app.po.ts` file
11 | getGreeting().contains('Welcome to examples-angular-tic-tac-toe!');
12 | });
13 | });
14 |
--------------------------------------------------------------------------------
/apps/examples/angular/tic-tac-toe-e2e/src/support/app.po.ts:
--------------------------------------------------------------------------------
1 | export const getGreeting = () => cy.get('h1');
2 |
--------------------------------------------------------------------------------
/apps/examples/angular/tic-tac-toe-e2e/src/support/commands.ts:
--------------------------------------------------------------------------------
1 | // ***********************************************
2 | // This example commands.js shows you how to
3 | // create various custom commands and overwrite
4 | // existing commands.
5 | //
6 | // For more comprehensive examples of custom
7 | // commands please read more here:
8 | // https://on.cypress.io/custom-commands
9 | // ***********************************************
10 |
11 | // eslint-disable-next-line @typescript-eslint/no-namespace
12 | declare namespace Cypress {
13 | // eslint-disable-next-line @typescript-eslint/no-unused-vars
14 | interface Chainable {
15 | login(email: string, password: string): void;
16 | }
17 | }
18 | //
19 | // -- This is a parent command --
20 | Cypress.Commands.add('login', (email, password) => {
21 | console.log('Custom command example: Login', email, password);
22 | });
23 | //
24 | // -- This is a child command --
25 | // Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
26 | //
27 | //
28 | // -- This is a dual command --
29 | // Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
30 | //
31 | //
32 | // -- This will overwrite an existing command --
33 | // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
34 |
--------------------------------------------------------------------------------
/apps/examples/angular/tic-tac-toe-e2e/src/support/index.ts:
--------------------------------------------------------------------------------
1 | // ***********************************************************
2 | // This example support/index.js is processed and
3 | // loaded automatically before your test files.
4 | //
5 | // This is a great place to put global configuration and
6 | // behavior that modifies Cypress.
7 | //
8 | // You can change the location of this file or turn off
9 | // automatically serving support files with the
10 | // 'supportFile' configuration option.
11 | //
12 | // You can read more here:
13 | // https://on.cypress.io/configuration
14 | // ***********************************************************
15 |
16 | // Import commands.js using ES2015 syntax:
17 | import './commands';
18 |
--------------------------------------------------------------------------------
/apps/examples/angular/tic-tac-toe-e2e/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "sourceMap": false,
5 | "outDir": "../../../../dist/out-tsc",
6 | "allowJs": true,
7 | "types": ["cypress", "node"],
8 | "forceConsistentCasingInFileNames": true,
9 | "strict": true,
10 | "noImplicitReturns": true,
11 | "noFallthroughCasesInSwitch": true
12 | },
13 | "include": ["src/**/*.ts", "src/**/*.js"],
14 | "angularCompilerOptions": {
15 | "strictInjectionParameters": true,
16 | "strictInputAccessModifiers": true,
17 | "strictTemplates": true
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/apps/examples/angular/tic-tac-toe/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["../../../../.eslintrc.json"],
3 | "ignorePatterns": ["!**/*"],
4 | "overrides": [
5 | {
6 | "files": ["*.ts"],
7 | "extends": [
8 | "plugin:@nx/angular",
9 | "plugin:@angular-eslint/template/process-inline-templates"
10 | ],
11 | "rules": {
12 | "@angular-eslint/directive-selector": [
13 | "error",
14 | {
15 | "type": "attribute",
16 | "prefix": "derxjs",
17 | "style": "camelCase"
18 | }
19 | ],
20 | "@angular-eslint/component-selector": [
21 | "error",
22 | {
23 | "type": "element",
24 | "prefix": "derxjs",
25 | "style": "kebab-case"
26 | }
27 | ]
28 | }
29 | },
30 | {
31 | "files": ["*.html"],
32 | "extends": ["plugin:@nx/angular-template"],
33 | "rules": {}
34 | }
35 | ]
36 | }
37 |
--------------------------------------------------------------------------------
/apps/examples/angular/tic-tac-toe/jest.config.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | export default {
3 | displayName: 'examples-angular-tic-tac-toe',
4 | preset: '../../../../jest.preset.js',
5 | setupFilesAfterEnv: ['/src/test-setup.ts'],
6 | globals: {},
7 | coverageDirectory: '../../../../coverage/apps/examples/angular/tic-tac-toe',
8 | transform: {
9 | '^.+.(ts|mjs|js|html)$': [
10 | 'jest-preset-angular',
11 | {
12 | tsconfig: '/tsconfig.spec.json',
13 | stringifyContentPathRegex: '\\.(html|svg)$',
14 | },
15 | ],
16 | },
17 | transformIgnorePatterns: ['node_modules/(?!.*.mjs$)'],
18 | snapshotSerializers: [
19 | 'jest-preset-angular/build/serializers/no-ng-attributes',
20 | 'jest-preset-angular/build/serializers/ng-snapshot',
21 | 'jest-preset-angular/build/serializers/html-comment',
22 | ],
23 | };
24 |
--------------------------------------------------------------------------------
/apps/examples/angular/tic-tac-toe/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "examples-angular-tic-tac-toe",
3 | "$schema": "../../../../node_modules/nx/schemas/project-schema.json",
4 | "projectType": "application",
5 | "sourceRoot": "apps/examples/angular/tic-tac-toe/src",
6 | "prefix": "derxjs",
7 | "targets": {
8 | "build": {
9 | "executor": "@angular-devkit/build-angular:browser",
10 | "outputs": ["{options.outputPath}"],
11 | "options": {
12 | "outputPath": "dist/apps/examples/angular/tic-tac-toe",
13 | "index": "apps/examples/angular/tic-tac-toe/src/index.html",
14 | "main": "apps/examples/angular/tic-tac-toe/src/main.ts",
15 | "polyfills": "apps/examples/angular/tic-tac-toe/src/polyfills.ts",
16 | "tsConfig": "apps/examples/angular/tic-tac-toe/tsconfig.app.json",
17 | "inlineStyleLanguage": "scss",
18 | "assets": [
19 | "apps/examples/angular/tic-tac-toe/src/favicon.ico",
20 | "apps/examples/angular/tic-tac-toe/src/assets"
21 | ],
22 | "styles": ["apps/examples/angular/tic-tac-toe/src/styles.scss"],
23 | "scripts": []
24 | },
25 | "configurations": {
26 | "production": {
27 | "budgets": [
28 | {
29 | "type": "initial",
30 | "maximumWarning": "500kb",
31 | "maximumError": "1mb"
32 | },
33 | {
34 | "type": "anyComponentStyle",
35 | "maximumWarning": "2kb",
36 | "maximumError": "4kb"
37 | }
38 | ],
39 | "fileReplacements": [
40 | {
41 | "replace": "apps/examples/angular/tic-tac-toe/src/environments/environment.ts",
42 | "with": "apps/examples/angular/tic-tac-toe/src/environments/environment.prod.ts"
43 | }
44 | ],
45 | "outputHashing": "all"
46 | },
47 | "development": {
48 | "buildOptimizer": false,
49 | "optimization": false,
50 | "vendorChunk": true,
51 | "extractLicenses": false,
52 | "sourceMap": true,
53 | "namedChunks": true
54 | }
55 | },
56 | "defaultConfiguration": "production"
57 | },
58 | "serve": {
59 | "executor": "@angular-devkit/build-angular:dev-server",
60 | "configurations": {
61 | "production": {
62 | "browserTarget": "examples-angular-tic-tac-toe:build:production"
63 | },
64 | "development": {
65 | "browserTarget": "examples-angular-tic-tac-toe:build:development"
66 | }
67 | },
68 | "defaultConfiguration": "development"
69 | },
70 | "extract-i18n": {
71 | "executor": "@angular-devkit/build-angular:extract-i18n",
72 | "options": {
73 | "browserTarget": "examples-angular-tic-tac-toe:build"
74 | }
75 | },
76 | "lint": {
77 | "executor": "@nx/linter:eslint",
78 | "options": {
79 | "lintFilePatterns": [
80 | "apps/examples/angular/tic-tac-toe/src/**/*.ts",
81 | "apps/examples/angular/tic-tac-toe/src/**/*.html"
82 | ]
83 | }
84 | },
85 | "test": {
86 | "executor": "@nx/jest:jest",
87 | "outputs": ["{workspaceRoot}/coverage/apps/examples/angular/tic-tac-toe"],
88 | "options": {
89 | "jestConfig": "apps/examples/angular/tic-tac-toe/jest.config.ts",
90 | "passWithNoTests": true
91 | }
92 | }
93 | },
94 | "tags": []
95 | }
96 |
--------------------------------------------------------------------------------
/apps/examples/angular/tic-tac-toe/src/app/app.component.html:
--------------------------------------------------------------------------------
1 | Tic Tac Toe
2 |
3 | {{ vm.turn }}
4 |
5 |
6 |
7 |
8 |
9 | {{ vm.board[row][column] | uppercase }}
10 |
11 |
12 |
13 |
14 |
15 | Reset
16 |
17 |
--------------------------------------------------------------------------------
/apps/examples/angular/tic-tac-toe/src/app/app.component.scss:
--------------------------------------------------------------------------------
1 | /*
2 | * Remove template code below
3 | */
4 | :host {
5 | display: grid;
6 | font-family: sans-serif;
7 | min-width: 300px;
8 | max-width: 600px;
9 | margin: 50px auto;
10 | align-content: center;
11 | justify-items: center;
12 | }
13 |
14 | .border {
15 | background-color: black;
16 | width: 200px;
17 | text-align: center;
18 | }
19 |
20 | .board {
21 | display: grid;
22 | grid-template-columns: fit-content(40px) fit-content(40px) fit-content(40px);
23 | grid-template-rows: fit-content(40px) fit-content(40px) fit-content(40px);
24 | grid-column-gap: 10px;
25 | grid-row-gap: 10px;
26 | & > div {
27 | background-color: white;
28 | padding: 10px;
29 | }
30 | margin: 0;
31 | button {
32 | text-align: center;
33 | height: 40px;
34 | width: 40px;
35 | }
36 | }
37 |
38 | .reset {
39 | margin-top: 40px;
40 | }
41 |
--------------------------------------------------------------------------------
/apps/examples/angular/tic-tac-toe/src/app/app.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed } from '@angular/core/testing';
2 | import { AppComponent } from './app.component';
3 |
4 | describe('AppComponent', () => {
5 | beforeEach(async () => {
6 | await TestBed.configureTestingModule({
7 | declarations: [AppComponent],
8 | }).compileComponents();
9 | });
10 |
11 | it('should create the app', () => {
12 | const fixture = TestBed.createComponent(AppComponent);
13 | const app = fixture.componentInstance;
14 | expect(app).toBeTruthy();
15 | });
16 |
17 | it('should render title', () => {
18 | const fixture = TestBed.createComponent(AppComponent);
19 | fixture.detectChanges();
20 | const compiled = fixture.nativeElement as HTMLElement;
21 | expect(compiled.querySelector('h1')?.textContent).toContain('Tic Tac Toe');
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/apps/examples/angular/tic-tac-toe/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import { ChangeDetectionStrategy, Component } from '@angular/core';
2 | import {
3 | ticTacToeViewModel$,
4 | randomAi,
5 | SpaceCoordinates,
6 | BoardIndex,
7 | } from '@derxjs/examples/tic-tac-toe-view-model-implementation';
8 | import { Observable, Observer } from 'rxjs';
9 |
10 | @Component({
11 | selector: 'derxjs-root',
12 | templateUrl: './app.component.html',
13 | styleUrls: ['./app.component.scss'],
14 | changeDetection: ChangeDetectionStrategy.OnPush,
15 | })
16 | export class AppComponent {
17 | userResetClickObserver!: Observer;
18 | userResetClickEvents$ = new Observable(
19 | (observer) => (this.userResetClickObserver = observer)
20 | );
21 | userSpaceClickObserver!: Observer;
22 | userSpaceClickEvents$ = new Observable(
23 | (observer) => (this.userSpaceClickObserver = observer)
24 | );
25 | vm$ = ticTacToeViewModel$({
26 | ai: randomAi,
27 | userSpaceClickEvents$: this.userSpaceClickEvents$,
28 | userResetClickEvents$: this.userResetClickEvents$,
29 | });
30 | rows: BoardIndex[] = [0, 1, 2];
31 |
32 | handleSpaceClick(coordinates: SpaceCoordinates) {
33 | this.userSpaceClickObserver.next(coordinates);
34 | }
35 |
36 | handleResetClick() {
37 | this.userResetClickObserver.next();
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/apps/examples/angular/tic-tac-toe/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { BrowserModule } from '@angular/platform-browser';
3 |
4 | import { AppComponent } from './app.component';
5 |
6 | @NgModule({
7 | declarations: [AppComponent],
8 | imports: [BrowserModule],
9 | providers: [],
10 | bootstrap: [AppComponent],
11 | })
12 | export class AppModule {}
13 |
--------------------------------------------------------------------------------
/apps/examples/angular/tic-tac-toe/src/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZackDeRose/derxjs/96e2497bf36e4bb110c9421b0f770361155df6d0/apps/examples/angular/tic-tac-toe/src/assets/.gitkeep
--------------------------------------------------------------------------------
/apps/examples/angular/tic-tac-toe/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true,
3 | };
4 |
--------------------------------------------------------------------------------
/apps/examples/angular/tic-tac-toe/src/environments/environment.ts:
--------------------------------------------------------------------------------
1 | // This file can be replaced during build by using the `fileReplacements` array.
2 | // `ng build` replaces `environment.ts` with `environment.prod.ts`.
3 | // The list of file replacements can be found in `angular.json`.
4 |
5 | export const environment = {
6 | production: false,
7 | };
8 |
9 | /*
10 | * For easier debugging in development mode, you can import the following file
11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
12 | *
13 | * This import should be commented out in production mode because it will have a negative impact
14 | * on performance if an error is thrown.
15 | */
16 | // import 'zone.js/plugins/zone-error'; // Included with Angular CLI.
17 |
--------------------------------------------------------------------------------
/apps/examples/angular/tic-tac-toe/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZackDeRose/derxjs/96e2497bf36e4bb110c9421b0f770361155df6d0/apps/examples/angular/tic-tac-toe/src/favicon.ico
--------------------------------------------------------------------------------
/apps/examples/angular/tic-tac-toe/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ExamplesAngularTicTacToe
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/apps/examples/angular/tic-tac-toe/src/main.ts:
--------------------------------------------------------------------------------
1 | import { enableProdMode } from '@angular/core';
2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
3 |
4 | import { AppModule } from './app/app.module';
5 | import { environment } from './environments/environment';
6 |
7 | if (environment.production) {
8 | enableProdMode();
9 | }
10 |
11 | platformBrowserDynamic()
12 | .bootstrapModule(AppModule)
13 | .catch((err) => console.error(err));
14 |
--------------------------------------------------------------------------------
/apps/examples/angular/tic-tac-toe/src/polyfills.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This file includes polyfills needed by Angular and is loaded before the app.
3 | * You can add your own extra polyfills to this file.
4 | *
5 | * This file is divided into 2 sections:
6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
8 | * file.
9 | *
10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
13 | *
14 | * Learn more in https://angular.io/guide/browser-support
15 | */
16 |
17 | /***************************************************************************************************
18 | * BROWSER POLYFILLS
19 | */
20 |
21 | /**
22 | * IE11 requires the following for NgClass support on SVG elements
23 | */
24 | // import 'classlist.js'; // Run `npm install --save classlist.js`.
25 |
26 | /**
27 | * Web Animations `@angular/platform-browser/animations`
28 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
29 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
30 | */
31 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`.
32 |
33 | /**
34 | * By default, zone.js will patch all possible macroTask and DomEvents
35 | * user can disable parts of macroTask/DomEvents patch by setting following flags
36 | * because those flags need to be set before `zone.js` being loaded, and webpack
37 | * will put import in the top of bundle, so user need to create a separate file
38 | * in this directory (for example: zone-flags.ts), and put the following flags
39 | * into that file, and then add the following code before importing zone.js.
40 | * import './zone-flags';
41 | *
42 | * The flags allowed in zone-flags.ts are listed here.
43 | *
44 | * The following flags will work for all browsers.
45 | *
46 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
47 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
48 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
49 | *
50 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
51 | * with the following flag, it will bypass `zone.js` patch for IE/Edge
52 | *
53 | * (window as any).__Zone_enable_cross_context_check = true;
54 | *
55 | */
56 |
57 | /***************************************************************************************************
58 | * Zone JS is required by default for Angular itself.
59 | */
60 | import 'zone.js'; // Included with Angular CLI.
61 |
62 | /***************************************************************************************************
63 | * APPLICATION IMPORTS
64 | */
65 |
--------------------------------------------------------------------------------
/apps/examples/angular/tic-tac-toe/src/styles.scss:
--------------------------------------------------------------------------------
1 | /* You can add global styles to this file, and also import other style files */
2 |
--------------------------------------------------------------------------------
/apps/examples/angular/tic-tac-toe/src/test-setup.ts:
--------------------------------------------------------------------------------
1 | import 'jest-preset-angular/setup-jest';
2 |
3 | import { getTestBed } from '@angular/core/testing';
4 | import {
5 | BrowserDynamicTestingModule,
6 | platformBrowserDynamicTesting,
7 | } from '@angular/platform-browser-dynamic/testing';
8 |
9 | getTestBed().resetTestEnvironment();
10 | getTestBed().initTestEnvironment(
11 | BrowserDynamicTestingModule,
12 | platformBrowserDynamicTesting(),
13 | { teardown: { destroyAfterEach: false } }
14 | );
15 |
--------------------------------------------------------------------------------
/apps/examples/angular/tic-tac-toe/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../../../dist/out-tsc",
5 | "types": [],
6 | "target": "ES2022",
7 | "useDefineForClassFields": false
8 | },
9 | "files": ["src/main.ts", "src/polyfills.ts"],
10 | "include": ["src/**/*.d.ts"],
11 | "exclude": ["jest.config.ts"]
12 | }
13 |
--------------------------------------------------------------------------------
/apps/examples/angular/tic-tac-toe/tsconfig.editor.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "include": ["**/*.ts"],
4 | "compilerOptions": {
5 | "types": ["jest", "node"]
6 | },
7 | "exclude": ["jest.config.ts"]
8 | }
9 |
--------------------------------------------------------------------------------
/apps/examples/angular/tic-tac-toe/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../../../tsconfig.base.json",
3 | "files": [],
4 | "include": [],
5 | "references": [
6 | {
7 | "path": "./tsconfig.app.json"
8 | },
9 | {
10 | "path": "./tsconfig.spec.json"
11 | },
12 | {
13 | "path": "./tsconfig.editor.json"
14 | }
15 | ],
16 | "compilerOptions": {
17 | "forceConsistentCasingInFileNames": true,
18 | "strict": true,
19 | "noImplicitReturns": true,
20 | "noFallthroughCasesInSwitch": true,
21 | "lib": ["ESNext", "dom"],
22 | "target": "es2020"
23 | },
24 | "angularCompilerOptions": {
25 | "strictInjectionParameters": true,
26 | "strictInputAccessModifiers": true,
27 | "strictTemplates": true
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/apps/examples/angular/tic-tac-toe/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../../../dist/out-tsc",
5 | "module": "commonjs",
6 | "types": ["jest", "node"]
7 | },
8 | "files": ["src/test-setup.ts"],
9 | "include": ["**/*.spec.ts", "**/*.test.ts", "**/*.d.ts", "jest.config.ts"]
10 | }
11 |
--------------------------------------------------------------------------------
/apps/examples/react/list-e2e/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["plugin:cypress/recommended", "../../../../.eslintrc.json"],
3 | "ignorePatterns": ["!**/*"],
4 | "overrides": [
5 | {
6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
7 | "rules": {}
8 | }
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/apps/examples/react/list-e2e/cypress.json:
--------------------------------------------------------------------------------
1 | {
2 | "fileServerFolder": ".",
3 | "fixturesFolder": "./src/fixtures",
4 | "integrationFolder": "./src/integration",
5 | "modifyObstructiveCode": false,
6 | "supportFile": "./src/support/index.ts",
7 | "pluginsFile": false,
8 | "video": true,
9 | "videosFolder": "../../../../dist/cypress/apps/examples/react/list-e2e/videos",
10 | "screenshotsFolder": "../../../../dist/cypress/apps/examples/react/list-e2e/screenshots",
11 | "chromeWebSecurity": false
12 | }
13 |
--------------------------------------------------------------------------------
/apps/examples/react/list-e2e/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "examples-react-list-e2e",
3 | "$schema": "../../../../node_modules/nx/schemas/project-schema.json",
4 | "sourceRoot": "apps/examples/react/list-e2e/src",
5 | "projectType": "application",
6 | "targets": {
7 | "e2e": {
8 | "executor": "@nx/cypress:cypress",
9 | "options": {
10 | "cypressConfig": "apps/examples/react/list-e2e/cypress.json",
11 | "devServerTarget": "examples-react-list:serve"
12 | },
13 | "configurations": {
14 | "production": {
15 | "devServerTarget": "examples-react-list:serve:production"
16 | }
17 | }
18 | },
19 | "lint": {
20 | "executor": "@nx/linter:eslint",
21 | "outputs": ["{options.outputFile}"],
22 | "options": {
23 | "lintFilePatterns": ["apps/examples/react/list-e2e/**/*.{js,ts}"]
24 | }
25 | }
26 | },
27 | "tags": [],
28 | "implicitDependencies": ["examples-react-list"]
29 | }
30 |
--------------------------------------------------------------------------------
/apps/examples/react/list-e2e/src/fixtures/example.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Using fixtures to represent data",
3 | "email": "hello@cypress.io"
4 | }
5 |
--------------------------------------------------------------------------------
/apps/examples/react/list-e2e/src/integration/app.spec.ts:
--------------------------------------------------------------------------------
1 | import { getGreeting } from '../support/app.po';
2 |
3 | describe('examples-react-list', () => {
4 | beforeEach(() => cy.visit('/'));
5 |
6 | it('should display welcome message', () => {
7 | // Custom command example, see `../support/commands.ts` file
8 | cy.login('my-email@something.com', 'myPassword');
9 |
10 | // Function helper example, see `../support/app.po.ts` file
11 | getGreeting().contains('Welcome to examples-react-list!');
12 | });
13 | });
14 |
--------------------------------------------------------------------------------
/apps/examples/react/list-e2e/src/support/app.po.ts:
--------------------------------------------------------------------------------
1 | export const getGreeting = () => cy.get('h1');
2 |
--------------------------------------------------------------------------------
/apps/examples/react/list-e2e/src/support/commands.ts:
--------------------------------------------------------------------------------
1 | // ***********************************************
2 | // This example commands.js shows you how to
3 | // create various custom commands and overwrite
4 | // existing commands.
5 | //
6 | // For more comprehensive examples of custom
7 | // commands please read more here:
8 | // https://on.cypress.io/custom-commands
9 | // ***********************************************
10 |
11 | // eslint-disable-next-line @typescript-eslint/no-namespace
12 | declare namespace Cypress {
13 | // eslint-disable-next-line @typescript-eslint/no-unused-vars
14 | interface Chainable {
15 | login(email: string, password: string): void;
16 | }
17 | }
18 | //
19 | // -- This is a parent command --
20 | Cypress.Commands.add('login', (email, password) => {
21 | console.log('Custom command example: Login', email, password);
22 | });
23 | //
24 | // -- This is a child command --
25 | // Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
26 | //
27 | //
28 | // -- This is a dual command --
29 | // Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
30 | //
31 | //
32 | // -- This will overwrite an existing command --
33 | // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
34 |
--------------------------------------------------------------------------------
/apps/examples/react/list-e2e/src/support/index.ts:
--------------------------------------------------------------------------------
1 | // ***********************************************************
2 | // This example support/index.js is processed and
3 | // loaded automatically before your test files.
4 | //
5 | // This is a great place to put global configuration and
6 | // behavior that modifies Cypress.
7 | //
8 | // You can change the location of this file or turn off
9 | // automatically serving support files with the
10 | // 'supportFile' configuration option.
11 | //
12 | // You can read more here:
13 | // https://on.cypress.io/configuration
14 | // ***********************************************************
15 |
16 | // Import commands.js using ES2015 syntax:
17 | import './commands';
18 |
--------------------------------------------------------------------------------
/apps/examples/react/list-e2e/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "sourceMap": false,
5 | "outDir": "../../../../dist/out-tsc",
6 | "allowJs": true,
7 | "types": ["cypress", "node"]
8 | },
9 | "include": ["src/**/*.ts", "src/**/*.js"]
10 | }
11 |
--------------------------------------------------------------------------------
/apps/examples/react/list/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "@nx/react/babel",
5 | {
6 | "runtime": "automatic"
7 | }
8 | ]
9 | ],
10 | "plugins": []
11 | }
12 |
--------------------------------------------------------------------------------
/apps/examples/react/list/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["plugin:@nx/react", "../../../../.eslintrc.json"],
3 | "ignorePatterns": ["!**/*"],
4 | "overrides": [
5 | {
6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
7 | "rules": {}
8 | },
9 | {
10 | "files": ["*.ts", "*.tsx"],
11 | "rules": {}
12 | },
13 | {
14 | "files": ["*.js", "*.jsx"],
15 | "rules": {}
16 | }
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/apps/examples/react/list/jest.config.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | export default {
3 | displayName: 'examples-react-list',
4 | preset: '../../../../jest.preset.js',
5 | transform: {
6 | '^(?!.*\\.(js|jsx|ts|tsx|css|json)$)': '@nx/react/plugins/jest',
7 | '^.+\\.[tj]sx?$': ['babel-jest', { presets: ['@nx/react/babel'] }],
8 | },
9 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
10 | coverageDirectory: '../../../../coverage/apps/examples/react/list',
11 | };
12 |
--------------------------------------------------------------------------------
/apps/examples/react/list/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "examples-react-list",
3 | "$schema": "../../../../node_modules/nx/schemas/project-schema.json",
4 | "sourceRoot": "apps/examples/react/list/src",
5 | "projectType": "application",
6 | "targets": {
7 | "build": {
8 | "executor": "@nx/webpack:webpack",
9 | "outputs": ["{options.outputPath}"],
10 | "options": {
11 | "outputPath": "dist/apps/examples/react/list",
12 | "index": "apps/examples/react/list/src/index.html",
13 | "main": "apps/examples/react/list/src/main.tsx",
14 | "polyfills": "apps/examples/react/list/src/polyfills.ts",
15 | "tsConfig": "apps/examples/react/list/tsconfig.app.json",
16 | "assets": [
17 | "apps/examples/react/list/src/favicon.ico",
18 | "apps/examples/react/list/src/assets"
19 | ],
20 | "styles": ["apps/examples/react/list/src/styles.scss"],
21 | "scripts": [],
22 | "webpackConfig": "@nx/react/plugins/webpack"
23 | },
24 | "configurations": {
25 | "production": {
26 | "fileReplacements": [
27 | {
28 | "replace": "apps/examples/react/list/src/environments/environment.ts",
29 | "with": "apps/examples/react/list/src/environments/environment.prod.ts"
30 | }
31 | ],
32 | "optimization": true,
33 | "outputHashing": "all",
34 | "sourceMap": false,
35 | "extractCss": true,
36 | "namedChunks": false,
37 | "extractLicenses": true,
38 | "vendorChunk": false,
39 | "budgets": [
40 | {
41 | "type": "initial",
42 | "maximumWarning": "500kb",
43 | "maximumError": "1mb"
44 | }
45 | ]
46 | }
47 | }
48 | },
49 | "serve": {
50 | "executor": "@nx/webpack:dev-server",
51 | "options": {
52 | "buildTarget": "examples-react-list:build",
53 | "hmr": true
54 | },
55 | "configurations": {
56 | "production": {
57 | "buildTarget": "examples-react-list:build:production",
58 | "hmr": false
59 | },
60 | "development": {
61 | "buildTarget": "examples-react-list:build:development"
62 | }
63 | },
64 | "defaultConfiguration": "development"
65 | },
66 | "lint": {
67 | "executor": "@nx/linter:eslint",
68 | "outputs": ["{options.outputFile}"],
69 | "options": {
70 | "lintFilePatterns": ["apps/examples/react/list/**/*.{ts,tsx,js,jsx}"]
71 | }
72 | },
73 | "test": {
74 | "executor": "@nx/jest:jest",
75 | "outputs": ["{workspaceRoot}/coverage/apps/examples/react/list"],
76 | "options": {
77 | "jestConfig": "apps/examples/react/list/jest.config.ts",
78 | "passWithNoTests": true
79 | }
80 | }
81 | },
82 | "tags": []
83 | }
84 |
--------------------------------------------------------------------------------
/apps/examples/react/list/src/app/CustomListImpl.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | ListViewModel,
3 | ListViewModelInputs,
4 | } from '@derxjs/examples/list-view-model-implementation';
5 | import { DeRxJSViewModel } from '@derxjs/view-model';
6 | import { useEffect, useState } from 'react';
7 | import { Observable, Observer } from 'rxjs';
8 |
9 | let pushObserver: Observer;
10 | let popObserver: Observer;
11 | const push$ = new Observable((observer) => (pushObserver = observer));
12 | const pop$ = new Observable((observer) => (popObserver = observer));
13 |
14 | export const CustomListImpl = (
15 | viewModel: DeRxJSViewModel,
16 | component: ({
17 | viewModel,
18 | pushClick,
19 | popClick,
20 | }: {
21 | viewModel: ListViewModel;
22 | pushClick: (x: string) => void;
23 | popClick: () => void;
24 | }) => any
25 | ) => {
26 | const [viewModelValue, setViewModel] = useState([] as ListViewModel);
27 | const pushClick = (value: string) => pushObserver.next(value);
28 | const popClick = () => popObserver.next();
29 | useEffect(() => {
30 | const subscription = viewModel({ push$, pop$, initialValue: [] }).subscribe(
31 | (x) => setViewModel(x)
32 | );
33 | return () => subscription.unsubscribe();
34 | }, []);
35 | return component({ viewModel: viewModelValue, popClick, pushClick });
36 | };
37 |
38 | export function ListView({
39 | viewModel,
40 | pushClick,
41 | popClick,
42 | }: {
43 | viewModel: ListViewModel;
44 | pushClick: (x: string) => void;
45 | popClick: () => void;
46 | }) {
47 | const handlePush = (e: any) => {
48 | e.preventDefault();
49 | const textInput = document.getElementById('textInput')! as HTMLInputElement;
50 | const formValue = textInput.value;
51 | pushClick(formValue);
52 | textInput.value = '';
53 | };
54 |
55 | return (
56 |
57 |
61 |
62 | {viewModel.map((item, i) => (
63 | {item}
64 | ))}
65 |
66 |
Pop
67 |
68 | );
69 | }
70 |
--------------------------------------------------------------------------------
/apps/examples/react/list/src/app/List.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | ListViewModel,
3 | customListImpl$ as listViewModel$,
4 | ListViewModelInputs,
5 | } from '@derxjs/examples/list-view-model-implementation';
6 | import { useRef } from 'react';
7 | import { DeRxJSComponent } from '@derxjs/react';
8 |
9 | export const List = ({ initialValue }: { initialValue: string[] }) => {
10 | return DeRxJSComponent>(
11 | {
12 | component: ListView as any,
13 | viewModel$: listViewModel$,
14 | initialValue,
15 | triggerMap: {
16 | pop: 'pop$',
17 | push: 'push$',
18 | },
19 | inputs: { initialValue },
20 | }
21 | );
22 | };
23 |
24 | export interface ListProps {
25 | initialState: T[];
26 | push: (toPush: T) => void;
27 | pop: () => void;
28 | }
29 |
30 | function ListView({
31 | state,
32 | triggers,
33 | }: {
34 | state: ListViewModel;
35 | triggers: {
36 | push: (x: string) => void;
37 | pop: () => void;
38 | };
39 | }) {
40 | const textInputElement = useRef(null);
41 | const handlePush = (e: any) => {
42 | e.preventDefault();
43 | const textInput = textInputElement.current!;
44 | const formValue = textInput.value;
45 | triggers.push(formValue);
46 | textInput.value = '';
47 | };
48 | return (
49 | <>
50 |
54 |
55 | {state.map((item, i) => (
56 | {item}
57 | ))}
58 |
59 | Pop
60 | >
61 | );
62 | }
63 |
--------------------------------------------------------------------------------
/apps/examples/react/list/src/app/app.module.scss:
--------------------------------------------------------------------------------
1 | .app {
2 | font-family: sans-serif;
3 | min-width: 300px;
4 | max-width: 600px;
5 | margin: 50px auto;
6 | }
7 |
8 | .app :global(.gutter-left) {
9 | margin-left: 9px;
10 | }
11 |
12 | .app :global(.col-span-2) {
13 | grid-column: span 2;
14 | }
15 |
16 | .app :global(.flex) {
17 | display: flex;
18 | align-items: center;
19 | justify-content: center;
20 | }
21 |
22 | .app :global(header) {
23 | background-color: #143055;
24 | color: white;
25 | padding: 5px;
26 | border-radius: 3px;
27 | }
28 |
29 | .app :global(main) {
30 | padding: 0 36px;
31 | }
32 |
33 | .app :global(p) {
34 | text-align: center;
35 | }
36 |
37 | .app :global(h1) {
38 | text-align: center;
39 | margin-left: 18px;
40 | font-size: 24px;
41 | }
42 |
43 | .app :global(h2) {
44 | text-align: center;
45 | font-size: 20px;
46 | margin: 40px 0 10px 0;
47 | }
48 |
49 | .app :global(.resources) {
50 | text-align: center;
51 | list-style: none;
52 | padding: 0;
53 | display: grid;
54 | grid-gap: 9px;
55 | grid-template-columns: 1fr 1fr;
56 | }
57 |
58 | .app :global(.resource) {
59 | color: #0094ba;
60 | height: 36px;
61 | background-color: rgba(0, 0, 0, 0);
62 | border: 1px solid rgba(0, 0, 0, 0.12);
63 | border-radius: 4px;
64 | padding: 3px 9px;
65 | text-decoration: none;
66 | }
67 |
68 | .app :global(.resource:hover) {
69 | background-color: rgba(68, 138, 255, 0.04);
70 | }
71 |
72 | .app :global(pre) {
73 | padding: 9px;
74 | border-radius: 4px;
75 | background-color: black;
76 | color: #eee;
77 | }
78 |
79 | .app :global(details) {
80 | border-radius: 4px;
81 | color: #333;
82 | background-color: rgba(0, 0, 0, 0);
83 | border: 1px solid rgba(0, 0, 0, 0.12);
84 | padding: 3px 9px;
85 | margin-bottom: 9px;
86 | }
87 |
88 | .app :global(summary) {
89 | outline: none;
90 | height: 36px;
91 | line-height: 36px;
92 | }
93 |
94 | .app :global(.github-star-container) {
95 | margin-top: 12px;
96 | line-height: 20px;
97 | }
98 |
99 | .app :global(.github-star-container a) {
100 | display: flex;
101 | align-items: center;
102 | text-decoration: none;
103 | color: #333;
104 | }
105 |
106 | .app :global(.github-star-badge) {
107 | color: #24292e;
108 | display: flex;
109 | align-items: center;
110 | font-size: 12px;
111 | padding: 3px 10px;
112 | border: 1px solid rgba(27, 31, 35, 0.2);
113 | border-radius: 3px;
114 | background-image: linear-gradient(-180deg, #fafbfc, #eff3f6 90%);
115 | margin-left: 4px;
116 | font-weight: 600;
117 | }
118 |
119 | .app :global(.github-star-badge:hover) {
120 | background-image: linear-gradient(-180deg, #f0f3f6, #e6ebf1 90%);
121 | border-color: rgba(27, 31, 35, 0.35);
122 | background-position: -0.5em;
123 | }
124 | .app :global(.github-star-badge .material-icons) {
125 | height: 16px;
126 | width: 16px;
127 | margin-right: 4px;
128 | }
129 |
--------------------------------------------------------------------------------
/apps/examples/react/list/src/app/app.spec.tsx:
--------------------------------------------------------------------------------
1 | import { render } from '@testing-library/react';
2 |
3 | import App from './app';
4 |
5 | describe('App', () => {
6 | it('should render successfully', () => {
7 | const { baseElement } = render( );
8 |
9 | expect(baseElement).toBeTruthy();
10 | });
11 | });
12 |
--------------------------------------------------------------------------------
/apps/examples/react/list/src/app/app.tsx:
--------------------------------------------------------------------------------
1 | import { customListImpl$ } from '@derxjs/examples/list-view-model-implementation';
2 | import { CustomListImpl, ListView } from './CustomListImpl';
3 | import { List } from './List';
4 |
5 | export const App = () => {
6 | return (
7 | <>
8 | @derxj/view-model React Usage Example
9 |
10 |
11 |
12 |
13 | {CustomListImpl(customListImpl$, ListView)}
14 | >
15 | );
16 | };
17 |
18 | export default App;
19 |
--------------------------------------------------------------------------------
/apps/examples/react/list/src/app/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/apps/examples/react/list/src/app/star.svg:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/apps/examples/react/list/src/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZackDeRose/derxjs/96e2497bf36e4bb110c9421b0f770361155df6d0/apps/examples/react/list/src/assets/.gitkeep
--------------------------------------------------------------------------------
/apps/examples/react/list/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true,
3 | };
4 |
--------------------------------------------------------------------------------
/apps/examples/react/list/src/environments/environment.ts:
--------------------------------------------------------------------------------
1 | // This file can be replaced during build by using the `fileReplacements` array.
2 | // When building for production, this file is replaced with `environment.prod.ts`.
3 |
4 | export const environment = {
5 | production: false,
6 | };
7 |
--------------------------------------------------------------------------------
/apps/examples/react/list/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZackDeRose/derxjs/96e2497bf36e4bb110c9421b0f770361155df6d0/apps/examples/react/list/src/favicon.ico
--------------------------------------------------------------------------------
/apps/examples/react/list/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | List
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/apps/examples/react/list/src/main.tsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from 'react';
2 | import * as ReactDOM from 'react-dom';
3 |
4 | import App from './app/app';
5 |
6 | ReactDOM.render(
7 |
8 |
9 | ,
10 | document.getElementById('root')
11 | );
12 |
--------------------------------------------------------------------------------
/apps/examples/react/list/src/polyfills.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Polyfill stable language features. These imports will be optimized by `@babel/preset-env`.
3 | *
4 | * See: https://github.com/zloirock/core-js#babel
5 | */
6 | import 'core-js/stable';
7 | import 'regenerator-runtime/runtime';
8 |
--------------------------------------------------------------------------------
/apps/examples/react/list/src/styles.scss:
--------------------------------------------------------------------------------
1 | /* You can add global styles to this file, and also import other style files */
2 |
--------------------------------------------------------------------------------
/apps/examples/react/list/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../../../dist/out-tsc",
5 | "types": ["node"]
6 | },
7 | "files": [
8 | "../../../../node_modules/@nx/react/typings/cssmodule.d.ts",
9 | "../../../../node_modules/@nx/react/typings/image.d.ts"
10 | ],
11 | "exclude": [
12 | "**/*.spec.ts",
13 | "**/*.test.ts",
14 | "**/*.spec.tsx",
15 | "**/*.test.tsx",
16 | "jest.config.ts"
17 | ],
18 | "include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"]
19 | }
20 |
--------------------------------------------------------------------------------
/apps/examples/react/list/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "jsx": "react-jsx",
5 | "allowJs": true,
6 | "esModuleInterop": true,
7 | "allowSyntheticDefaultImports": true,
8 | "forceConsistentCasingInFileNames": true,
9 | "strict": true,
10 | "noImplicitReturns": true,
11 | "noFallthroughCasesInSwitch": true
12 | },
13 | "files": [],
14 | "include": [],
15 | "references": [
16 | {
17 | "path": "./tsconfig.app.json"
18 | },
19 | {
20 | "path": "./tsconfig.spec.json"
21 | }
22 | ]
23 | }
24 |
--------------------------------------------------------------------------------
/apps/examples/react/list/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../../../dist/out-tsc",
5 | "module": "commonjs",
6 | "types": ["jest", "node"]
7 | },
8 | "include": [
9 | "**/*.spec.ts",
10 | "**/*.test.ts",
11 | "**/*.spec.tsx",
12 | "**/*.test.tsx",
13 | "**/*.spec.js",
14 | "**/*.test.js",
15 | "**/*.spec.jsx",
16 | "**/*.test.jsx",
17 | "**/*.d.ts",
18 | "jest.config.ts"
19 | ],
20 | "files": [
21 | "../../../../node_modules/@nx/react/typings/cssmodule.d.ts",
22 | "../../../../node_modules/@nx/react/typings/image.d.ts"
23 | ]
24 | }
25 |
--------------------------------------------------------------------------------
/apps/examples/react/tic-tac-toe-e2e/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["plugin:cypress/recommended", "../../../../.eslintrc.json"],
3 | "ignorePatterns": ["!**/*"],
4 | "overrides": [
5 | {
6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
7 | "rules": {}
8 | }
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/apps/examples/react/tic-tac-toe-e2e/cypress.json:
--------------------------------------------------------------------------------
1 | {
2 | "fileServerFolder": ".",
3 | "fixturesFolder": "./src/fixtures",
4 | "integrationFolder": "./src/integration",
5 | "modifyObstructiveCode": false,
6 | "supportFile": "./src/support/index.ts",
7 | "pluginsFile": false,
8 | "video": true,
9 | "videosFolder": "../../../../dist/cypress/apps/examples/react/tic-tac-toe-e2e/videos",
10 | "screenshotsFolder": "../../../../dist/cypress/apps/examples/react/tic-tac-toe-e2e/screenshots",
11 | "chromeWebSecurity": false
12 | }
13 |
--------------------------------------------------------------------------------
/apps/examples/react/tic-tac-toe-e2e/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "examples-react-tic-tac-toe-e2e",
3 | "$schema": "../../../../node_modules/nx/schemas/project-schema.json",
4 | "sourceRoot": "apps/examples/react/tic-tac-toe-e2e/src",
5 | "projectType": "application",
6 | "targets": {
7 | "e2e": {
8 | "executor": "@nx/cypress:cypress",
9 | "options": {
10 | "cypressConfig": "apps/examples/react/tic-tac-toe-e2e/cypress.json",
11 | "devServerTarget": "examples-react-tic-tac-toe:serve"
12 | },
13 | "configurations": {
14 | "production": {
15 | "devServerTarget": "examples-react-tic-tac-toe:serve:production"
16 | }
17 | }
18 | },
19 | "lint": {
20 | "executor": "@nx/linter:eslint",
21 | "outputs": ["{options.outputFile}"],
22 | "options": {
23 | "lintFilePatterns": ["apps/examples/react/tic-tac-toe-e2e/**/*.{js,ts}"]
24 | }
25 | }
26 | },
27 | "tags": [],
28 | "implicitDependencies": ["examples-react-tic-tac-toe"]
29 | }
30 |
--------------------------------------------------------------------------------
/apps/examples/react/tic-tac-toe-e2e/src/fixtures/example.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Using fixtures to represent data",
3 | "email": "hello@cypress.io"
4 | }
5 |
--------------------------------------------------------------------------------
/apps/examples/react/tic-tac-toe-e2e/src/integration/app.spec.ts:
--------------------------------------------------------------------------------
1 | import { getGreeting } from '../support/app.po';
2 |
3 | describe('examples-react-tic-tac-toe', () => {
4 | beforeEach(() => cy.visit('/'));
5 |
6 | it('should display welcome message', () => {
7 | // Custom command example, see `../support/commands.ts` file
8 | cy.login('my-email@something.com', 'myPassword');
9 |
10 | // Function helper example, see `../support/app.po.ts` file
11 | getGreeting().contains('Welcome to examples-react-tic-tac-toe!');
12 | });
13 | });
14 |
--------------------------------------------------------------------------------
/apps/examples/react/tic-tac-toe-e2e/src/support/app.po.ts:
--------------------------------------------------------------------------------
1 | export const getGreeting = () => cy.get('h1');
2 |
--------------------------------------------------------------------------------
/apps/examples/react/tic-tac-toe-e2e/src/support/commands.ts:
--------------------------------------------------------------------------------
1 | // ***********************************************
2 | // This example commands.js shows you how to
3 | // create various custom commands and overwrite
4 | // existing commands.
5 | //
6 | // For more comprehensive examples of custom
7 | // commands please read more here:
8 | // https://on.cypress.io/custom-commands
9 | // ***********************************************
10 |
11 | // eslint-disable-next-line @typescript-eslint/no-namespace
12 | declare namespace Cypress {
13 | // eslint-disable-next-line @typescript-eslint/no-unused-vars
14 | interface Chainable {
15 | login(email: string, password: string): void;
16 | }
17 | }
18 | //
19 | // -- This is a parent command --
20 | Cypress.Commands.add('login', (email, password) => {
21 | console.log('Custom command example: Login', email, password);
22 | });
23 | //
24 | // -- This is a child command --
25 | // Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
26 | //
27 | //
28 | // -- This is a dual command --
29 | // Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
30 | //
31 | //
32 | // -- This will overwrite an existing command --
33 | // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
34 |
--------------------------------------------------------------------------------
/apps/examples/react/tic-tac-toe-e2e/src/support/index.ts:
--------------------------------------------------------------------------------
1 | // ***********************************************************
2 | // This example support/index.js is processed and
3 | // loaded automatically before your test files.
4 | //
5 | // This is a great place to put global configuration and
6 | // behavior that modifies Cypress.
7 | //
8 | // You can change the location of this file or turn off
9 | // automatically serving support files with the
10 | // 'supportFile' configuration option.
11 | //
12 | // You can read more here:
13 | // https://on.cypress.io/configuration
14 | // ***********************************************************
15 |
16 | // Import commands.js using ES2015 syntax:
17 | import './commands';
18 |
--------------------------------------------------------------------------------
/apps/examples/react/tic-tac-toe-e2e/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "sourceMap": false,
5 | "outDir": "../../../../dist/out-tsc",
6 | "allowJs": true,
7 | "types": ["cypress", "node"]
8 | },
9 | "include": ["src/**/*.ts", "src/**/*.js"]
10 | }
11 |
--------------------------------------------------------------------------------
/apps/examples/react/tic-tac-toe/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "@nx/react/babel",
5 | {
6 | "runtime": "automatic"
7 | }
8 | ]
9 | ],
10 | "plugins": []
11 | }
12 |
--------------------------------------------------------------------------------
/apps/examples/react/tic-tac-toe/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["plugin:@nx/react", "../../../../.eslintrc.json"],
3 | "ignorePatterns": ["!**/*"],
4 | "overrides": [
5 | {
6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
7 | "rules": {}
8 | },
9 | {
10 | "files": ["*.ts", "*.tsx"],
11 | "rules": {}
12 | },
13 | {
14 | "files": ["*.js", "*.jsx"],
15 | "rules": {}
16 | }
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/apps/examples/react/tic-tac-toe/jest.config.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | export default {
3 | displayName: 'examples-react-tic-tac-toe',
4 | preset: '../../../../jest.preset.js',
5 | transform: {
6 | '^(?!.*\\.(js|jsx|ts|tsx|css|json)$)': '@nx/react/plugins/jest',
7 | '^.+\\.[tj]sx?$': ['babel-jest', { presets: ['@nx/react/babel'] }],
8 | },
9 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
10 | coverageDirectory: '../../../../coverage/apps/examples/react/tic-tac-toe',
11 | };
12 |
--------------------------------------------------------------------------------
/apps/examples/react/tic-tac-toe/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "examples-react-tic-tac-toe",
3 | "$schema": "../../../../node_modules/nx/schemas/project-schema.json",
4 | "sourceRoot": "apps/examples/react/tic-tac-toe/src",
5 | "projectType": "application",
6 | "targets": {
7 | "build": {
8 | "executor": "@nx/webpack:webpack",
9 | "outputs": ["{options.outputPath}"],
10 | "options": {
11 | "outputPath": "dist/apps/examples/react/tic-tac-toe",
12 | "index": "apps/examples/react/tic-tac-toe/src/index.html",
13 | "main": "apps/examples/react/tic-tac-toe/src/main.tsx",
14 | "polyfills": "apps/examples/react/tic-tac-toe/src/polyfills.ts",
15 | "tsConfig": "apps/examples/react/tic-tac-toe/tsconfig.app.json",
16 | "assets": [
17 | "apps/examples/react/tic-tac-toe/src/favicon.ico",
18 | "apps/examples/react/tic-tac-toe/src/assets"
19 | ],
20 | "styles": ["apps/examples/react/tic-tac-toe/src/styles.scss"],
21 | "scripts": [],
22 | "webpackConfig": "@nx/react/plugins/webpack"
23 | },
24 | "configurations": {
25 | "production": {
26 | "fileReplacements": [
27 | {
28 | "replace": "apps/examples/react/tic-tac-toe/src/environments/environment.ts",
29 | "with": "apps/examples/react/tic-tac-toe/src/environments/environment.prod.ts"
30 | }
31 | ],
32 | "optimization": true,
33 | "outputHashing": "all",
34 | "sourceMap": false,
35 | "extractCss": true,
36 | "namedChunks": false,
37 | "extractLicenses": true,
38 | "vendorChunk": false,
39 | "budgets": [
40 | {
41 | "type": "initial",
42 | "maximumWarning": "500kb",
43 | "maximumError": "1mb"
44 | }
45 | ]
46 | }
47 | }
48 | },
49 | "serve": {
50 | "executor": "@nx/webpack:dev-server",
51 | "options": {
52 | "buildTarget": "examples-react-tic-tac-toe:build",
53 | "hmr": true
54 | },
55 | "configurations": {
56 | "production": {
57 | "buildTarget": "examples-react-tic-tac-toe:build:production",
58 | "hmr": false
59 | },
60 | "development": {
61 | "buildTarget": "examples-react-tic-tac-toe:build:development"
62 | }
63 | },
64 | "defaultConfiguration": "development"
65 | },
66 | "lint": {
67 | "executor": "@nx/linter:eslint",
68 | "outputs": ["{options.outputFile}"],
69 | "options": {
70 | "lintFilePatterns": [
71 | "apps/examples/react/tic-tac-toe/**/*.{ts,tsx,js,jsx}"
72 | ]
73 | }
74 | },
75 | "test": {
76 | "executor": "@nx/jest:jest",
77 | "outputs": ["{workspaceRoot}/coverage/apps/examples/react/tic-tac-toe"],
78 | "options": {
79 | "jestConfig": "apps/examples/react/tic-tac-toe/jest.config.ts",
80 | "passWithNoTests": true
81 | }
82 | }
83 | },
84 | "tags": []
85 | }
86 |
--------------------------------------------------------------------------------
/apps/examples/react/tic-tac-toe/src/app/TicTacToe.tsx:
--------------------------------------------------------------------------------
1 | import { DeRxJSComponent } from '@derxjs/react';
2 | import {
3 | TicTacToeViewModelInputs,
4 | TicTacToeViewModel,
5 | SpaceCoordinates,
6 | SpaceContent,
7 | ticTacToeViewModel$,
8 | createInitialViewModel,
9 | randomAi,
10 | } from '@derxjs/examples/tic-tac-toe-view-model-implementation';
11 |
12 | export const TicTacToe = () => {
13 | return DeRxJSComponent<
14 | TicTacToeViewModelInputs,
15 | TicTacToeViewModel,
16 | TicTacToeProps
17 | >({
18 | viewModel$: ticTacToeViewModel$,
19 | component: TicTacToeView as any,
20 | initialValue: createInitialViewModel(),
21 | triggerMap: {
22 | spaceClick: 'userSpaceClickEvents$',
23 | resetClick: 'userResetClickEvents$',
24 | },
25 | inputs: {
26 | ai: randomAi,
27 | },
28 | });
29 | };
30 |
31 | export interface TicTacToeProps {
32 | spaceClick: (spaceCoordinates: SpaceCoordinates) => void;
33 | resetClick: () => void;
34 | }
35 |
36 | interface SpaceProps {
37 | contents: SpaceContent;
38 | spaceCoordinates: SpaceCoordinates;
39 | clickHandler: (spaceCoordinates: SpaceCoordinates) => void;
40 | }
41 | const Space = ({ contents, clickHandler, spaceCoordinates }: SpaceProps) => (
42 |
43 | clickHandler(spaceCoordinates)}>
44 | {contents.toUpperCase()}
45 |
46 |
47 | );
48 |
49 | function TicTacToeView({
50 | state,
51 | triggers,
52 | }: {
53 | state: TicTacToeViewModel;
54 | triggers: TicTacToeProps;
55 | }) {
56 | return (
57 | <>
58 | {state.turn}
59 |
60 |
61 | {([0, 1, 2] as const)
62 | .map((row) => ([0, 1, 2] as const).map((column) => [row, column]))
63 | .flat()
64 | .map(([row, column]) => (
65 |
71 | ))}
72 |
73 |
74 |
75 | Reset
76 |
77 | >
78 | );
79 | }
80 |
--------------------------------------------------------------------------------
/apps/examples/react/tic-tac-toe/src/app/app.module.scss:
--------------------------------------------------------------------------------
1 | .app {
2 | font-family: sans-serif;
3 | min-width: 300px;
4 | max-width: 600px;
5 | margin: 50px auto;
6 | }
7 |
8 | .app :global(.gutter-left) {
9 | margin-left: 9px;
10 | }
11 |
12 | .app :global(.col-span-2) {
13 | grid-column: span 2;
14 | }
15 |
16 | .app :global(.flex) {
17 | display: flex;
18 | align-items: center;
19 | justify-content: center;
20 | }
21 |
22 | .app :global(header) {
23 | background-color: #143055;
24 | color: white;
25 | padding: 5px;
26 | border-radius: 3px;
27 | }
28 |
29 | .app :global(main) {
30 | padding: 0 36px;
31 | }
32 |
33 | .app :global(p) {
34 | text-align: center;
35 | }
36 |
37 | .app :global(h1) {
38 | text-align: center;
39 | margin-left: 18px;
40 | font-size: 24px;
41 | }
42 |
43 | .app :global(h2) {
44 | text-align: center;
45 | font-size: 20px;
46 | margin: 40px 0 10px 0;
47 | }
48 |
49 | .app :global(.resources) {
50 | text-align: center;
51 | list-style: none;
52 | padding: 0;
53 | display: grid;
54 | grid-gap: 9px;
55 | grid-template-columns: 1fr 1fr;
56 | }
57 |
58 | .app :global(.resource) {
59 | color: #0094ba;
60 | height: 36px;
61 | background-color: rgba(0, 0, 0, 0);
62 | border: 1px solid rgba(0, 0, 0, 0.12);
63 | border-radius: 4px;
64 | padding: 3px 9px;
65 | text-decoration: none;
66 | }
67 |
68 | .app :global(.resource:hover) {
69 | background-color: rgba(68, 138, 255, 0.04);
70 | }
71 |
72 | .app :global(pre) {
73 | padding: 9px;
74 | border-radius: 4px;
75 | background-color: black;
76 | color: #eee;
77 | }
78 |
79 | .app :global(details) {
80 | border-radius: 4px;
81 | color: #333;
82 | background-color: rgba(0, 0, 0, 0);
83 | border: 1px solid rgba(0, 0, 0, 0.12);
84 | padding: 3px 9px;
85 | margin-bottom: 9px;
86 | }
87 |
88 | .app :global(summary) {
89 | outline: none;
90 | height: 36px;
91 | line-height: 36px;
92 | }
93 |
94 | .app :global(.github-star-container) {
95 | margin-top: 12px;
96 | line-height: 20px;
97 | }
98 |
99 | .app :global(.github-star-container a) {
100 | display: flex;
101 | align-items: center;
102 | text-decoration: none;
103 | color: #333;
104 | }
105 |
106 | .app :global(.github-star-badge) {
107 | color: #24292e;
108 | display: flex;
109 | align-items: center;
110 | font-size: 12px;
111 | padding: 3px 10px;
112 | border: 1px solid rgba(27, 31, 35, 0.2);
113 | border-radius: 3px;
114 | background-image: linear-gradient(-180deg, #fafbfc, #eff3f6 90%);
115 | margin-left: 4px;
116 | font-weight: 600;
117 | }
118 |
119 | .app :global(.github-star-badge:hover) {
120 | background-image: linear-gradient(-180deg, #f0f3f6, #e6ebf1 90%);
121 | border-color: rgba(27, 31, 35, 0.35);
122 | background-position: -0.5em;
123 | }
124 | .app :global(.github-star-badge .material-icons) {
125 | height: 16px;
126 | width: 16px;
127 | margin-right: 4px;
128 | }
129 |
--------------------------------------------------------------------------------
/apps/examples/react/tic-tac-toe/src/app/app.spec.tsx:
--------------------------------------------------------------------------------
1 | import { render } from '@testing-library/react';
2 |
3 | import App from './app';
4 |
5 | describe('App', () => {
6 | it('should render successfully', () => {
7 | const { baseElement } = render( );
8 |
9 | expect(baseElement).toBeTruthy();
10 | });
11 |
12 | it('should have a greeting as the title', () => {
13 | const { getByText } = render( );
14 |
15 | expect(getByText('Tic Tac Toe')).toBeTruthy();
16 | });
17 | });
18 |
--------------------------------------------------------------------------------
/apps/examples/react/tic-tac-toe/src/app/app.tsx:
--------------------------------------------------------------------------------
1 | import styles from './app.module.scss';
2 |
3 | import { TicTacToe } from './TicTacToe';
4 |
5 | export function App() {
6 | return (
7 | <>
8 | Tic Tac Toe
9 |
10 | >
11 | );
12 | }
13 |
14 | export default App;
15 |
--------------------------------------------------------------------------------
/apps/examples/react/tic-tac-toe/src/app/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/apps/examples/react/tic-tac-toe/src/app/star.svg:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/apps/examples/react/tic-tac-toe/src/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZackDeRose/derxjs/96e2497bf36e4bb110c9421b0f770361155df6d0/apps/examples/react/tic-tac-toe/src/assets/.gitkeep
--------------------------------------------------------------------------------
/apps/examples/react/tic-tac-toe/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true,
3 | };
4 |
--------------------------------------------------------------------------------
/apps/examples/react/tic-tac-toe/src/environments/environment.ts:
--------------------------------------------------------------------------------
1 | // This file can be replaced during build by using the `fileReplacements` array.
2 | // When building for production, this file is replaced with `environment.prod.ts`.
3 |
4 | export const environment = {
5 | production: false,
6 | };
7 |
--------------------------------------------------------------------------------
/apps/examples/react/tic-tac-toe/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZackDeRose/derxjs/96e2497bf36e4bb110c9421b0f770361155df6d0/apps/examples/react/tic-tac-toe/src/favicon.ico
--------------------------------------------------------------------------------
/apps/examples/react/tic-tac-toe/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | TicTacToe
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/apps/examples/react/tic-tac-toe/src/main.tsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from 'react';
2 | import * as ReactDOM from 'react-dom';
3 |
4 | import App from './app/app';
5 |
6 | ReactDOM.render(
7 |
8 |
9 | ,
10 | document.getElementById('root')
11 | );
12 |
--------------------------------------------------------------------------------
/apps/examples/react/tic-tac-toe/src/polyfills.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Polyfill stable language features. These imports will be optimized by `@babel/preset-env`.
3 | *
4 | * See: https://github.com/zloirock/core-js#babel
5 | */
6 | import 'core-js/stable';
7 | import 'regenerator-runtime/runtime';
8 |
--------------------------------------------------------------------------------
/apps/examples/react/tic-tac-toe/src/styles.scss:
--------------------------------------------------------------------------------
1 | /*
2 | * Remove template code below
3 | */
4 | .app {
5 | display: grid;
6 | font-family: sans-serif;
7 | min-width: 300px;
8 | max-width: 600px;
9 | margin: 50px auto;
10 | align-content: center;
11 | justify-items: center;
12 | }
13 |
14 | .border {
15 | background-color: black;
16 | width: 200px;
17 | text-align: center;
18 | }
19 |
20 | .board {
21 | display: grid;
22 | grid-template-columns: fit-content(40px) fit-content(40px) fit-content(40px);
23 | grid-template-rows: fit-content(40px) fit-content(40px) fit-content(40px);
24 | grid-column-gap: 10px;
25 | grid-row-gap: 10px;
26 | & > div {
27 | background-color: white;
28 | padding: 10px;
29 | }
30 | margin: 0;
31 | button {
32 | text-align: center;
33 | height: 40px;
34 | width: 40px;
35 | }
36 | }
37 |
38 | .reset {
39 | margin-top: 40px;
40 | }
41 |
--------------------------------------------------------------------------------
/apps/examples/react/tic-tac-toe/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../../../dist/out-tsc",
5 | "types": ["node"]
6 | },
7 | "files": [
8 | "../../../../node_modules/@nx/react/typings/cssmodule.d.ts",
9 | "../../../../node_modules/@nx/react/typings/image.d.ts"
10 | ],
11 | "exclude": [
12 | "**/*.spec.ts",
13 | "**/*.test.ts",
14 | "**/*.spec.tsx",
15 | "**/*.test.tsx",
16 | "jest.config.ts"
17 | ],
18 | "include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"]
19 | }
20 |
--------------------------------------------------------------------------------
/apps/examples/react/tic-tac-toe/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "jsx": "react-jsx",
5 | "allowJs": true,
6 | "esModuleInterop": true,
7 | "allowSyntheticDefaultImports": true,
8 | "forceConsistentCasingInFileNames": true,
9 | "strict": true,
10 | "noImplicitReturns": true,
11 | "noFallthroughCasesInSwitch": true,
12 | "lib": ["ESNext", "DOM"]
13 | },
14 | "files": [],
15 | "include": [],
16 | "references": [
17 | {
18 | "path": "./tsconfig.app.json"
19 | },
20 | {
21 | "path": "./tsconfig.spec.json"
22 | }
23 | ]
24 | }
25 |
--------------------------------------------------------------------------------
/apps/examples/react/tic-tac-toe/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../../../dist/out-tsc",
5 | "module": "commonjs",
6 | "types": ["jest", "node"]
7 | },
8 | "include": [
9 | "**/*.spec.ts",
10 | "**/*.test.ts",
11 | "**/*.spec.tsx",
12 | "**/*.test.tsx",
13 | "**/*.spec.js",
14 | "**/*.test.js",
15 | "**/*.spec.jsx",
16 | "**/*.test.jsx",
17 | "**/*.d.ts",
18 | "jest.config.ts"
19 | ],
20 | "files": [
21 | "../../../../node_modules/@nx/react/typings/cssmodule.d.ts",
22 | "../../../../node_modules/@nx/react/typings/image.d.ts"
23 | ]
24 | }
25 |
--------------------------------------------------------------------------------
/apps/examples/vanilla/list-e2e/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["plugin:cypress/recommended", "../../../../.eslintrc.json"],
3 | "ignorePatterns": ["!**/*"],
4 | "overrides": [
5 | {
6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
7 | "rules": {}
8 | }
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/apps/examples/vanilla/list-e2e/cypress.json:
--------------------------------------------------------------------------------
1 | {
2 | "fileServerFolder": ".",
3 | "fixturesFolder": "./src/fixtures",
4 | "integrationFolder": "./src/integration",
5 | "modifyObstructiveCode": false,
6 | "supportFile": "./src/support/index.ts",
7 | "pluginsFile": false,
8 | "video": true,
9 | "videosFolder": "../../../../dist/cypress/apps/examples/vanilla/list-e2e/videos",
10 | "screenshotsFolder": "../../../../dist/cypress/apps/examples/vanilla/list-e2e/screenshots",
11 | "chromeWebSecurity": false
12 | }
13 |
--------------------------------------------------------------------------------
/apps/examples/vanilla/list-e2e/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "examples-vanilla-list-e2e",
3 | "$schema": "../../../../node_modules/nx/schemas/project-schema.json",
4 | "sourceRoot": "apps/examples/vanilla/list-e2e/src",
5 | "projectType": "application",
6 | "targets": {
7 | "e2e": {
8 | "executor": "@nx/cypress:cypress",
9 | "options": {
10 | "cypressConfig": "apps/examples/vanilla/list-e2e/cypress.json",
11 | "devServerTarget": "examples-vanilla-list:serve"
12 | },
13 | "configurations": {
14 | "production": {
15 | "devServerTarget": "examples-vanilla-list:serve:production"
16 | }
17 | }
18 | },
19 | "lint": {
20 | "executor": "@nx/linter:eslint",
21 | "outputs": ["{options.outputFile}"],
22 | "options": {
23 | "lintFilePatterns": ["apps/examples/vanilla/list-e2e/**/*.{js,ts}"]
24 | }
25 | }
26 | },
27 | "tags": [],
28 | "implicitDependencies": ["examples-vanilla-list"]
29 | }
30 |
--------------------------------------------------------------------------------
/apps/examples/vanilla/list-e2e/src/fixtures/example.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Using fixtures to represent data",
3 | "email": "hello@cypress.io"
4 | }
5 |
--------------------------------------------------------------------------------
/apps/examples/vanilla/list-e2e/src/integration/app.spec.ts:
--------------------------------------------------------------------------------
1 | import { getGreeting } from '../support/app.po';
2 |
3 | describe('examples-vanilla-list', () => {
4 | beforeEach(() => cy.visit('/'));
5 |
6 | it('should display welcome message', () => {
7 | // Custom command example, see `../support/commands.ts` file
8 | cy.login('my-email@something.com', 'myPassword');
9 |
10 | // Function helper example, see `../support/app.po.ts` file
11 | getGreeting().contains('Welcome to examples-vanilla-list!');
12 | });
13 | });
14 |
--------------------------------------------------------------------------------
/apps/examples/vanilla/list-e2e/src/support/app.po.ts:
--------------------------------------------------------------------------------
1 | export const getGreeting = () => cy.get('h1');
2 |
--------------------------------------------------------------------------------
/apps/examples/vanilla/list-e2e/src/support/commands.ts:
--------------------------------------------------------------------------------
1 | // ***********************************************
2 | // This example commands.js shows you how to
3 | // create various custom commands and overwrite
4 | // existing commands.
5 | //
6 | // For more comprehensive examples of custom
7 | // commands please read more here:
8 | // https://on.cypress.io/custom-commands
9 | // ***********************************************
10 |
11 | // eslint-disable-next-line @typescript-eslint/no-namespace
12 | declare namespace Cypress {
13 | // eslint-disable-next-line @typescript-eslint/no-unused-vars
14 | interface Chainable {
15 | login(email: string, password: string): void;
16 | }
17 | }
18 | //
19 | // -- This is a parent command --
20 | Cypress.Commands.add('login', (email, password) => {
21 | console.log('Custom command example: Login', email, password);
22 | });
23 | //
24 | // -- This is a child command --
25 | // Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
26 | //
27 | //
28 | // -- This is a dual command --
29 | // Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
30 | //
31 | //
32 | // -- This will overwrite an existing command --
33 | // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
34 |
--------------------------------------------------------------------------------
/apps/examples/vanilla/list-e2e/src/support/index.ts:
--------------------------------------------------------------------------------
1 | // ***********************************************************
2 | // This example support/index.js is processed and
3 | // loaded automatically before your test files.
4 | //
5 | // This is a great place to put global configuration and
6 | // behavior that modifies Cypress.
7 | //
8 | // You can change the location of this file or turn off
9 | // automatically serving support files with the
10 | // 'supportFile' configuration option.
11 | //
12 | // You can read more here:
13 | // https://on.cypress.io/configuration
14 | // ***********************************************************
15 |
16 | // Import commands.js using ES2015 syntax:
17 | import './commands';
18 |
--------------------------------------------------------------------------------
/apps/examples/vanilla/list-e2e/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "sourceMap": false,
5 | "outDir": "../../../../dist/out-tsc",
6 | "allowJs": true,
7 | "types": ["cypress", "node"]
8 | },
9 | "include": ["src/**/*.ts", "src/**/*.js"]
10 | }
11 |
--------------------------------------------------------------------------------
/apps/examples/vanilla/list/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "@nrwl/js/babel"
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/apps/examples/vanilla/list/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["../../../../.eslintrc.json"],
3 | "ignorePatterns": ["!**/*"],
4 | "overrides": [
5 | {
6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
7 | "rules": {}
8 | },
9 | {
10 | "files": ["*.ts", "*.tsx"],
11 | "rules": {}
12 | },
13 | {
14 | "files": ["*.js", "*.jsx"],
15 | "rules": {}
16 | }
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/apps/examples/vanilla/list/jest.config.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | export default {
3 | displayName: 'examples-vanilla-list',
4 | preset: '../../../../jest.preset.js',
5 | setupFilesAfterEnv: ['/src/test-setup.ts'],
6 | globals: {},
7 | transform: {
8 | '^.+\\.[tj]s$': [
9 | 'ts-jest',
10 | {
11 | tsconfig: '/tsconfig.spec.json',
12 | },
13 | ],
14 | },
15 | moduleFileExtensions: ['ts', 'js', 'html'],
16 | coverageDirectory: '../../../../coverage/apps/examples/vanilla/list',
17 | };
18 |
--------------------------------------------------------------------------------
/apps/examples/vanilla/list/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "examples-vanilla-list",
3 | "$schema": "../../../../node_modules/nx/schemas/project-schema.json",
4 | "projectType": "application",
5 | "sourceRoot": "apps/examples/vanilla/list/src",
6 | "tags": [],
7 | "targets": {
8 | "build": {
9 | "executor": "@nx/webpack:webpack",
10 | "outputs": ["{options.outputPath}"],
11 | "options": {
12 | "outputPath": "dist/apps/examples/vanilla/list",
13 | "index": "apps/examples/vanilla/list/src/index.html",
14 | "main": "apps/examples/vanilla/list/src/main.ts",
15 | "polyfills": "apps/examples/vanilla/list/src/polyfills.ts",
16 | "tsConfig": "apps/examples/vanilla/list/tsconfig.app.json",
17 | "assets": [
18 | "apps/examples/vanilla/list/src/favicon.ico",
19 | "apps/examples/vanilla/list/src/assets"
20 | ],
21 | "styles": ["apps/examples/vanilla/list/src/styles.css"],
22 | "scripts": []
23 | },
24 | "configurations": {
25 | "production": {
26 | "fileReplacements": [
27 | {
28 | "replace": "apps/examples/vanilla/list/src/environments/environment.ts",
29 | "with": "apps/examples/vanilla/list/src/environments/environment.prod.ts"
30 | }
31 | ],
32 | "optimization": true,
33 | "outputHashing": "all",
34 | "sourceMap": false,
35 | "extractCss": true,
36 | "namedChunks": false,
37 | "extractLicenses": true,
38 | "vendorChunk": false,
39 | "budgets": [
40 | {
41 | "type": "initial",
42 | "maximumWarning": "2mb",
43 | "maximumError": "5mb"
44 | }
45 | ]
46 | }
47 | }
48 | },
49 | "serve": {
50 | "executor": "@nx/webpack:dev-server",
51 | "options": {
52 | "buildTarget": "examples-vanilla-list:build"
53 | },
54 | "configurations": {
55 | "production": {
56 | "buildTarget": "examples-vanilla-list:build:production"
57 | },
58 | "development": {
59 | "buildTarget": "examples-vanilla-list:build:development"
60 | }
61 | },
62 | "defaultConfiguration": "development"
63 | },
64 | "lint": {
65 | "executor": "@nx/linter:eslint",
66 | "outputs": ["{options.outputFile}"],
67 | "options": {
68 | "lintFilePatterns": ["apps/examples/vanilla/list/**/*.ts"]
69 | }
70 | },
71 | "test": {
72 | "executor": "@nx/jest:jest",
73 | "outputs": ["{workspaceRoot}/coverage/apps/examples/vanilla/list"],
74 | "options": {
75 | "jestConfig": "apps/examples/vanilla/list/jest.config.ts",
76 | "passWithNoTests": true
77 | }
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/apps/examples/vanilla/list/src/app/app.element.css:
--------------------------------------------------------------------------------
1 | /*
2 | * Remove template code below
3 | */
4 | body > *:first-child {
5 | display: block;
6 | font-family: sans-serif;
7 | min-width: 300px;
8 | max-width: 600px;
9 | margin: 50px auto;
10 | }
11 |
12 | .gutter-left {
13 | margin-left: 9px;
14 | }
15 |
16 | .col-span-2 {
17 | grid-column: span 2;
18 | }
19 |
20 | .flex {
21 | display: flex;
22 | align-items: center;
23 | justify-content: center;
24 | }
25 |
26 | header {
27 | background-color: #143055;
28 | color: white;
29 | padding: 5px;
30 | border-radius: 3px;
31 | }
32 |
33 | main {
34 | padding: 0 36px;
35 | }
36 |
37 | p {
38 | text-align: center;
39 | }
40 |
41 | h1 {
42 | text-align: center;
43 | margin-left: 18px;
44 | font-size: 24px;
45 | }
46 |
47 | h2 {
48 | text-align: center;
49 | font-size: 20px;
50 | margin: 40px 0 10px 0;
51 | }
52 |
53 | .resources {
54 | text-align: center;
55 | list-style: none;
56 | padding: 0;
57 | display: grid;
58 | grid-gap: 9px;
59 | grid-template-columns: 1fr 1fr;
60 | }
61 |
62 | .resource {
63 | color: #0094ba;
64 | height: 36px;
65 | background-color: rgba(0, 0, 0, 0);
66 | border: 1px solid rgba(0, 0, 0, 0.12);
67 | border-radius: 4px;
68 | padding: 3px 9px;
69 | text-decoration: none;
70 | }
71 |
72 | .resource:hover {
73 | background-color: rgba(68, 138, 255, 0.04);
74 | }
75 |
76 | pre {
77 | padding: 9px;
78 | border-radius: 4px;
79 | background-color: black;
80 | color: #eee;
81 | }
82 |
83 | details {
84 | border-radius: 4px;
85 | color: #333;
86 | background-color: rgba(0, 0, 0, 0);
87 | border: 1px solid rgba(0, 0, 0, 0.12);
88 | padding: 3px 9px;
89 | margin-bottom: 9px;
90 | }
91 |
92 | summary {
93 | cursor: pointer;
94 | outline: none;
95 | height: 36px;
96 | line-height: 36px;
97 | }
98 |
99 | .github-star-container {
100 | margin-top: 12px;
101 | line-height: 20px;
102 | }
103 |
104 | .github-star-container a {
105 | display: flex;
106 | align-items: center;
107 | text-decoration: none;
108 | color: #333;
109 | }
110 |
111 | .github-star-badge {
112 | color: #24292e;
113 | display: flex;
114 | align-items: center;
115 | font-size: 12px;
116 | padding: 3px 10px;
117 | border: 1px solid rgba(27, 31, 35, 0.2);
118 | border-radius: 3px;
119 | background-image: linear-gradient(-180deg, #fafbfc, #eff3f6 90%);
120 | margin-left: 4px;
121 | font-weight: 600;
122 | }
123 |
124 | .github-star-badge:hover {
125 | background-image: linear-gradient(-180deg, #f0f3f6, #e6ebf1 90%);
126 | border-color: rgba(27, 31, 35, 0.35);
127 | background-position: -0.5em;
128 | }
129 | .github-star-badge .material-icons {
130 | height: 16px;
131 | width: 16px;
132 | margin-right: 4px;
133 | }
134 |
--------------------------------------------------------------------------------
/apps/examples/vanilla/list/src/app/app.element.ts:
--------------------------------------------------------------------------------
1 | import { fromEvent } from 'rxjs';
2 | import { map, tap } from 'rxjs/operators';
3 | import './app.element.css';
4 | import { reducerListImpl$ as listViewModel$ } from '@derxjs/examples/list-view-model-implementation';
5 | // import { customListImpl$ as listViewModel$ } from '@derxjs/examples/list-view-model-implementation';
6 |
7 | const appDiv: HTMLElement = document.getElementById('app');
8 | const formId = `push-form`;
9 | const textInputId = `text-input`;
10 | const pushButtonId = `push-button`;
11 | const popButtonId = `pop-button`;
12 | const listId = `list`;
13 | appDiv.innerHTML = `@derxjs/view-model Usage Example
14 |
15 |
19 |
20 |
21 |
22 | Pop
23 | `;
24 |
25 | const listTarget = document.getElementById(listId);
26 | const pushForm = document.getElementById(formId) as HTMLFormElement;
27 | const textInput = document.getElementById(textInputId) as HTMLInputElement;
28 | const pushButton = document.getElementById(pushButtonId);
29 | const popButton = document.getElementById(popButtonId);
30 |
31 | pushForm.onsubmit = (event) => {
32 | event.preventDefault();
33 | };
34 |
35 | const push$ = fromEvent(pushButton, 'click').pipe(
36 | map(() => textInput.value),
37 | tap(() => (textInput.value = ''))
38 | );
39 | const pop$ = fromEvent(popButton, 'click').pipe(map(() => undefined));
40 |
41 | listViewModel$({
42 | push$,
43 | pop$,
44 | initialValue: [],
45 | }).subscribe((list) => {
46 | listTarget.innerHTML = list.map((item) => `${item} `).join('');
47 | });
48 |
--------------------------------------------------------------------------------
/apps/examples/vanilla/list/src/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZackDeRose/derxjs/96e2497bf36e4bb110c9421b0f770361155df6d0/apps/examples/vanilla/list/src/assets/.gitkeep
--------------------------------------------------------------------------------
/apps/examples/vanilla/list/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true,
3 | };
4 |
--------------------------------------------------------------------------------
/apps/examples/vanilla/list/src/environments/environment.ts:
--------------------------------------------------------------------------------
1 | // This file can be replaced during build by using the `fileReplacements` array.
2 | // When building for production, this file is replaced with `environment.prod.ts`.
3 |
4 | export const environment = {
5 | production: false,
6 | };
7 |
--------------------------------------------------------------------------------
/apps/examples/vanilla/list/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZackDeRose/derxjs/96e2497bf36e4bb110c9421b0f770361155df6d0/apps/examples/vanilla/list/src/favicon.ico
--------------------------------------------------------------------------------
/apps/examples/vanilla/list/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | List
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/apps/examples/vanilla/list/src/main.ts:
--------------------------------------------------------------------------------
1 | import './app/app.element.ts';
2 |
--------------------------------------------------------------------------------
/apps/examples/vanilla/list/src/polyfills.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Polyfill stable language features. These imports will be optimized by `@babel/preset-env`.
3 | *
4 | * See: https://github.com/zloirock/core-js#babel
5 | */
6 | import 'core-js/stable';
7 | import 'regenerator-runtime/runtime';
8 |
--------------------------------------------------------------------------------
/apps/examples/vanilla/list/src/styles.css:
--------------------------------------------------------------------------------
1 | /* You can add global styles to this file, and also import other style files */
2 |
--------------------------------------------------------------------------------
/apps/examples/vanilla/list/src/test-setup.ts:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZackDeRose/derxjs/96e2497bf36e4bb110c9421b0f770361155df6d0/apps/examples/vanilla/list/src/test-setup.ts
--------------------------------------------------------------------------------
/apps/examples/vanilla/list/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../../../dist/out-tsc",
5 | "types": ["node"]
6 | },
7 | "exclude": ["**/*.spec.ts", "**/*.test.ts", "jest.config.ts"],
8 | "include": ["**/*.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/apps/examples/vanilla/list/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../../../tsconfig.base.json",
3 | "files": [],
4 | "include": [],
5 | "references": [
6 | {
7 | "path": "./tsconfig.app.json"
8 | },
9 | {
10 | "path": "./tsconfig.spec.json"
11 | }
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/apps/examples/vanilla/list/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../../../dist/out-tsc",
5 | "module": "commonjs",
6 | "types": ["jest", "node"]
7 | },
8 | "files": ["src/test-setup.ts"],
9 | "include": ["**/*.spec.ts", "**/*.test.ts", "**/*.d.ts", "jest.config.ts"]
10 | }
11 |
--------------------------------------------------------------------------------
/apps/examples/vanilla/tic-tac-toe-e2e/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["plugin:cypress/recommended", "../../../../.eslintrc.json"],
3 | "ignorePatterns": ["!**/*"],
4 | "overrides": [
5 | {
6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
7 | "rules": {}
8 | }
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/apps/examples/vanilla/tic-tac-toe-e2e/cypress.json:
--------------------------------------------------------------------------------
1 | {
2 | "fileServerFolder": ".",
3 | "fixturesFolder": "./src/fixtures",
4 | "integrationFolder": "./src/integration",
5 | "modifyObstructiveCode": false,
6 | "supportFile": "./src/support/index.ts",
7 | "pluginsFile": false,
8 | "video": true,
9 | "videosFolder": "../../../../dist/cypress/apps/examples/vanilla/tic-tac-toe-e2e/videos",
10 | "screenshotsFolder": "../../../../dist/cypress/apps/examples/vanilla/tic-tac-toe-e2e/screenshots",
11 | "chromeWebSecurity": false
12 | }
13 |
--------------------------------------------------------------------------------
/apps/examples/vanilla/tic-tac-toe-e2e/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "examples-vanilla-tic-tac-toe-e2e",
3 | "$schema": "../../../../node_modules/nx/schemas/project-schema.json",
4 | "sourceRoot": "apps/examples/vanilla/tic-tac-toe-e2e/src",
5 | "projectType": "application",
6 | "targets": {
7 | "e2e": {
8 | "executor": "@nx/cypress:cypress",
9 | "options": {
10 | "cypressConfig": "apps/examples/vanilla/tic-tac-toe-e2e/cypress.json",
11 | "devServerTarget": "examples-vanilla-tic-tac-toe:serve"
12 | },
13 | "configurations": {
14 | "production": {
15 | "devServerTarget": "examples-vanilla-tic-tac-toe:serve:production"
16 | }
17 | }
18 | },
19 | "lint": {
20 | "executor": "@nx/linter:eslint",
21 | "outputs": ["{options.outputFile}"],
22 | "options": {
23 | "lintFilePatterns": [
24 | "apps/examples/vanilla/tic-tac-toe-e2e/**/*.{js,ts}"
25 | ]
26 | }
27 | }
28 | },
29 | "tags": [],
30 | "implicitDependencies": ["examples-vanilla-tic-tac-toe"]
31 | }
32 |
--------------------------------------------------------------------------------
/apps/examples/vanilla/tic-tac-toe-e2e/src/fixtures/example.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Using fixtures to represent data",
3 | "email": "hello@cypress.io"
4 | }
5 |
--------------------------------------------------------------------------------
/apps/examples/vanilla/tic-tac-toe-e2e/src/integration/app.spec.ts:
--------------------------------------------------------------------------------
1 | import { getGreeting } from '../support/app.po';
2 |
3 | describe('examples-vanilla-tic-tac-toe', () => {
4 | beforeEach(() => cy.visit('/'));
5 |
6 | it('should display welcome message', () => {
7 | // Custom command example, see `../support/commands.ts` file
8 | cy.login('my-email@something.com', 'myPassword');
9 |
10 | // Function helper example, see `../support/app.po.ts` file
11 | getGreeting().contains('Welcome to examples-vanilla-tic-tac-toe!');
12 | });
13 | });
14 |
--------------------------------------------------------------------------------
/apps/examples/vanilla/tic-tac-toe-e2e/src/support/app.po.ts:
--------------------------------------------------------------------------------
1 | export const getGreeting = () => cy.get('h1');
2 |
--------------------------------------------------------------------------------
/apps/examples/vanilla/tic-tac-toe-e2e/src/support/commands.ts:
--------------------------------------------------------------------------------
1 | // ***********************************************
2 | // This example commands.js shows you how to
3 | // create various custom commands and overwrite
4 | // existing commands.
5 | //
6 | // For more comprehensive examples of custom
7 | // commands please read more here:
8 | // https://on.cypress.io/custom-commands
9 | // ***********************************************
10 |
11 | // eslint-disable-next-line @typescript-eslint/no-namespace
12 | declare namespace Cypress {
13 | // eslint-disable-next-line @typescript-eslint/no-unused-vars
14 | interface Chainable {
15 | login(email: string, password: string): void;
16 | }
17 | }
18 | //
19 | // -- This is a parent command --
20 | Cypress.Commands.add('login', (email, password) => {
21 | console.log('Custom command example: Login', email, password);
22 | });
23 | //
24 | // -- This is a child command --
25 | // Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
26 | //
27 | //
28 | // -- This is a dual command --
29 | // Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
30 | //
31 | //
32 | // -- This will overwrite an existing command --
33 | // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
34 |
--------------------------------------------------------------------------------
/apps/examples/vanilla/tic-tac-toe-e2e/src/support/index.ts:
--------------------------------------------------------------------------------
1 | // ***********************************************************
2 | // This example support/index.js is processed and
3 | // loaded automatically before your test files.
4 | //
5 | // This is a great place to put global configuration and
6 | // behavior that modifies Cypress.
7 | //
8 | // You can change the location of this file or turn off
9 | // automatically serving support files with the
10 | // 'supportFile' configuration option.
11 | //
12 | // You can read more here:
13 | // https://on.cypress.io/configuration
14 | // ***********************************************************
15 |
16 | // Import commands.js using ES2015 syntax:
17 | import './commands';
18 |
--------------------------------------------------------------------------------
/apps/examples/vanilla/tic-tac-toe-e2e/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "sourceMap": false,
5 | "outDir": "../../../../dist/out-tsc",
6 | "allowJs": true,
7 | "types": ["cypress", "node"]
8 | },
9 | "include": ["src/**/*.ts", "src/**/*.js"]
10 | }
11 |
--------------------------------------------------------------------------------
/apps/examples/vanilla/tic-tac-toe/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "@nrwl/js/babel"
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/apps/examples/vanilla/tic-tac-toe/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["../../../../.eslintrc.json"],
3 | "ignorePatterns": ["!**/*"],
4 | "overrides": [
5 | {
6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
7 | "rules": {}
8 | },
9 | {
10 | "files": ["*.ts", "*.tsx"],
11 | "rules": {}
12 | },
13 | {
14 | "files": ["*.js", "*.jsx"],
15 | "rules": {}
16 | }
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/apps/examples/vanilla/tic-tac-toe/jest.config.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | export default {
3 | displayName: 'examples-vanilla-tic-tac-toe',
4 | preset: '../../../../jest.preset.js',
5 | setupFilesAfterEnv: ['/src/test-setup.ts'],
6 | globals: {},
7 | transform: {
8 | '^.+\\.[tj]s$': [
9 | 'ts-jest',
10 | {
11 | tsconfig: '/tsconfig.spec.json',
12 | },
13 | ],
14 | },
15 | moduleFileExtensions: ['ts', 'js', 'html'],
16 | coverageDirectory: '../../../../coverage/apps/examples/vanilla/tic-tac-toe',
17 | };
18 |
--------------------------------------------------------------------------------
/apps/examples/vanilla/tic-tac-toe/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "examples-vanilla-tic-tac-toe",
3 | "$schema": "../../../../node_modules/nx/schemas/project-schema.json",
4 | "projectType": "application",
5 | "sourceRoot": "apps/examples/vanilla/tic-tac-toe/src",
6 | "tags": [],
7 | "targets": {
8 | "build": {
9 | "executor": "@nx/webpack:webpack",
10 | "outputs": ["{options.outputPath}"],
11 | "options": {
12 | "outputPath": "dist/apps/examples/vanilla/tic-tac-toe",
13 | "index": "apps/examples/vanilla/tic-tac-toe/src/index.html",
14 | "main": "apps/examples/vanilla/tic-tac-toe/src/main.ts",
15 | "polyfills": "apps/examples/vanilla/tic-tac-toe/src/polyfills.ts",
16 | "tsConfig": "apps/examples/vanilla/tic-tac-toe/tsconfig.app.json",
17 | "assets": [
18 | "apps/examples/vanilla/tic-tac-toe/src/favicon.ico",
19 | "apps/examples/vanilla/tic-tac-toe/src/assets"
20 | ],
21 | "styles": ["apps/examples/vanilla/tic-tac-toe/src/styles.scss"],
22 | "scripts": []
23 | },
24 | "configurations": {
25 | "production": {
26 | "fileReplacements": [
27 | {
28 | "replace": "apps/examples/vanilla/tic-tac-toe/src/environments/environment.ts",
29 | "with": "apps/examples/vanilla/tic-tac-toe/src/environments/environment.prod.ts"
30 | }
31 | ],
32 | "optimization": true,
33 | "outputHashing": "all",
34 | "sourceMap": false,
35 | "extractCss": true,
36 | "namedChunks": false,
37 | "extractLicenses": true,
38 | "vendorChunk": false,
39 | "budgets": [
40 | {
41 | "type": "initial",
42 | "maximumWarning": "2mb",
43 | "maximumError": "5mb"
44 | }
45 | ]
46 | }
47 | }
48 | },
49 | "serve": {
50 | "executor": "@nx/webpack:dev-server",
51 | "options": {
52 | "buildTarget": "examples-vanilla-tic-tac-toe:build"
53 | },
54 | "configurations": {
55 | "production": {
56 | "buildTarget": "examples-vanilla-tic-tac-toe:build:production"
57 | },
58 | "development": {
59 | "buildTarget": "examples-vanilla-tic-tac-toe:build:development"
60 | }
61 | },
62 | "defaultConfiguration": "development"
63 | },
64 | "lint": {
65 | "executor": "@nx/linter:eslint",
66 | "outputs": ["{options.outputFile}"],
67 | "options": {
68 | "lintFilePatterns": ["apps/examples/vanilla/tic-tac-toe/**/*.ts"]
69 | }
70 | },
71 | "test": {
72 | "executor": "@nx/jest:jest",
73 | "outputs": ["{workspaceRoot}/coverage/apps/examples/vanilla/tic-tac-toe"],
74 | "options": {
75 | "jestConfig": "apps/examples/vanilla/tic-tac-toe/jest.config.ts",
76 | "passWithNoTests": true
77 | }
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/apps/examples/vanilla/tic-tac-toe/src/app/app.element.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZackDeRose/derxjs/96e2497bf36e4bb110c9421b0f770361155df6d0/apps/examples/vanilla/tic-tac-toe/src/app/app.element.scss
--------------------------------------------------------------------------------
/apps/examples/vanilla/tic-tac-toe/src/app/app.element.ts:
--------------------------------------------------------------------------------
1 | import {
2 | randomAi,
3 | SpaceCoordinates,
4 | ticTacToeViewModel$,
5 | } from '@derxjs/examples/tic-tac-toe-view-model-implementation';
6 | import { fromEvent, merge, Observable } from 'rxjs';
7 | import { map } from 'rxjs/operators';
8 | import './app.element.scss';
9 |
10 | const appDiv: HTMLElement = document.getElementById('app');
11 | const turnTextId = 'turn-text';
12 |
13 | appDiv.innerHTML = `
14 | Tic Tac Toe
15 |
16 |
17 |
18 | ${([0, 1, 2] as const)
19 | .map((row) => ([0, 1, 2] as const).map((column) => [row, column]))
20 | .flat()
21 | .map(
22 | ([row, column]) => `
23 |
24 |
25 |
26 | `
27 | )
28 | .join('')}
29 |
30 |
31 |
32 | Reset
33 |
34 | `;
35 |
36 | const turnTextElement = document.getElementById(turnTextId);
37 | const spaceButtonElements: Record> = (
38 | [0, 1, 2] as const
39 | )
40 | .map((row) => ([0, 1, 2] as const).map((column) => [row, column]))
41 | .flat()
42 | .reduce((acc, [row, column]) => {
43 | const id = `row:${row}::column:${column}`;
44 | const buttonElement = document.getElementById(id) as HTMLButtonElement;
45 | acc[id] = fromEvent(buttonElement, 'click').pipe(
46 | map(() => ({ row, column }))
47 | );
48 | return acc;
49 | }, {} as Record>);
50 |
51 | const userSpaceClickEvents$ = merge(...Object.values(spaceButtonElements));
52 | const userResetClickEvents$ = fromEvent(
53 | document.getElementById('reset'),
54 | 'click'
55 | ).pipe(map(() => undefined));
56 | const vm$ = ticTacToeViewModel$({
57 | userSpaceClickEvents$,
58 | userResetClickEvents$,
59 | ai: randomAi,
60 | });
61 | vm$.subscribe(({ board, turn }) => {
62 | turnTextElement.textContent = turn;
63 | ([0, 1, 2] as const)
64 | .map((row) => ([0, 1, 2] as const).map((column) => [row, column]))
65 | .flat()
66 | .forEach(([row, column]) => {
67 | const buttonElement = document.getElementById(
68 | `row:${row}::column:${column}`
69 | );
70 | buttonElement.textContent = board[row][column].toUpperCase();
71 | });
72 | });
73 |
--------------------------------------------------------------------------------
/apps/examples/vanilla/tic-tac-toe/src/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZackDeRose/derxjs/96e2497bf36e4bb110c9421b0f770361155df6d0/apps/examples/vanilla/tic-tac-toe/src/assets/.gitkeep
--------------------------------------------------------------------------------
/apps/examples/vanilla/tic-tac-toe/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true,
3 | };
4 |
--------------------------------------------------------------------------------
/apps/examples/vanilla/tic-tac-toe/src/environments/environment.ts:
--------------------------------------------------------------------------------
1 | // This file can be replaced during build by using the `fileReplacements` array.
2 | // When building for production, this file is replaced with `environment.prod.ts`.
3 |
4 | export const environment = {
5 | production: false,
6 | };
7 |
--------------------------------------------------------------------------------
/apps/examples/vanilla/tic-tac-toe/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZackDeRose/derxjs/96e2497bf36e4bb110c9421b0f770361155df6d0/apps/examples/vanilla/tic-tac-toe/src/favicon.ico
--------------------------------------------------------------------------------
/apps/examples/vanilla/tic-tac-toe/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | TicTacToe
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/apps/examples/vanilla/tic-tac-toe/src/main.ts:
--------------------------------------------------------------------------------
1 | import './app/app.element.ts';
2 |
--------------------------------------------------------------------------------
/apps/examples/vanilla/tic-tac-toe/src/polyfills.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Polyfill stable language features. These imports will be optimized by `@babel/preset-env`.
3 | *
4 | * See: https://github.com/zloirock/core-js#babel
5 | */
6 | import 'core-js/stable';
7 | import 'regenerator-runtime/runtime';
8 |
--------------------------------------------------------------------------------
/apps/examples/vanilla/tic-tac-toe/src/styles.scss:
--------------------------------------------------------------------------------
1 | /*
2 | * Remove template code below
3 | */
4 | #app {
5 | display: grid;
6 | font-family: sans-serif;
7 | min-width: 300px;
8 | max-width: 600px;
9 | margin: 50px auto;
10 | align-content: center;
11 | justify-items: center;
12 | }
13 |
14 | .border {
15 | background-color: black;
16 | width: 200px;
17 | text-align: center;
18 | }
19 |
20 | .board {
21 | display: grid;
22 | grid-template-columns: fit-content(40px) fit-content(40px) fit-content(40px);
23 | grid-template-rows: fit-content(40px) fit-content(40px) fit-content(40px);
24 | grid-column-gap: 10px;
25 | grid-row-gap: 10px;
26 | & > div {
27 | background-color: white;
28 | padding: 10px;
29 | }
30 | margin: 0;
31 | button {
32 | text-align: center;
33 | height: 40px;
34 | width: 40px;
35 | }
36 | }
37 |
38 | .reset {
39 | margin-top: 40px;
40 | }
41 |
--------------------------------------------------------------------------------
/apps/examples/vanilla/tic-tac-toe/src/test-setup.ts:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZackDeRose/derxjs/96e2497bf36e4bb110c9421b0f770361155df6d0/apps/examples/vanilla/tic-tac-toe/src/test-setup.ts
--------------------------------------------------------------------------------
/apps/examples/vanilla/tic-tac-toe/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../../../dist/out-tsc",
5 | "types": ["node"]
6 | },
7 | "exclude": ["**/*.spec.ts", "**/*.test.ts", "jest.config.ts"],
8 | "include": ["**/*.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/apps/examples/vanilla/tic-tac-toe/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../../../tsconfig.base.json",
3 | "files": [],
4 | "include": [],
5 | "references": [
6 | {
7 | "path": "./tsconfig.app.json"
8 | },
9 | {
10 | "path": "./tsconfig.spec.json"
11 | }
12 | ],
13 | "compilerOptions": {
14 | "lib": ["DOM", "ESNext"]
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/apps/examples/vanilla/tic-tac-toe/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../../../dist/out-tsc",
5 | "module": "commonjs",
6 | "types": ["jest", "node"]
7 | },
8 | "files": ["src/test-setup.ts"],
9 | "include": ["**/*.spec.ts", "**/*.test.ts", "**/*.d.ts", "jest.config.ts"]
10 | }
11 |
--------------------------------------------------------------------------------
/babel.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "babelrcRoots": ["*"]
3 | }
4 |
--------------------------------------------------------------------------------
/derxjs-logo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZackDeRose/derxjs/96e2497bf36e4bb110c9421b0f770361155df6d0/derxjs-logo.jpg
--------------------------------------------------------------------------------
/derxjs-react-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZackDeRose/derxjs/96e2497bf36e4bb110c9421b0f770361155df6d0/derxjs-react-logo.png
--------------------------------------------------------------------------------
/jest.config.ts:
--------------------------------------------------------------------------------
1 | const { getJestProjects } = require('@nx/jest');
2 |
3 | export default {
4 | projects: getJestProjects(),
5 | };
6 |
--------------------------------------------------------------------------------
/jest.preset.js:
--------------------------------------------------------------------------------
1 | const nxPreset = require('@nx/jest/preset').default;
2 |
3 | module.exports = {
4 | ...nxPreset,
5 | /* TODO: Update to latest Jest snapshotFormat
6 | * By default Nx has kept the older style of Jest Snapshot formats
7 | * to prevent breaking of any existing tests with snapshots.
8 | * It's recommend you update to the latest format.
9 | * You can do this by removing snapshotFormat property
10 | * and running tests with --update-snapshot flag.
11 | * Example: "nx affected --targets=test --update-snapshot"
12 | * More info: https://jestjs.io/docs/upgrading-to-jest29#snapshot-format
13 | */
14 | snapshotFormat: { escapeString: true, printBasicPrototype: true },
15 | };
16 |
--------------------------------------------------------------------------------
/libs/examples/list-view-model-implementation/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "@nrwl/js/babel",
5 | {
6 | "useBuiltIns": "usage"
7 | }
8 | ]
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/libs/examples/list-view-model-implementation/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["../../../.eslintrc.json"],
3 | "ignorePatterns": ["!**/*"],
4 | "overrides": [
5 | {
6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
7 | "rules": {}
8 | },
9 | {
10 | "files": ["*.ts", "*.tsx"],
11 | "rules": {}
12 | },
13 | {
14 | "files": ["*.js", "*.jsx"],
15 | "rules": {}
16 | }
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/libs/examples/list-view-model-implementation/README.md:
--------------------------------------------------------------------------------
1 | # examples-list-view-model-implementation
2 |
3 | This library was generated with [Nx](https://nx.dev).
4 |
5 | ## Running unit tests
6 |
7 | Run `nx test examples-list-view-model-implementation` to execute the unit tests via [Jest](https://jestjs.io).
8 |
--------------------------------------------------------------------------------
/libs/examples/list-view-model-implementation/jest.config.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | export default {
3 | displayName: 'examples-list-view-model-implementation',
4 | preset: '../../../jest.preset.js',
5 | globals: {},
6 | transform: {
7 | '^.+\\.[tj]sx?$': [
8 | 'ts-jest',
9 | {
10 | tsconfig: '/tsconfig.spec.json',
11 | },
12 | ],
13 | },
14 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
15 | coverageDirectory:
16 | '../../../coverage/libs/examples/list-view-model-implementation',
17 | };
18 |
--------------------------------------------------------------------------------
/libs/examples/list-view-model-implementation/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "examples-list-view-model-implementation",
3 | "$schema": "../../../node_modules/nx/schemas/project-schema.json",
4 | "sourceRoot": "libs/examples/list-view-model-implementation/src",
5 | "projectType": "library",
6 | "targets": {
7 | "lint": {
8 | "executor": "@nx/linter:eslint",
9 | "outputs": ["{options.outputFile}"],
10 | "options": {
11 | "lintFilePatterns": [
12 | "libs/examples/list-view-model-implementation/**/*.ts"
13 | ]
14 | }
15 | },
16 | "test": {
17 | "executor": "@nx/jest:jest",
18 | "outputs": [
19 | "{workspaceRoot}/coverage/libs/examples/list-view-model-implementation"
20 | ],
21 | "options": {
22 | "jestConfig": "libs/examples/list-view-model-implementation/jest.config.ts",
23 | "passWithNoTests": true
24 | }
25 | }
26 | },
27 | "tags": ["type:example"]
28 | }
29 |
--------------------------------------------------------------------------------
/libs/examples/list-view-model-implementation/src/index.ts:
--------------------------------------------------------------------------------
1 | export { listViewModel$ as customListImpl$ } from './lib/custom-implementation';
2 | export { listViewModel$ as reducerListImpl$ } from './lib/implementation-using-reducer-package';
3 | export * from './lib/types';
4 |
--------------------------------------------------------------------------------
/libs/examples/list-view-model-implementation/src/lib/custom-implementation.ts:
--------------------------------------------------------------------------------
1 | import { DeRxJSViewModel } from '@derxjs/view-model';
2 | import { merge } from 'rxjs';
3 | import { map, scan, startWith } from 'rxjs/operators';
4 | import { ListAction, ListViewModel, ListViewModelInputs } from './types';
5 |
6 | export const listViewModel$: DeRxJSViewModel<
7 | ListViewModelInputs,
8 | ListViewModel
9 | > = ({ push$, pop$, initialValue }) => {
10 | return merge(
11 | push$.pipe(map((item) => ({ type: 'push' as const, item }))),
12 | pop$.pipe(map(() => ({ type: 'pop' as const })))
13 | ).pipe(scan(reducer, initialValue), startWith(initialValue));
14 | };
15 |
16 | function reducer(state: ListViewModel, action: ListAction): ListViewModel {
17 | switch (action.type) {
18 | case 'push': {
19 | return [...state, action.item];
20 | }
21 | case 'pop': {
22 | const newState = [...state];
23 | newState.pop();
24 | return newState;
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/libs/examples/list-view-model-implementation/src/lib/implementation-using-reducer-package.ts:
--------------------------------------------------------------------------------
1 | import { actionize, createDeRxJSReducer } from '@derxjs/reducer';
2 | import { DeRxJSViewModel } from '@derxjs/view-model';
3 | import { map } from 'rxjs/operators';
4 | import { ListAction, ListViewModel, ListViewModelInputs } from './types';
5 |
6 | export const listViewModel$: DeRxJSViewModel<
7 | ListViewModelInputs,
8 | ListViewModel
9 | > = ({ push$, pop$, initialValue }) => {
10 | const state = createDeRxJSReducer({
11 | reducer,
12 | incomingObservables: {
13 | push: push$.pipe(
14 | map((item) => ({ item })),
15 | actionize('push')
16 | ),
17 | pop: pop$.pipe(
18 | map(() => undefined),
19 | actionize('pop')
20 | ),
21 | }, // when out of beta, this should be:
22 | // incomingObservables: [push$.pipe(actionize('push'), pop$.pipe(actionize('pop')))]
23 | effects: [],
24 | sideEffects: [],
25 | initialState: initialValue,
26 | });
27 | return state;
28 | };
29 |
30 | function reducer(state: ListViewModel, action: ListAction): ListViewModel {
31 | switch (action.type) {
32 | case 'push': {
33 | return [...state, action.item];
34 | }
35 | case 'pop': {
36 | const newState = [...state];
37 | newState.pop();
38 | return newState;
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/libs/examples/list-view-model-implementation/src/lib/list-view-model-implementation.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestScheduler } from 'rxjs/testing';
2 | import { listViewModel$ as customImpl$ } from './custom-implementation';
3 | import { listViewModel$ as reducerPackageImpl$ } from './implementation-using-reducer-package';
4 | import { DeRxJSListViewModel } from './types';
5 |
6 | const implementations: DeRxJSListViewModel[] = [
7 | customImpl$,
8 | reducerPackageImpl$,
9 | ];
10 |
11 | const testSuite = (impl$: DeRxJSListViewModel) => {
12 | describe('viewModel', () => {
13 | let testScheduler: TestScheduler;
14 |
15 | beforeEach(() => {
16 | testScheduler = new TestScheduler((actual, expected) =>
17 | expect(actual).toEqual(expected)
18 | );
19 | });
20 |
21 | test('with no user events, the list stays empty', () => {
22 | const initialValue = [];
23 | testScheduler.run(({ cold, expectObservable }) => {
24 | const push$ = cold('------');
25 | const pop$ = cold('------');
26 | const expected = cold('a-----', { a: [] });
27 | const result = impl$({
28 | push$,
29 | pop$,
30 | initialValue,
31 | });
32 | expectObservable(result).toEqual(expected);
33 | });
34 | });
35 |
36 | test('push pop push push pop push push pop pop pop pop pop', () => {
37 | const initialValue = [];
38 | testScheduler.run(({ cold, expectObservable }) => {
39 | const push$ = cold('--a-----b-c----d-e-f', {
40 | a: 'a',
41 | b: 'b',
42 | c: 'c',
43 | d: 'd',
44 | e: 'e',
45 | f: 'f',
46 | });
47 | const pop$ = cold(' -----z------z--------z-z-z-z');
48 | const expected = cold(' a-b--c--d-e-f--g-h-i-j-k-l-m', {
49 | a: [],
50 | b: ['a'],
51 | c: [],
52 | d: ['b'],
53 | e: ['b', 'c'],
54 | f: ['b'],
55 | g: ['b', 'd'],
56 | h: ['b', 'd', 'e'],
57 | i: ['b', 'd', 'e', 'f'],
58 | j: ['b', 'd', 'e'],
59 | k: ['b', 'd'],
60 | l: ['b'],
61 | m: [],
62 | });
63 | const result = impl$({
64 | push$,
65 | pop$,
66 | initialValue,
67 | });
68 | expectObservable(result).toEqual(expected);
69 | });
70 | });
71 | });
72 | };
73 |
74 | // note: the DeRxJSViewModel pattern allows us to use the same exact
75 | // test suite to test either implementation
76 | implementations.forEach(testSuite);
77 |
--------------------------------------------------------------------------------
/libs/examples/list-view-model-implementation/src/lib/types.ts:
--------------------------------------------------------------------------------
1 | import { DeRxJSViewModel } from 'packages/view-model/src/lib/view-model';
2 | import { Observable } from 'rxjs';
3 |
4 | export type DeRxJSListViewModel = DeRxJSViewModel<
5 | ListViewModelInputs,
6 | ListViewModel
7 | >;
8 |
9 | export interface ListViewModelInputs {
10 | push$: Observable;
11 | pop$: Observable;
12 | initialValue: string[];
13 | }
14 |
15 | export type ListViewModel = string[];
16 |
17 | export type ListAction = { type: 'push'; item: string } | { type: 'pop' };
18 |
--------------------------------------------------------------------------------
/libs/examples/list-view-model-implementation/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../../tsconfig.base.json",
3 | "files": [],
4 | "include": [],
5 | "references": [
6 | {
7 | "path": "./tsconfig.lib.json"
8 | },
9 | {
10 | "path": "./tsconfig.spec.json"
11 | }
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/libs/examples/list-view-model-implementation/tsconfig.lib.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../../dist/out-tsc",
5 | "declaration": true,
6 | "types": []
7 | },
8 | "include": ["**/*.ts"],
9 | "exclude": ["**/*.spec.ts", "**/*.test.ts", "jest.config.ts"]
10 | }
11 |
--------------------------------------------------------------------------------
/libs/examples/list-view-model-implementation/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../../dist/out-tsc",
5 | "module": "commonjs",
6 | "types": ["jest", "node"]
7 | },
8 | "include": [
9 | "**/*.spec.ts",
10 | "**/*.test.ts",
11 | "**/*.spec.tsx",
12 | "**/*.test.tsx",
13 | "**/*.spec.js",
14 | "**/*.test.js",
15 | "**/*.spec.jsx",
16 | "**/*.test.jsx",
17 | "**/*.d.ts",
18 | "jest.config.ts"
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/libs/examples/tic-tac-toe-view-model-implementation/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "@nrwl/js/babel",
5 | {
6 | "useBuiltIns": "usage"
7 | }
8 | ]
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/libs/examples/tic-tac-toe-view-model-implementation/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["../../../.eslintrc.json"],
3 | "ignorePatterns": ["!**/*"],
4 | "overrides": [
5 | {
6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
7 | "rules": {}
8 | },
9 | {
10 | "files": ["*.ts", "*.tsx"],
11 | "rules": {}
12 | },
13 | {
14 | "files": ["*.js", "*.jsx"],
15 | "rules": {}
16 | }
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/libs/examples/tic-tac-toe-view-model-implementation/README.md:
--------------------------------------------------------------------------------
1 | # examples-tic-tac-toe-view-model-implementation
2 |
3 | This library was generated with [Nx](https://nx.dev).
4 |
5 | ## Running unit tests
6 |
7 | Run `nx test examples-tic-tac-toe-view-model-implementation` to execute the unit tests via [Jest](https://jestjs.io).
8 |
--------------------------------------------------------------------------------
/libs/examples/tic-tac-toe-view-model-implementation/jest.config.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | export default {
3 | displayName: 'examples-tic-tac-toe-view-model-implementation',
4 | preset: '../../../jest.preset.js',
5 | globals: {},
6 | transform: {
7 | '^.+\\.[tj]sx?$': [
8 | 'ts-jest',
9 | {
10 | tsconfig: '/tsconfig.spec.json',
11 | },
12 | ],
13 | },
14 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
15 | coverageDirectory:
16 | '../../../coverage/libs/examples/tic-tac-toe-view-model-implementation',
17 | };
18 |
--------------------------------------------------------------------------------
/libs/examples/tic-tac-toe-view-model-implementation/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "examples-tic-tac-toe-view-model-implementation",
3 | "$schema": "../../../node_modules/nx/schemas/project-schema.json",
4 | "sourceRoot": "libs/examples/tic-tac-toe-view-model-implementation/src",
5 | "projectType": "library",
6 | "targets": {
7 | "lint": {
8 | "executor": "@nx/linter:eslint",
9 | "outputs": ["{options.outputFile}"],
10 | "options": {
11 | "lintFilePatterns": [
12 | "libs/examples/tic-tac-toe-view-model-implementation/**/*.ts"
13 | ]
14 | }
15 | },
16 | "test": {
17 | "executor": "@nx/jest:jest",
18 | "outputs": [
19 | "{workspaceRoot}/coverage/libs/examples/tic-tac-toe-view-model-implementation"
20 | ],
21 | "options": {
22 | "jestConfig": "libs/examples/tic-tac-toe-view-model-implementation/jest.config.ts",
23 | "passWithNoTests": true
24 | }
25 | }
26 | },
27 | "tags": []
28 | }
29 |
--------------------------------------------------------------------------------
/libs/examples/tic-tac-toe-view-model-implementation/src/index.ts:
--------------------------------------------------------------------------------
1 | export { ticTacToeViewModel$, createInitialViewModel } from './lib/view-model';
2 | export * from './lib/types';
3 | export * from './lib/random-ai';
4 |
--------------------------------------------------------------------------------
/libs/examples/tic-tac-toe-view-model-implementation/src/lib/random-ai.ts:
--------------------------------------------------------------------------------
1 | import { SpaceCoordinates } from '..';
2 | import { TicTacToeAi } from './types';
3 |
4 | export const randomAi: TicTacToeAi = ({ board }) => {
5 | const openSpots: SpaceCoordinates[] = [];
6 | for (let row = 0; row <= 2; row++) {
7 | for (let col = 0; col <= 2; col++) {
8 | if (board[row][col] === '') {
9 | openSpots.push({ row, column: col } as SpaceCoordinates);
10 | }
11 | }
12 | }
13 | const index = Math.floor(Math.random() * openSpots.length);
14 | return openSpots[index];
15 | };
16 |
--------------------------------------------------------------------------------
/libs/examples/tic-tac-toe-view-model-implementation/src/lib/types.ts:
--------------------------------------------------------------------------------
1 | import { Observable } from 'rxjs';
2 |
3 | export interface TicTacToeViewModelInputs {
4 | userSpaceClickEvents$: Observable;
5 | userResetClickEvents$: Observable;
6 | ai: TicTacToeAi;
7 | }
8 |
9 | export type TicTacToeAi = (params: AiParams) => SpaceCoordinates;
10 | interface AiParams {
11 | board: Board;
12 | aiLetter: 'o' | 'x';
13 | }
14 |
15 | export interface TicTacToeViewModel {
16 | board: Board;
17 | turn: Turn;
18 | }
19 |
20 | export type Turn =
21 | | 'your turn'
22 | | `computer's turn`
23 | | 'game over - you win'
24 | | 'game over - you lose'
25 | | `game over - it's a tie`;
26 |
27 | export type SpaceContent = 'x' | 'o' | '';
28 |
29 | export type Board = [
30 | [SpaceContent, SpaceContent, SpaceContent],
31 | [SpaceContent, SpaceContent, SpaceContent],
32 | [SpaceContent, SpaceContent, SpaceContent]
33 | ];
34 |
35 | export type BoardIndex = 0 | 1 | 2;
36 |
37 | export interface SpaceCoordinates {
38 | row: BoardIndex;
39 | column: BoardIndex;
40 | }
41 |
--------------------------------------------------------------------------------
/libs/examples/tic-tac-toe-view-model-implementation/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../../tsconfig.base.json",
3 | "files": [],
4 | "include": [],
5 | "compilerOptions": {
6 | "lib": ["esnext", "dom"]
7 | },
8 | "references": [
9 | {
10 | "path": "./tsconfig.lib.json"
11 | },
12 | {
13 | "path": "./tsconfig.spec.json"
14 | }
15 | ]
16 | }
17 |
--------------------------------------------------------------------------------
/libs/examples/tic-tac-toe-view-model-implementation/tsconfig.lib.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../../dist/out-tsc",
5 | "declaration": true,
6 | "types": []
7 | },
8 | "include": ["**/*.ts"],
9 | "exclude": ["**/*.spec.ts", "**/*.test.ts", "jest.config.ts"]
10 | }
11 |
--------------------------------------------------------------------------------
/libs/examples/tic-tac-toe-view-model-implementation/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../../dist/out-tsc",
5 | "module": "commonjs",
6 | "types": ["jest", "node"]
7 | },
8 | "include": [
9 | "**/*.spec.ts",
10 | "**/*.test.ts",
11 | "**/*.spec.tsx",
12 | "**/*.test.tsx",
13 | "**/*.spec.js",
14 | "**/*.test.js",
15 | "**/*.spec.jsx",
16 | "**/*.test.jsx",
17 | "**/*.d.ts",
18 | "jest.config.ts"
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/nx.json:
--------------------------------------------------------------------------------
1 | {
2 | "tasksRunnerOptions": {
3 | "default": {
4 | "runner": "nx-cloud",
5 | "options": {
6 | "cacheableOperations": ["build", "lint", "test", "e2e"],
7 | "accessToken": "MTc5YzBiOGQtZWExOS00YzJjLTk5YjAtYmYzYmIxNGZlZGU4fHJlYWQtd3JpdGU=",
8 | "parallel": 1
9 | }
10 | }
11 | },
12 | "extends": "nx/presets/npm.json",
13 | "npmScope": "derxjs",
14 | "affected": {
15 | "defaultBase": "master"
16 | },
17 | "workspaceLayout": {
18 | "libsDir": "libs"
19 | },
20 | "cli": {
21 | "defaultCollection": "@nx/angular"
22 | },
23 | "generators": {
24 | "@nx/angular:application": {
25 | "style": "scss",
26 | "linter": "eslint",
27 | "unitTestRunner": "jest",
28 | "e2eTestRunner": "cypress"
29 | },
30 | "@nx/angular:library": {
31 | "linter": "eslint",
32 | "unitTestRunner": "jest"
33 | },
34 | "@nx/angular:component": {
35 | "style": "scss"
36 | },
37 | "@nx/web:application": {
38 | "style": "css",
39 | "linter": "eslint",
40 | "unitTestRunner": "jest",
41 | "e2eTestRunner": "cypress"
42 | },
43 | "@nx/web:library": {
44 | "style": "css",
45 | "linter": "eslint",
46 | "unitTestRunner": "jest"
47 | },
48 | "@nx/react": {
49 | "application": {
50 | "style": "scss",
51 | "linter": "eslint",
52 | "babel": true
53 | },
54 | "component": {
55 | "style": "scss"
56 | },
57 | "library": {
58 | "style": "scss",
59 | "linter": "eslint"
60 | }
61 | }
62 | },
63 | "defaultProject": "examples-vanilla-list",
64 | "$schema": "./node_modules/nx/schemas/nx-schema.json",
65 | "targetDefaults": {
66 | "lint": {
67 | "inputs": ["default", "{workspaceRoot}/.eslintrc.json"]
68 | },
69 | "e2e": {
70 | "inputs": ["default", "^default"]
71 | },
72 | "test": {
73 | "inputs": ["default", "^default", "{workspaceRoot}/jest.preset.js"]
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "derxjs",
3 | "version": "1.4.1",
4 | "license": "MIT",
5 | "scripts": {
6 | "publish-packages": "npx ts-node tools/scripts/publish-all.ts"
7 | },
8 | "private": true,
9 | "dependencies": {
10 | "@angular/animations": "16.1.9",
11 | "@angular/common": "16.1.9",
12 | "@angular/compiler": "16.1.9",
13 | "@angular/core": "16.1.9",
14 | "@angular/forms": "16.1.9",
15 | "@angular/platform-browser": "16.1.9",
16 | "@angular/platform-browser-dynamic": "16.1.9",
17 | "@angular/router": "16.1.9",
18 | "@swc/helpers": "~0.5.0",
19 | "core-js": "^3.6.5",
20 | "react": "18.2.0",
21 | "react-dom": "18.2.0",
22 | "react-is": "18.2.0",
23 | "regenerator-runtime": "0.13.7",
24 | "rxjs": "7.8.1",
25 | "tslib": "^2.3.0",
26 | "zone.js": "0.13.1"
27 | },
28 | "devDependencies": {
29 | "@angular-devkit/build-angular": "16.1.8",
30 | "@angular-devkit/core": "16.1.8",
31 | "@angular-devkit/schematics": "16.1.8",
32 | "@angular-eslint/eslint-plugin": "16.0.3",
33 | "@angular-eslint/eslint-plugin-template": "16.0.3",
34 | "@angular-eslint/template-parser": "16.0.3",
35 | "@angular/compiler-cli": "16.1.9",
36 | "@angular/language-service": "16.1.9",
37 | "@nrwl/js": "16.6.0",
38 | "@nx/cypress": "16.6.0",
39 | "@nx/devkit": "16.6.0",
40 | "@nx/eslint-plugin": "16.6.0",
41 | "@nx/jest": "16.6.0",
42 | "@nx/js": "16.6.0",
43 | "@nx/linter": "16.6.0",
44 | "@nx/plugin": "16.6.0",
45 | "@nx/react": "16.6.0",
46 | "@nx/web": "16.6.0",
47 | "@nx/workspace": "16.6.0",
48 | "@schematics/angular": "16.1.8",
49 | "@swc-node/register": "~1.4.2",
50 | "@swc/cli": "~0.1.62",
51 | "@swc/core": "~1.3.51",
52 | "@testing-library/react": "14.0.0",
53 | "@types/jest": "29.4.4",
54 | "@types/node": "18.14.2",
55 | "@types/prompt": "^1.1.1",
56 | "@types/react": "18.2.14",
57 | "@types/react-dom": "18.2.6",
58 | "@typescript-eslint/eslint-plugin": "5.62.0",
59 | "@typescript-eslint/parser": "5.62.0",
60 | "babel-jest": "29.4.3",
61 | "cypress": "^8.3.0",
62 | "eslint": "8.15.0",
63 | "eslint-config-prettier": "8.1.0",
64 | "eslint-plugin-cypress": "^2.10.3",
65 | "eslint-plugin-import": "2.27.5",
66 | "eslint-plugin-jsx-a11y": "6.7.1",
67 | "eslint-plugin-react": "7.32.2",
68 | "eslint-plugin-react-hooks": "4.6.0",
69 | "jest": "29.4.3",
70 | "jest-environment-jsdom": "^29.4.1",
71 | "jest-preset-angular": "13.1.1",
72 | "nx": "16.6.0",
73 | "nx-cloud": "16.3.0",
74 | "prettier": "2.6.2",
75 | "prompt": "^1.2.0",
76 | "ts-jest": "29.1.1",
77 | "ts-node": "10.9.1",
78 | "typescript": "5.1.6",
79 | "webpack-sources": "^3.2.1",
80 | "@nx/angular": "16.6.0"
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/packages/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZackDeRose/derxjs/96e2497bf36e4bb110c9421b0f770361155df6d0/packages/.gitkeep
--------------------------------------------------------------------------------
/packages/react/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "@nrwl/js/babel",
5 | {
6 | "useBuiltIns": "usage"
7 | }
8 | ]
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/packages/react/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["../../.eslintrc.json"],
3 | "ignorePatterns": ["!**/*"],
4 | "overrides": [
5 | {
6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
7 | "rules": {}
8 | },
9 | {
10 | "files": ["*.ts", "*.tsx"],
11 | "rules": {}
12 | },
13 | {
14 | "files": ["*.js", "*.jsx"],
15 | "rules": {}
16 | }
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/packages/react/LICENSE.md:
--------------------------------------------------------------------------------
1 | **Note: This API is Subject to Change!**
2 | _(@derxjs/view-model is stable however)_
3 |
4 | # @derxjs/react
5 |
6 | Because your state management code should be domain-agnostic.
7 |
8 |
9 |
10 |
11 |
12 | ## Installation
13 |
14 | ```bash
15 | npm i @derxjs/react
16 | ```
17 |
18 | ## Usage
19 |
20 | - More docs to come for 1.0.0 release
21 |
22 | ## Why @derxjs
23 |
24 | ### Domain-agnostic state-management
25 |
26 | Your state management code should not depend on which framework or tools your project happens to be using at the time.
27 |
28 | `@derxjs/view-model` is all about first-principles thinking and problem-solving. The pattern enforced by this package requires you to break down the inputs of your system - regardless of scope - to some set of inputs, represented as RxJS Observables, and
29 |
30 | Future packages on the roadmap will provide utilities for implementing this pattern (`@derxjs/reducer` 👀), as well as ultilities for plugging this pattern into popular front-end frameworks (`@derxjs/react` 👀).
31 |
32 | ### Separation of concerns
33 |
34 | We solved this a long time ago. Programming to interfaces allows us to put a joint in our wrokflows that allows for parallel work to be completed by multiple developers, and lets your team play to their strengths.
35 |
36 |
37 |
38 |
39 |
40 | This allows for easy transitions into other implementations, frameworks, as well as implementing the facade, adapter, and proxy patterns from the Gang of Four.
41 |
42 | ### Complimentary to all existing state-management libraries
43 |
44 | We're not here to take a shot at the king ([👑](https://ngrx.io/)👀) - we're just here to help out where we can!
45 |
46 | The `@derxjs/view-model` package is designed to work with with any other state management frameworks that can expose state or events as an Observable, making it a great compliment to any and all existing code in your codebase.
47 |
48 | ### Future-Proof Code
49 |
50 | Domain-agnostic first-principles-based code will never go out of style 🌲.
51 |
52 | As long as JavaScript is the language of the web, your state-management code will be valid.
53 |
54 | Go ahead, change to that trendy new framework. Your @derxjs code will still work just fine :).
55 |
56 | ### Simplicity && Elegence
57 |
58 | The `DeRxJSViewModel` type is the `E = mc^2` of state management.
59 |
60 | Deceptively simple, but elegant enough to encompass any && all of your state management requirements.
61 |
62 |
63 |
64 |
65 |
66 | ### TDD made awesome with timeline testing
67 |
68 | Embrace TDD, using timeline testing to test your code with a whole new dimension of precision.
69 |
70 | On the roadmap for `@derxjs` is a timeline test generation GUI tool that will take your Typescript interface code, and allow you to "draw" hypothetical timelines of events from your inputs - specifying what the output timeline for each hypothetical should look like.
71 |
72 | This tool will generate `.spec.ts` files that you can paste directly into your repos for easy TDD, and coding the way we were meant to.
73 |
74 | ## @derxjs Roadmap
75 |
76 | - @derxjs/view-model package ✅
77 | - Article on TDD and implementing DeRxJS View Models (10/8/2021)
78 | - Article on using DeRxJS View Models in different Frameworks (10/15/2021)
79 | - @derxjs/reducer package (TBD; beta available now [you're looking at it!])
80 | - Timeline Test Code Generation Tool (TBD)
81 | - @derxjs/selector package (TBD)
82 | - @derxjs/react package (TBD)
83 | - Ai-Driven DeRxJS Code Generation (TBD)
84 |
--------------------------------------------------------------------------------
/packages/react/jest.config.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | export default {
3 | displayName: '..-packages-react',
4 | preset: '../../jest.preset.js',
5 | globals: {},
6 | transform: {
7 | '^.+\\.[tj]sx?$': [
8 | 'ts-jest',
9 | {
10 | tsconfig: '/tsconfig.spec.json',
11 | },
12 | ],
13 | },
14 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
15 | coverageDirectory: '../../coverage/libs/../packages/react',
16 | };
17 |
--------------------------------------------------------------------------------
/packages/react/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@derxjs/react",
3 | "version": "1.4.1",
4 | "main": "src/index.js",
5 | "scripts": {
6 | "test": "echo \"Error: no test specified\" && exit 1"
7 | },
8 | "repository": {
9 | "type": "git",
10 | "url": "git+https://github.com/ZackDeRose/derxjs.git"
11 | },
12 | "keywords": [
13 | "rxjs",
14 | "state-management"
15 | ],
16 | "author": "Zack DeRose",
17 | "license": "MIT",
18 | "bugs": {
19 | "url": "https://github.com/ZackDeRose/derxjs/issues"
20 | },
21 | "homepage": "https://github.com/ZackDeRose/derxjs#readme",
22 | "dependencies": {
23 | "rxjs": "^7.4.0",
24 | "typescript": "4.7.3",
25 | "@derxjs/view-model": "^1.4.0",
26 | "react": "18.2.0"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/packages/react/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react",
3 | "$schema": "../../node_modules/nx/schemas/project-schema.json",
4 | "sourceRoot": "packages/react/src",
5 | "projectType": "library",
6 | "targets": {
7 | "build": {
8 | "executor": "@nx/js:tsc",
9 | "outputs": ["{options.outputPath}"],
10 | "options": {
11 | "outputPath": "dist/packages/react",
12 | "main": "packages/react/src/index.ts",
13 | "tsConfig": "packages/react/tsconfig.lib.json",
14 | "assets": ["packages/react/*.md"],
15 | "updateBuildableProjectDepsInPackageJson": true
16 | },
17 | "outputPath": "packages/react/dist"
18 | },
19 | "lint": {
20 | "executor": "@nx/linter:eslint",
21 | "outputs": ["{options.outputFile}"],
22 | "options": {
23 | "lintFilePatterns": ["packages/react/**/*.ts"]
24 | }
25 | },
26 | "test": {
27 | "executor": "@nx/jest:jest",
28 | "outputs": ["{workspaceRoot}/coverage/packages/react"],
29 | "options": {
30 | "jestConfig": "packages/react/jest.config.ts",
31 | "passWithNoTests": true
32 | }
33 | },
34 | "publish": {
35 | "executor": "nx:run-commands",
36 | "options": {
37 | "commands": [
38 | "npx ts-node tools/scripts/bump-version.ts react {args.version}",
39 | "nx build react",
40 | "npx ts-node tools/scripts/publish.ts react"
41 | ],
42 | "parallel": false
43 | }
44 | }
45 | },
46 | "tags": []
47 | }
48 |
--------------------------------------------------------------------------------
/packages/react/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './lib/DeRxJSComponent';
2 |
--------------------------------------------------------------------------------
/packages/react/src/lib/DeRxJSComponent.tsx:
--------------------------------------------------------------------------------
1 | import { DeRxJSViewModel } from '@derxjs/view-model';
2 | import { useEffect, useRef, useState } from 'react';
3 | import { Observable, Observer } from 'rxjs';
4 |
5 | export const DeRxJSComponent = ({
6 | viewModel$,
7 | component,
8 | triggerMap,
9 | initialValue,
10 | inputs,
11 | }: DeRxJSViewModelComponentProps) => {
12 | const [state, setState] = useState(initialValue);
13 |
14 | const triggers = useRef(
15 | {} as Partial void>>
16 | );
17 |
18 | /** Subscribe */
19 | useEffect(() => {
20 | const observers: Partial>> = {};
21 | const observables: Partial>> = {};
22 | for (const [triggerKey, inputKey] of Object.entries(triggerMap)) {
23 | const inputName = inputKey as keyof InputType;
24 | const triggerName = triggerKey as keyof PropType;
25 |
26 | observables[inputName] = new Observable((observer) => {
27 | observers[triggerName] = observer;
28 | triggers.current[triggerName] = (x: any) => observer.next(x);
29 | }) as any;
30 | }
31 | const subscription = viewModel$({
32 | ...inputs,
33 | ...observables,
34 | } as InputType).subscribe(setState);
35 | return () => subscription.unsubscribe();
36 | }, []);
37 | return component({
38 | state: state || initialValue,
39 | triggers: triggers.current,
40 | });
41 | };
42 |
43 | export interface DeRxJSViewModelComponentProps<
44 | InputType,
45 | ViewModelType,
46 | PropType
47 | > {
48 | viewModel$: DeRxJSViewModel;
49 | component: (x: {
50 | state: ViewModelType;
51 | triggers: Partial void>>;
52 | }) => JSX.Element;
53 | triggerMap: Partial>;
54 | // valueMap: Partial>;
55 | initialValue: ViewModelType;
56 | inputs: Partial;
57 | }
58 |
--------------------------------------------------------------------------------
/packages/react/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "files": [],
4 | "include": [],
5 | "compilerOptions": {
6 | "module": "commonjs",
7 | "jsx": "react-jsx"
8 | },
9 | "references": [
10 | {
11 | "path": "./tsconfig.lib.json"
12 | },
13 | {
14 | "path": "./tsconfig.spec.json"
15 | }
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/packages/react/tsconfig.lib.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "declaration": true,
6 | "types": []
7 | },
8 | "include": ["**/*.ts"],
9 | "exclude": ["**/*.spec.ts", "**/*.test.ts", "jest.config.ts"]
10 | }
11 |
--------------------------------------------------------------------------------
/packages/react/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "module": "commonjs",
6 | "types": ["jest", "node"]
7 | },
8 | "include": [
9 | "**/*.spec.ts",
10 | "**/*.test.ts",
11 | "**/*.spec.tsx",
12 | "**/*.test.tsx",
13 | "**/*.spec.js",
14 | "**/*.test.js",
15 | "**/*.spec.jsx",
16 | "**/*.test.jsx",
17 | "**/*.d.ts",
18 | "jest.config.ts"
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/packages/reducer/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "@nrwl/js/babel",
5 | {
6 | "useBuiltIns": "usage"
7 | }
8 | ]
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/packages/reducer/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["../../.eslintrc.json"],
3 | "ignorePatterns": ["!**/*"],
4 | "overrides": [
5 | {
6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
7 | "rules": {}
8 | },
9 | {
10 | "files": ["*.ts", "*.tsx"],
11 | "rules": {}
12 | },
13 | {
14 | "files": ["*.js", "*.jsx"],
15 | "rules": {}
16 | }
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/packages/reducer/.gitignore:
--------------------------------------------------------------------------------
1 | dist
--------------------------------------------------------------------------------
/packages/reducer/.npmignore:
--------------------------------------------------------------------------------
1 | jest.config.js
2 | project.json
3 | .eslint.json
4 |
--------------------------------------------------------------------------------
/packages/reducer/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Zachary DeRose
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.
22 |
--------------------------------------------------------------------------------
/packages/reducer/README.md:
--------------------------------------------------------------------------------
1 | **Note: This API is Subject to Change!**
2 | _(@derxjs/view-model is stable however)_
3 |
4 | # @derxjs/reducer
5 |
6 | Because your state management code should be domain-agnostic.
7 |
8 |
9 |
10 |
11 |
12 | ## Installation
13 |
14 | ```bash
15 | npm i @derxjs/reducer
16 | ```
17 |
18 | ## Usage
19 |
20 | - [Example Tic Tac Toe App on Github (fully tested with Timeline Tests)](https://github.com/ZackDeRose/tic-tac-toe-rxjs-view-model/blob/main/libs/view-model/src/lib/using-reducer-package.ts)
21 | - See the [@derxjs/view-model package](https://github.com/ZackDeRose/view-model) for general usage of the @derxjs/view-model pattern
22 | - More docs to come for 1.0.0 release
23 |
24 | ## Why @derxjs
25 |
26 | ### Domain-agnostic state-management
27 |
28 | Your state management code should not depend on which framework or tools your project happens to be using at the time.
29 |
30 | `@derxjs/view-model` is all about first-principles thinking and problem-solving. The pattern enforced by this package requires you to break down the inputs of your system - regardless of scope - to some set of inputs, represented as RxJS Observables, and
31 |
32 | Future packages on the roadmap will provide utilities for implementing this pattern (`@derxjs/reducer` 👀), as well as ultilities for plugging this pattern into popular front-end frameworks (`@derxjs/react` 👀).
33 |
34 | ### Separation of concerns
35 |
36 | We solved this a long time ago. Programming to interfaces allows us to put a joint in our wrokflows that allows for parallel work to be completed by multiple developers, and lets your team play to their strengths.
37 |
38 |
39 |
40 |
41 |
42 | This allows for easy transitions into other implementations, frameworks, as well as implementing the facade, adapter, and proxy patterns from the Gang of Four.
43 |
44 | ### Complimentary to all existing state-management libraries
45 |
46 | We're not here to take a shot at the king ([👑](https://ngrx.io/)👀) - we're just here to help out where we can!
47 |
48 | The `@derxjs/view-model` package is designed to work with with any other state management frameworks that can expose state or events as an Observable, making it a great compliment to any and all existing code in your codebase.
49 |
50 | ### Future-Proof Code
51 |
52 | Domain-agnostic first-principles-based code will never go out of style 🌲.
53 |
54 | As long as JavaScript is the language of the web, your state-management code will be valid.
55 |
56 | Go ahead, change to that trendy new framework. Your @derxjs code will still work just fine :).
57 |
58 | ### Simplicity && Elegence
59 |
60 | The `DeRxJSViewModel` type is the `E = mc^2` of state management.
61 |
62 | Deceptively simple, but elegant enough to encompass any && all of your state management requirements.
63 |
64 |
65 |
66 |
67 |
68 | ### TDD made awesome with timeline testing
69 |
70 | Embrace TDD, using timeline testing to test your code with a whole new dimension of precision.
71 |
72 | On the roadmap for `@derxjs` is a timeline test generation GUI tool that will take your Typescript interface code, and allow you to "draw" hypothetical timelines of events from your inputs - specifying what the output timeline for each hypothetical should look like.
73 |
74 | This tool will generate `.spec.ts` files that you can paste directly into your repos for easy TDD, and coding the way we were meant to.
75 |
76 | ## @derxjs Roadmap
77 |
78 | - @derxjs/view-model package ✅
79 | - [Article on TDD and implementing DeRxJS View Models](https://dev.to/zackderose/the-derxjsviewmodel-pattern-the-e-mc-2-of-state-management-part-1-3dka) ✅
80 | - [Article on using DeRxJS View Models in different Frameworks](https://dev.to/zackderose/the-derxjsviewmodel-pattern-the-emc2-of-state-management-part-2-2i73) ✅
81 | - @derxjs/reducer package (TBD; [beta available now](https://www.npmjs.com/package/@derxjs/reducer)) 🚧
82 | - @derxjs/react package (TBD; [beta available now](https://www.npmjs.com/package/@derxjs/react)) 🚧
83 | - Timeline Test Code Generation Tool (TBD)
84 | - @derxjs/selector package (TBD)
85 | - Ai-Driven DeRxJS Code Generation (TBD)
86 |
--------------------------------------------------------------------------------
/packages/reducer/jest.config.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | export default {
3 | displayName: 'reducer',
4 | preset: '../../jest.preset.js',
5 | globals: {},
6 | transform: {
7 | '^.+\\.[tj]sx?$': [
8 | 'ts-jest',
9 | {
10 | tsconfig: '/tsconfig.spec.json',
11 | },
12 | ],
13 | },
14 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
15 | coverageDirectory: '../../coverage/packages/reducer',
16 | };
17 |
--------------------------------------------------------------------------------
/packages/reducer/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@derxjs/reducer",
3 | "version": "1.4.1",
4 | "description": "Reducer util library for DeRxJSViewModel Pattern",
5 | "main": "src/index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git+https://github.com/ZackDeRose/derxjs.git"
12 | },
13 | "keywords": [
14 | "rxjs",
15 | "state-management"
16 | ],
17 | "author": "Zack DeRose",
18 | "license": "MIT",
19 | "bugs": {
20 | "url": "https://github.com/ZackDeRose/derxjs/issues"
21 | },
22 | "homepage": "https://github.com/ZackDeRose/derxjs#readme",
23 | "dependencies": {
24 | "rxjs": "^7.4.0",
25 | "typescript": "4.7.3",
26 | "@derxjs/view-model": "^1.4.0"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/packages/reducer/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "reducer",
3 | "$schema": "../../node_modules/nx/schemas/project-schema.json",
4 | "sourceRoot": "packages/reducer/src",
5 | "projectType": "library",
6 | "targets": {
7 | "build": {
8 | "executor": "@nx/js:tsc",
9 | "outputs": ["{options.outputPath}"],
10 | "options": {
11 | "outputPath": "dist/packages/reducer",
12 | "main": "packages/reducer/src/index.ts",
13 | "tsConfig": "packages/reducer/tsconfig.lib.json",
14 | "assets": ["packages/reducer/*.md"],
15 | "updateBuildableProjectDepsInPackageJson": true
16 | }
17 | },
18 | "lint": {
19 | "executor": "@nx/linter:eslint",
20 | "outputs": ["{options.outputFile}"],
21 | "options": {
22 | "lintFilePatterns": ["packages/reducer/**/*.ts"]
23 | }
24 | },
25 | "test": {
26 | "executor": "@nx/jest:jest",
27 | "outputs": ["{workspaceRoot}/coverage/packages/reducer"],
28 | "options": {
29 | "jestConfig": "packages/reducer/jest.config.ts",
30 | "passWithNoTests": true
31 | }
32 | },
33 | "publish": {
34 | "executor": "nx:run-commands",
35 | "options": {
36 | "commands": [
37 | "npx ts-node tools/scripts/bump-version.ts reducer {args.version}",
38 | "nx build reducer",
39 | "npx ts-node tools/scripts/publish.ts reducer"
40 | ],
41 | "parallel": false
42 | }
43 | }
44 | },
45 | "tags": ["package-type:implementation"]
46 | }
47 |
--------------------------------------------------------------------------------
/packages/reducer/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './lib/reducer';
2 |
--------------------------------------------------------------------------------
/packages/reducer/src/lib/reducer.ts:
--------------------------------------------------------------------------------
1 | import { merge, Observable, Subject, Subscription } from 'rxjs';
2 | import {
3 | distinctUntilChanged,
4 | map,
5 | scan,
6 | share,
7 | startWith,
8 | } from 'rxjs/operators';
9 |
10 | // export function createDeRxJSReducer: DeRxJSViewModel;
11 | export function createDeRxJSReducer({
12 | reducer,
13 | effects,
14 | sideEffects,
15 | incomingObservables,
16 | teardownFn,
17 | initialState,
18 | }: DeRxJSReducerInputs<
19 | ViewModelType,
20 | ActionsUnionType
21 | >): Observable {
22 | const actionsSubject = new Subject();
23 | const actions$ = merge(...Object.values(incomingObservables)).pipe(
24 | share({ connector: () => actionsSubject })
25 | );
26 | const state$ = actions$.pipe(
27 | scan(reducer, initialState),
28 | startWith(initialState),
29 | distinctUntilChanged()
30 | );
31 | const subscriptions = new Subscription();
32 | if (teardownFn) {
33 | const teardown = async () => {
34 | await teardownFn();
35 | subscriptions.unsubscribe();
36 | };
37 | teardown();
38 | }
39 | for (const effect of effects) {
40 | subscriptions.add(
41 | effect(state$, actionsSubject)
42 | .pipe(share({ connector: () => actionsSubject }))
43 | .subscribe()
44 | );
45 | }
46 | for (const sideEffect of sideEffects) {
47 | subscriptions.add(sideEffect(state$, actionsSubject).subscribe());
48 | }
49 | return state$;
50 | }
51 |
52 | export interface DeRxJSReducerInputs {
53 | reducer: (a: ViewModelType, b: ActionsUnionType) => ViewModelType;
54 | effects: ((
55 | state$: Observable,
56 | actions: Observable
57 | ) => Observable)[];
58 | // | (() => ActionsUnionType)
59 | // | (() => Promise))[];
60 | sideEffects: ((
61 | state$: Observable,
62 | actions: Observable
63 | ) => Observable)[];
64 | // | (() => any) | (() => Promise);
65 | incomingObservables: {
66 | [x: string]: Observable;
67 | };
68 | teardownFn?: () => Promise;
69 | initialState: ViewModelType;
70 | }
71 |
72 | export type Action = {
73 | type: K;
74 | } & T;
75 |
76 | // export type ActionCreator = (
77 | // data?: T
78 | // ) => Action;
79 |
80 | // export function createAction(
81 | // type: K
82 | // ): ActionCreator {
83 | // const actionCreatorComposer = (x: K) => (data?: T) => ({
84 | // type: x,
85 | // ...(data || {}),
86 | // });
87 | // const actionCreator = actionCreatorComposer(type);
88 | // return actionCreator;
89 | // }
90 |
91 | export function actionize(actionName: K) {
92 | return function (inc$: Observable): Observable<{ type: K } & T> {
93 | return inc$.pipe(map((x) => ({ type: actionName, ...x })));
94 | };
95 | }
96 |
--------------------------------------------------------------------------------
/packages/reducer/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "files": [],
4 | "include": [],
5 | "references": [
6 | {
7 | "path": "./tsconfig.lib.json"
8 | },
9 | {
10 | "path": "./tsconfig.spec.json"
11 | }
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/packages/reducer/tsconfig.lib.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "declaration": true,
6 | "types": []
7 | },
8 | "include": ["**/*.ts"],
9 | "exclude": ["**/*.spec.ts", "**/*.test.ts", "jest.config.ts"]
10 | }
11 |
--------------------------------------------------------------------------------
/packages/reducer/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "module": "commonjs",
6 | "types": ["jest", "node"]
7 | },
8 | "include": [
9 | "**/*.spec.ts",
10 | "**/*.test.ts",
11 | "**/*.spec.tsx",
12 | "**/*.test.tsx",
13 | "**/*.spec.js",
14 | "**/*.test.js",
15 | "**/*.spec.jsx",
16 | "**/*.test.jsx",
17 | "**/*.d.ts",
18 | "jest.config.ts"
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/packages/view-model/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "@nrwl/js/babel",
5 | {
6 | "useBuiltIns": "usage"
7 | }
8 | ]
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/packages/view-model/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["../../.eslintrc.json"],
3 | "ignorePatterns": ["!**/*"],
4 | "overrides": [
5 | {
6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
7 | "rules": {}
8 | },
9 | {
10 | "files": ["*.ts", "*.tsx"],
11 | "rules": {}
12 | },
13 | {
14 | "files": ["*.js", "*.jsx"],
15 | "rules": {}
16 | }
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/packages/view-model/.npmignore:
--------------------------------------------------------------------------------
1 | jest.config.js
2 | project.json
3 | .eslint.json
4 |
--------------------------------------------------------------------------------
/packages/view-model/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Zachary DeRose
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.
22 |
--------------------------------------------------------------------------------
/packages/view-model/jest.config.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | export default {
3 | displayName: 'view-model',
4 | preset: '../../jest.preset.js',
5 | globals: {},
6 | transform: {
7 | '^.+\\.[tj]sx?$': [
8 | 'ts-jest',
9 | {
10 | tsconfig: '/tsconfig.spec.json',
11 | },
12 | ],
13 | },
14 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
15 | coverageDirectory: '../../coverage/packages/view-model',
16 | };
17 |
--------------------------------------------------------------------------------
/packages/view-model/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@derxjs/view-model",
3 | "version": "1.4.1",
4 | "description": "Official library for the DeRxJS Pattern",
5 | "scripts": {
6 | "test": "nx test view-model"
7 | },
8 | "repository": {
9 | "type": "git",
10 | "url": "git+https://github.com/ZackDeRose/derxjs.git"
11 | },
12 | "keywords": [
13 | "rxjs",
14 | "state",
15 | "management"
16 | ],
17 | "author": "Zack DeRose",
18 | "license": "MIT",
19 | "bugs": {
20 | "url": "https://github.com/ZackDeRose/derxjs/issues"
21 | },
22 | "homepage": "https://github.com/ZackDeRose/derxjs#readme",
23 | "types": "index.ts",
24 | "dependencies": {
25 | "rxjs": "^7.4.0"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/packages/view-model/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "view-model",
3 | "$schema": "../../node_modules/nx/schemas/project-schema.json",
4 | "sourceRoot": "packages/view-model/src",
5 | "projectType": "library",
6 | "targets": {
7 | "build": {
8 | "executor": "@nx/js:tsc",
9 | "outputs": ["{options.outputPath}"],
10 | "options": {
11 | "outputPath": "dist/packages/view-model",
12 | "main": "packages/view-model/src/index.ts",
13 | "tsConfig": "packages/view-model/tsconfig.lib.json",
14 | "assets": ["packages/view-model/*.md"],
15 | "updateBuildableProjectDepsInPackageJson": true
16 | }
17 | },
18 | "lint": {
19 | "executor": "@nx/linter:eslint",
20 | "outputs": ["{options.outputFile}"],
21 | "options": {
22 | "lintFilePatterns": ["packages/view-model/**/*.ts"]
23 | }
24 | },
25 | "test": {
26 | "executor": "@nx/jest:jest",
27 | "outputs": ["{workspaceRoot}/coverage/packages/view-model"],
28 | "options": {
29 | "jestConfig": "packages/view-model/jest.config.ts",
30 | "passWithNoTests": true
31 | }
32 | },
33 | "publish": {
34 | "executor": "nx:run-commands",
35 | "options": {
36 | "commands": [
37 | "npx ts-node tools/scripts/bump-version.ts view-model {args.version}",
38 | "nx build view-model",
39 | "npx ts-node tools/scripts/publish.ts view-model"
40 | ],
41 | "parallel": false
42 | }
43 | }
44 | },
45 | "tags": []
46 | }
47 |
--------------------------------------------------------------------------------
/packages/view-model/src/index.ts:
--------------------------------------------------------------------------------
1 | export { DeRxJSViewModel } from './lib/view-model';
2 |
--------------------------------------------------------------------------------
/packages/view-model/src/lib/view-model.ts:
--------------------------------------------------------------------------------
1 | import { Observable } from 'rxjs';
2 |
3 | export type DeRxJSViewModel = (
4 | inputs: InputType
5 | ) => Observable;
6 |
--------------------------------------------------------------------------------
/packages/view-model/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "files": [],
4 | "include": [],
5 | "references": [
6 | {
7 | "path": "./tsconfig.lib.json"
8 | },
9 | {
10 | "path": "./tsconfig.spec.json"
11 | }
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/packages/view-model/tsconfig.lib.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "declaration": true,
6 | "types": [],
7 | "forceConsistentCasingInFileNames": true,
8 | "strict": true,
9 | "noImplicitReturns": true,
10 | "noFallthroughCasesInSwitch": true
11 | },
12 | "include": ["**/*.ts"],
13 | "exclude": ["**/*.spec.ts", "**/*.test.ts", "jest.config.ts"]
14 | }
15 |
--------------------------------------------------------------------------------
/packages/view-model/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "module": "commonjs",
6 | "types": ["jest", "node"]
7 | },
8 | "include": [
9 | "**/*.spec.ts",
10 | "**/*.test.ts",
11 | "**/*.spec.tsx",
12 | "**/*.test.tsx",
13 | "**/*.spec.js",
14 | "**/*.test.js",
15 | "**/*.spec.jsx",
16 | "**/*.test.jsx",
17 | "**/*.d.ts",
18 | "jest.config.ts"
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/separation-of-tasks.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZackDeRose/derxjs/96e2497bf36e4bb110c9421b0f770361155df6d0/separation-of-tasks.png
--------------------------------------------------------------------------------
/the-derxjs-view-model-pattern.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZackDeRose/derxjs/96e2497bf36e4bb110c9421b0f770361155df6d0/the-derxjs-view-model-pattern.png
--------------------------------------------------------------------------------
/tools/scripts/bump-version.ts:
--------------------------------------------------------------------------------
1 | import { execSync } from 'child_process';
2 | import { join } from 'path';
3 |
4 | async function run() {
5 | const packageName = process.argv[2];
6 | const versionNumber = process.argv[3];
7 | bumpVersion(packageName, versionNumber);
8 | }
9 |
10 | run();
11 |
12 | function bumpVersion(packageName: string, version: string) {
13 | const cwd = join('packages', packageName);
14 | execSync(`npm version ${version}`, { cwd });
15 | }
16 |
--------------------------------------------------------------------------------
/tools/scripts/publish-all.ts:
--------------------------------------------------------------------------------
1 | import { execSync } from 'child_process';
2 | import { readFileSync } from 'fs';
3 | import prompt from 'prompt';
4 |
5 | const releaseTypes = ['major', 'minor', 'patch'] as const;
6 | const packages = ['react', 'view-model', 'reducer'] as const;
7 |
8 | async function run() {
9 | if (!areWeOnMainBranch()) {
10 | console.error('not on main branch');
11 | process.exit(1);
12 | }
13 | if (uncomittedChangesExist()) {
14 | console.error('uncommitted changes exist');
15 | process.exit(1);
16 | }
17 | const releaseType = process.argv[2];
18 | if (!isReleaseType(releaseType)) {
19 | console.error(`release type '${releaseType}' is not valid`);
20 | process.exit(1);
21 | }
22 | await promptForConfirmation();
23 | const nextVersion = getCurrentVersion(releaseType);
24 | console.error(nextVersion);
25 | publishEachPackage(nextVersion);
26 | gitCommit(nextVersion);
27 | bumpRepoVersion(nextVersion);
28 | pushToGithub();
29 | }
30 |
31 | run();
32 |
33 | function getCurrentVersion(releaseType: ReleaseType): string {
34 | const packageVersion = JSON.parse(
35 | readFileSync('package.json').toString()
36 | ).version;
37 | const [major, minor, patch] = packageVersion.split('.');
38 | return [
39 | releaseType === 'major' ? `${+major + 1}` : major,
40 | releaseType === 'major'
41 | ? 0
42 | : releaseType === 'minor'
43 | ? `${+minor + 1}`
44 | : minor,
45 | releaseType === 'patch' ? `${+patch + 1}` : 0,
46 | ].join('.');
47 | }
48 |
49 | function uncomittedChangesExist() {
50 | const statusMsg = execSync(`git status`).toString();
51 | return !statusMsg.includes('nothing to commit, working tree clean');
52 | }
53 |
54 | async function promptForConfirmation(): Promise {
55 | const { response } = await prompt.get([
56 | {
57 | name: 'response',
58 | description: 'are you sure you want to bump the version and publish?',
59 | },
60 | ]);
61 | if ((response as string).toLowerCase() === 'y') {
62 | return;
63 | ``;
64 | }
65 | console.error('User confirmation not provided');
66 | process.exit(1);
67 | }
68 |
69 | function areWeOnMainBranch(): boolean {
70 | const output = execSync('git branch').toString();
71 | return output.includes('* main');
72 | }
73 |
74 | function gitCommit(version: string) {
75 | execSync(`git commit -am "Packages bumped for release: ${version}"`);
76 | }
77 |
78 | function bumpRepoVersion(version: string) {
79 | execSync(`npm version ${version}`);
80 | }
81 |
82 | type ReleaseType = typeof releaseTypes[number];
83 | function isReleaseType(x: any): x is ReleaseType {
84 | return releaseTypes.includes(x);
85 | }
86 |
87 | function pushToGithub() {
88 | execSync('git push');
89 | }
90 |
91 | function publishEachPackage(versionNumber: string) {
92 | for (const packageName of packages) {
93 | execSync(
94 | `nx run ${packageName}:publish --args="--version=${versionNumber}"`
95 | );
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/tools/scripts/publish.ts:
--------------------------------------------------------------------------------
1 | import { execSync } from 'child_process';
2 | import { join } from 'path';
3 | import { existsSync } from 'fs';
4 |
5 | async function run() {
6 | const packageName = process.argv[2];
7 | publishToNpm(packageName);
8 | }
9 |
10 | run();
11 |
12 | function publishToNpm(packageName: string) {
13 | const cwd = join('dist', 'packages', packageName);
14 | execSync(`npm publish --access public`, { cwd });
15 | }
16 |
--------------------------------------------------------------------------------
/tools/tsconfig.tools.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.base.json",
3 | "compilerOptions": {
4 | "outDir": "../dist/out-tsc/tools",
5 | "rootDir": ".",
6 | "module": "commonjs",
7 | "target": "es5",
8 | "types": ["node"],
9 | "importHelpers": false
10 | },
11 | "include": ["**/*.ts"]
12 | }
13 |
--------------------------------------------------------------------------------
/tools/workspace-plugin/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["../../.eslintrc.json"],
3 | "ignorePatterns": ["!**/*"],
4 | "overrides": [
5 | {
6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
7 | "rules": {}
8 | },
9 | {
10 | "files": ["*.ts", "*.tsx"],
11 | "rules": {}
12 | },
13 | {
14 | "files": ["*.js", "*.jsx"],
15 | "rules": {}
16 | },
17 | {
18 | "files": ["*.json"],
19 | "parser": "jsonc-eslint-parser",
20 | "rules": {
21 | "@nx/dependency-checks": "error"
22 | }
23 | },
24 | {
25 | "files": ["./package.json", "./generators.json"],
26 | "parser": "jsonc-eslint-parser",
27 | "rules": {
28 | "@nx/nx-plugin-checks": "error"
29 | }
30 | }
31 | ]
32 | }
33 |
--------------------------------------------------------------------------------
/tools/workspace-plugin/generators.json:
--------------------------------------------------------------------------------
1 | {
2 | "generators": {
3 | "package": {
4 | "implementation": "./src/generators/package",
5 | "schema": "./src/generators/package/schema.json",
6 | "description": "Generator package"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/tools/workspace-plugin/jest.config.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | export default {
3 | displayName: 'workspace-plugin',
4 | preset: '../../jest.preset.js',
5 | transform: {
6 | '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }],
7 | },
8 | moduleFileExtensions: ['ts', 'js', 'html'],
9 | coverageDirectory: '../../coverage/tools/workspace-plugin',
10 | };
11 |
--------------------------------------------------------------------------------
/tools/workspace-plugin/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@derxjs/workspace-plugin",
3 | "version": "0.0.1",
4 | "dependencies": {
5 | "tslib": "^2.3.0"
6 | },
7 | "type": "commonjs",
8 | "main": "./src/index.js",
9 | "typings": "./src/index.d.ts",
10 | "generators": "./generators.json"
11 | }
12 |
--------------------------------------------------------------------------------
/tools/workspace-plugin/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "workspace-plugin",
3 | "$schema": "../../node_modules/nx/schemas/project-schema.json",
4 | "sourceRoot": "tools/workspace-plugin/src",
5 | "projectType": "library",
6 | "targets": {
7 | "build": {
8 | "executor": "@nx/js:tsc",
9 | "outputs": ["{options.outputPath}"],
10 | "options": {
11 | "outputPath": "dist/tools/workspace-plugin",
12 | "main": "tools/workspace-plugin/src/index.ts",
13 | "tsConfig": "tools/workspace-plugin/tsconfig.lib.json",
14 | "assets": [
15 | {
16 | "input": "./tools/workspace-plugin/src",
17 | "glob": "**/!(*.ts)",
18 | "output": "./src"
19 | },
20 | {
21 | "input": "./tools/workspace-plugin/src",
22 | "glob": "**/*.d.ts",
23 | "output": "./src"
24 | },
25 | {
26 | "input": "./tools/workspace-plugin",
27 | "glob": "generators.json",
28 | "output": "."
29 | },
30 | {
31 | "input": "./tools/workspace-plugin",
32 | "glob": "executors.json",
33 | "output": "."
34 | }
35 | ],
36 | "updateBuildableProjectDepsInPackageJson": true
37 | }
38 | },
39 | "lint": {
40 | "executor": "@nx/linter:eslint",
41 | "outputs": ["{options.outputFile}"],
42 | "options": {
43 | "lintFilePatterns": [
44 | "tools/workspace-plugin/**/*.ts",
45 | "tools/workspace-plugin/package.json",
46 | "tools/workspace-plugin/generators.json"
47 | ]
48 | }
49 | },
50 | "test": {
51 | "executor": "@nx/jest:jest",
52 | "outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
53 | "options": {
54 | "jestConfig": "tools/workspace-plugin/jest.config.ts",
55 | "passWithNoTests": true
56 | },
57 | "configurations": {
58 | "ci": {
59 | "ci": true,
60 | "codeCoverage": true
61 | }
62 | }
63 | }
64 | },
65 | "tags": []
66 | }
67 |
--------------------------------------------------------------------------------
/tools/workspace-plugin/src/generators/package/schema.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json-schema.org/schema",
3 | "cli": "nx",
4 | "$id": "package",
5 | "type": "object",
6 | "properties": {
7 | "name": {
8 | "type": "string",
9 | "description": "Library name",
10 | "$default": {
11 | "$source": "argv",
12 | "index": 0
13 | }
14 | }
15 | },
16 | "required": ["name"]
17 | }
18 |
--------------------------------------------------------------------------------
/tools/workspace-plugin/src/index.ts:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZackDeRose/derxjs/96e2497bf36e4bb110c9421b0f770361155df6d0/tools/workspace-plugin/src/index.ts
--------------------------------------------------------------------------------
/tools/workspace-plugin/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "module": "commonjs"
5 | },
6 | "files": [],
7 | "include": [],
8 | "references": [
9 | {
10 | "path": "./tsconfig.lib.json"
11 | },
12 | {
13 | "path": "./tsconfig.spec.json"
14 | }
15 | ]
16 | }
17 |
--------------------------------------------------------------------------------
/tools/workspace-plugin/tsconfig.lib.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "declaration": true,
6 | "types": ["node"]
7 | },
8 | "include": ["src/**/*.ts"],
9 | "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"]
10 | }
11 |
--------------------------------------------------------------------------------
/tools/workspace-plugin/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "module": "commonjs",
6 | "types": ["jest", "node"]
7 | },
8 | "include": [
9 | "jest.config.ts",
10 | "src/**/*.test.ts",
11 | "src/**/*.spec.ts",
12 | "src/**/*.d.ts"
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/tsconfig.base.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "rootDir": ".",
5 | "sourceMap": true,
6 | "declaration": false,
7 | "moduleResolution": "node",
8 | "emitDecoratorMetadata": true,
9 | "experimentalDecorators": true,
10 | "importHelpers": true,
11 | "target": "es2015",
12 | "module": "esnext",
13 | "lib": ["es2017", "dom"],
14 | "skipLibCheck": true,
15 | "skipDefaultLibCheck": true,
16 | "baseUrl": ".",
17 | "paths": {
18 | "@derxjs/examples/list-view-model-implementation": [
19 | "libs/examples/list-view-model-implementation/src/index.ts"
20 | ],
21 | "@derxjs/examples/tic-tac-toe-view-model-implementation": [
22 | "libs/examples/tic-tac-toe-view-model-implementation/src/index.ts"
23 | ],
24 | "@derxjs/react": ["packages/react/src/index.ts"],
25 | "@derxjs/reducer": ["packages/reducer/src/index.ts"],
26 | "@derxjs/view-model": ["packages/view-model/src/index.ts"],
27 | "@derxjs/workspace-plugin": ["tools/workspace-plugin/src/index.ts"]
28 | }
29 | },
30 | "exclude": ["node_modules", "tmp"]
31 | }
32 |
--------------------------------------------------------------------------------