From cb4dbe1c935bfccaa521e9721e6702f621e225cf Mon Sep 17 00:00:00 2001 From: cohitre Date: Fri, 23 Feb 2024 07:07:08 -0800 Subject: [PATCH] Moving package to a monorepo (#6) --- .eslintrc.json | 2 +- .github/workflows/ci.yaml | 1 + .gitignore | 2 +- README.md | 3 - package-lock.json | 128 +++++++++++------- package.json | 9 +- .npmignore => packages/document/.npmignore | 0 packages/document/LICENSE | 19 +++ packages/document/README.md | 53 ++++++++ packages/document/package-lock.json | 56 ++++++++ packages/document/package.json | 20 +++ .../src}/builders/buildBlockComponent.ts | 2 +- .../builders/buildBlockConfigurationSchema.ts | 0 .../src/builders/buildDocumentHandlers.ts | 23 ++++ packages/document/src/index.ts | 4 + {src => packages/document/src}/utils.ts | 0 .../buildBlockComponent.spec.tsx.snap | 0 .../builder/buildBlockComponent.spec.tsx | 0 .../buildBlockConfigurationSchema.spec.tsx | 0 .../builder/buildDocumentHandlers.spec.tsx | 39 ++++++ .../document/tsconfig.build.json | 0 packages/document/tsconfig.json | 9 ++ .../buildBlockConfigurationByIdSchema.ts | 16 --- src/builders/buildDocumentEditor.ts | 68 ---------- src/builders/buildDocumentReader.ts | 56 -------- src/index.tsx | 6 - ...buildBlockConfigurationByIdSchema.spec.tsx | 30 ---- tests/builder/buildDocumentEditor.spec.tsx | 114 ---------------- tests/builder/buildDocumentReader.spec.tsx | 77 ----------- tsconfig.json | 2 - 30 files changed, 310 insertions(+), 429 deletions(-) delete mode 100644 README.md rename .npmignore => packages/document/.npmignore (100%) create mode 100644 packages/document/LICENSE create mode 100644 packages/document/README.md create mode 100644 packages/document/package-lock.json create mode 100644 packages/document/package.json rename {src => packages/document/src}/builders/buildBlockComponent.ts (95%) rename {src => packages/document/src}/builders/buildBlockConfigurationSchema.ts (100%) create mode 100644 packages/document/src/builders/buildDocumentHandlers.ts create mode 100644 packages/document/src/index.ts rename {src => packages/document/src}/utils.ts (100%) rename {tests => packages/document/tests}/builder/__snapshots__/buildBlockComponent.spec.tsx.snap (100%) rename {tests => packages/document/tests}/builder/buildBlockComponent.spec.tsx (100%) rename {tests => packages/document/tests}/builder/buildBlockConfigurationSchema.spec.tsx (100%) create mode 100644 packages/document/tests/builder/buildDocumentHandlers.spec.tsx rename tsconfig.build.json => packages/document/tsconfig.build.json (100%) create mode 100644 packages/document/tsconfig.json delete mode 100644 src/builders/buildBlockConfigurationByIdSchema.ts delete mode 100644 src/builders/buildDocumentEditor.ts delete mode 100644 src/builders/buildDocumentReader.ts delete mode 100644 src/index.tsx delete mode 100644 tests/builder/buildBlockConfigurationByIdSchema.spec.tsx delete mode 100644 tests/builder/buildDocumentEditor.spec.tsx delete mode 100644 tests/builder/buildDocumentReader.spec.tsx diff --git a/.eslintrc.json b/.eslintrc.json index 0a1dc8a..7b0295e 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -32,7 +32,7 @@ }, "overrides": [ { - "files": ["tests/**/*"], + "files": ["*.spec.ts", "*.spec.tsx"], "rules": { "@typescript-eslint/no-explicit-any": ["off"] }, diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index c3a5342..6da15d5 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -21,4 +21,5 @@ jobs: - run: npm ci - run: npx eslint . - run: npx prettier . --check + - run: npx tsc --noEmit - run: npm test diff --git a/.gitignore b/.gitignore index 1d8411f..c8b1f19 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,4 @@ npm-debug.log* yarn-debug.log* yarn-error.log* -dist \ No newline at end of file +dist diff --git a/README.md b/README.md deleted file mode 100644 index 22932f7..0000000 --- a/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# @usewaypoint/document - -A library to render waypoint-style documents diff --git a/package-lock.json b/package-lock.json index 7f7d86d..a52a582 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,17 +1,13 @@ { - "name": "@usewaypoint/document", + "name": "@usewaypoint-monorepo", "version": "0.0.6", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "@usewaypoint/document", + "name": "@usewaypoint-monorepo", "version": "0.0.6", "license": "MIT", - "dependencies": { - "react": "^18.2.0", - "zod": "^3.22.4" - }, "devDependencies": { "@jest/globals": "^29.7.0", "@testing-library/react": "^14.2.1", @@ -26,7 +22,8 @@ "react-test-renderer": "^18.2.0", "ts-jest": "^29.1.2", "ts-node": "^10.9.2", - "typescript": "^5.3.3" + "typescript": "^5.3.3", + "zod": "^3.22.4" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -1523,9 +1520,9 @@ "dev": true }, "node_modules/@types/react": { - "version": "18.2.55", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.55.tgz", - "integrity": "sha512-Y2Tz5P4yz23brwm2d7jNon39qoAtMMmalOQv6+fEFt1mT+FcM3D841wDpoUvFXhaYenuROCy3FZYqdTjM7qVyA==", + "version": "18.2.57", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.57.tgz", + "integrity": "sha512-ZvQsktJgSYrQiMirAN60y4O/LRevIV8hUzSOSNB6gfR3/o3wCBFQx3sPwIYtuDMeiVgsSS3UzCV26tEzgnfvQw==", "dev": true, "dependencies": { "@types/prop-types": "*", @@ -2093,10 +2090,13 @@ "dev": true }, "node_modules/available-typed-arrays": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.6.tgz", - "integrity": "sha512-j1QzY8iPNPG4o4xmO3ptzpRxTciqD3MgEHtifP/YnJpIo58Xu+ne4BejlbkuaLfXn/nz6HFiw29bLpj2PNMdGg==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", "dev": true, + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, "engines": { "node": ">= 0.4" }, @@ -2299,15 +2299,16 @@ "dev": true }, "node_modules/call-bind": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.6.tgz", - "integrity": "sha512-Mj50FLHtlsoVfRfnHaZvyrooHcrlceNZdL/QBvJJVd9Ta55qCQK0gs4ss2oZDeV9zFCs6ewzYgVE5yfVmfFpVg==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "dev": true, "dependencies": { + "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.3", - "set-function-length": "^1.2.0" + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" }, "engines": { "node": ">= 0.4" @@ -2642,18 +2643,20 @@ } }, "node_modules/define-data-property": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.2.tgz", - "integrity": "sha512-SRtsSqsDbgpJBbW3pABMCOt6rQyeM8s8RiyeSN8jYG8sYmt/kGJejbydttUsnDs1tadr19tvhT4ShwMyoqAm4g==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dev": true, "dependencies": { + "es-define-property": "^1.0.0", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.2", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.1" + "gopd": "^1.0.1" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/define-properties": { @@ -2797,6 +2800,18 @@ "is-arrayish": "^0.2.1" } }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-errors": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", @@ -3445,21 +3460,21 @@ } }, "node_modules/has-property-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", - "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dev": true, "dependencies": { - "get-intrinsic": "^1.2.2" + "es-define-property": "^1.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", "dev": true, "engines": { "node": ">= 0.4" @@ -3874,12 +3889,15 @@ } }, "node_modules/is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", + "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2" + "call-bind": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -4678,7 +4696,8 @@ "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true }, "node_modules/js-yaml": { "version": "4.1.0", @@ -4862,6 +4881,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, @@ -5387,6 +5407,15 @@ "node": ">=8" } }, + "node_modules/possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -5511,6 +5540,8 @@ "version": "18.2.0", "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "dev": true, + "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -5572,14 +5603,15 @@ "dev": true }, "node_modules/regexp.prototype.flags": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", - "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==", + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", + "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "set-function-name": "^2.0.0" + "call-bind": "^1.0.6", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "set-function-name": "^2.0.1" }, "engines": { "node": ">= 0.4" @@ -5761,14 +5793,15 @@ } }, "node_modules/set-function-name": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", - "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", "dev": true, "dependencies": { - "define-data-property": "^1.0.1", + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.0" + "has-property-descriptors": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -6594,6 +6627,7 @@ "version": "3.22.4", "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz", "integrity": "sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==", + "dev": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/package.json b/package.json index dc18575..098de06 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,6 @@ { - "name": "@usewaypoint/document", + "name": "@usewaypoint-monorepo", "version": "0.0.6", - "description": "Tools to render waypoint-style documents.", "main": "dist/index.js", "types": "dist/index.d.ts", "target": "ES2022", @@ -10,7 +9,6 @@ ], "scripts": { "build": "npx tsc", - "prepublish": "npx tsc --project tsconfig.build.json", "test": "npx jest" }, "author": "carlos@usewaypoint.com", @@ -29,10 +27,7 @@ "react-test-renderer": "^18.2.0", "ts-jest": "^29.1.2", "ts-node": "^10.9.2", - "typescript": "^5.3.3" - }, - "dependencies": { - "react": "^18.2.0", + "typescript": "^5.3.3", "zod": "^3.22.4" } } diff --git a/.npmignore b/packages/document/.npmignore similarity index 100% rename from .npmignore rename to packages/document/.npmignore diff --git a/packages/document/LICENSE b/packages/document/LICENSE new file mode 100644 index 0000000..5a09b83 --- /dev/null +++ b/packages/document/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2024 Carlos Rodriguez-Rosario + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/document/README.md b/packages/document/README.md new file mode 100644 index 0000000..a0ae7b0 --- /dev/null +++ b/packages/document/README.md @@ -0,0 +1,53 @@ +# @usewaypoint/document + +This is the core library used to build the email messages at [Waypoint](https://www.usewaypoint.com). It is non-opinionated and light on dependencies so that it can be used to compose complex documents. + +> [!WARNING] +> This library is still under development and the final interface is subject +> to change + +## Installation + +**Installation with npm** + +``` +npm install --save @usewaypoint/document +``` + +## Usage + +The root of the library is the `DocumentBlocksDictionary` dictionary. This is a mapping of block names to an object with a zod schema and a corresponding React Component. + +``` +const dictionary = { + Alert: { + schema: z.object({ + message: z.string(), + }), + Component: ({ message }: { message: string }) => { + return
{message.toUpperCase()}
+ } + } +} +``` + +This dictionary object is passed as an argument to the builder functions. + +### `buildBlockComponent` + +``` +const Block = buildBlockComponent(dictionary); + + +``` + +### `buildBlockConfigurationSchema` + +``` +const Schema = buildBlockConfigurationSchema(dictionary); + +const parsedData = Schema.safeParse({ + type: 'Alert', + data: { message: 'Hello World' }, +}); +``` diff --git a/packages/document/package-lock.json b/packages/document/package-lock.json new file mode 100644 index 0000000..dd77c2a --- /dev/null +++ b/packages/document/package-lock.json @@ -0,0 +1,56 @@ +{ + "name": "@usewaypoint/document", + "version": "0.0.6", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@usewaypoint/document", + "version": "0.0.6", + "license": "MIT", + "peerDependencies": { + "react": "^16 || ^17 || ^18", + "zod": "^1 || ^2 || ^3" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "peer": true + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "peer": true, + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/react": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/zod": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz", + "integrity": "sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==", + "peer": true, + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/packages/document/package.json b/packages/document/package.json new file mode 100644 index 0000000..136815d --- /dev/null +++ b/packages/document/package.json @@ -0,0 +1,20 @@ +{ + "name": "@usewaypoint/document", + "version": "0.0.6", + "description": "Tools to render waypoint-style documents (core package)", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "target": "ES2022", + "files": [ + "dist" + ], + "scripts": { + "build": "npx tsc" + }, + "author": "carlos@usewaypoint.com", + "license": "MIT", + "peerDependencies": { + "react": "^16 || ^17 || ^18", + "zod": "^1 || ^2 || ^3" + } +} diff --git a/src/builders/buildBlockComponent.ts b/packages/document/src/builders/buildBlockComponent.ts similarity index 95% rename from src/builders/buildBlockComponent.ts rename to packages/document/src/builders/buildBlockComponent.ts index 3a5cb14..40879eb 100644 --- a/src/builders/buildBlockComponent.ts +++ b/packages/document/src/builders/buildBlockComponent.ts @@ -7,7 +7,7 @@ import { BaseZodDictionary, BlockConfiguration, DocumentBlocksDictionary } from * @returns React component that can render a BlockConfiguration that is compatible with blocks */ export default function buildBlockComponent(blocks: DocumentBlocksDictionary) { - return function BlockComponent({ type, data }: BlockConfiguration): React.ReactNode { + return function BlockComponent({ type, data }: BlockConfiguration) { return React.createElement(blocks[type].Component, data); }; } diff --git a/src/builders/buildBlockConfigurationSchema.ts b/packages/document/src/builders/buildBlockConfigurationSchema.ts similarity index 100% rename from src/builders/buildBlockConfigurationSchema.ts rename to packages/document/src/builders/buildBlockConfigurationSchema.ts diff --git a/packages/document/src/builders/buildDocumentHandlers.ts b/packages/document/src/builders/buildDocumentHandlers.ts new file mode 100644 index 0000000..810c3b9 --- /dev/null +++ b/packages/document/src/builders/buildDocumentHandlers.ts @@ -0,0 +1,23 @@ +import { BaseZodDictionary, DocumentBlocksDictionary } from '../utils'; + +import buildBlockComponent from './buildBlockComponent'; +import buildBlockConfigurationSchema from './buildBlockConfigurationSchema'; + +/** + * @typedef {Object} DocumentHandlers + * @property Schema - zod schema for a Document block + * @property Block - React component that can render a BlockConfiguration that is compatible with blocks + */ + +/** + * Shorthand function to create a Block and Schema in a single call + * + * @param blocks Main DocumentBlocksDictionary + * @returns {DocumentHandlers} + */ +export default function buildDocumentHandlers(blocks: DocumentBlocksDictionary) { + return { + Block: buildBlockComponent(blocks), + Schema: buildBlockConfigurationSchema(blocks), + }; +} diff --git a/packages/document/src/index.ts b/packages/document/src/index.ts new file mode 100644 index 0000000..4345b20 --- /dev/null +++ b/packages/document/src/index.ts @@ -0,0 +1,4 @@ +export { default as buildBlockComponent } from './builders/buildBlockComponent'; +export { default as buildBlockConfigurationSchema } from './builders/buildBlockConfigurationSchema'; +export { default as buildDocumentHandlers } from './builders/buildDocumentHandlers'; +export { BlockConfiguration, DocumentBlocksDictionary } from './utils'; diff --git a/src/utils.ts b/packages/document/src/utils.ts similarity index 100% rename from src/utils.ts rename to packages/document/src/utils.ts diff --git a/tests/builder/__snapshots__/buildBlockComponent.spec.tsx.snap b/packages/document/tests/builder/__snapshots__/buildBlockComponent.spec.tsx.snap similarity index 100% rename from tests/builder/__snapshots__/buildBlockComponent.spec.tsx.snap rename to packages/document/tests/builder/__snapshots__/buildBlockComponent.spec.tsx.snap diff --git a/tests/builder/buildBlockComponent.spec.tsx b/packages/document/tests/builder/buildBlockComponent.spec.tsx similarity index 100% rename from tests/builder/buildBlockComponent.spec.tsx rename to packages/document/tests/builder/buildBlockComponent.spec.tsx diff --git a/tests/builder/buildBlockConfigurationSchema.spec.tsx b/packages/document/tests/builder/buildBlockConfigurationSchema.spec.tsx similarity index 100% rename from tests/builder/buildBlockConfigurationSchema.spec.tsx rename to packages/document/tests/builder/buildBlockConfigurationSchema.spec.tsx diff --git a/packages/document/tests/builder/buildDocumentHandlers.spec.tsx b/packages/document/tests/builder/buildDocumentHandlers.spec.tsx new file mode 100644 index 0000000..e8659bf --- /dev/null +++ b/packages/document/tests/builder/buildDocumentHandlers.spec.tsx @@ -0,0 +1,39 @@ +import React from 'react'; +import { z } from 'zod'; + +import { render } from '@testing-library/react'; + +import buildDocumentHandlers from '../../src/builders/buildDocumentHandlers'; + +describe('builders/buildDocumentHandlers', () => { + const { Schema, Block } = buildDocumentHandlers({ + SampleBlock: { + schema: z.object({ text: z.string() }), + Component: ({ text }) =>
{text.toUpperCase()}
, + }, + }); + + describe('#Block', () => { + it('renders the component from the BlocksConfiguration', () => { + expect( + render().queryAllByText('TEST TEXT!') + ).toHaveLength(1); + }); + }); + + describe('#Schema', () => { + it('builds a BlockConfiguration schema with an id, data, and type', () => { + const parsedData = Schema.safeParse({ + type: 'SampleBlock', + data: { text: 'Test text!' }, + }); + expect(parsedData).toEqual({ + success: true, + data: { + type: 'SampleBlock', + data: { text: 'Test text!' }, + }, + }); + }); + }); +}); diff --git a/tsconfig.build.json b/packages/document/tsconfig.build.json similarity index 100% rename from tsconfig.build.json rename to packages/document/tsconfig.build.json diff --git a/packages/document/tsconfig.json b/packages/document/tsconfig.json new file mode 100644 index 0000000..c447dd3 --- /dev/null +++ b/packages/document/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "target": "es2015", + "module": "esnext", + "outDir": "dist" + }, + "exclude": ["node_modules", "dist"] +} diff --git a/src/builders/buildBlockConfigurationByIdSchema.ts b/src/builders/buildBlockConfigurationByIdSchema.ts deleted file mode 100644 index 40a4799..0000000 --- a/src/builders/buildBlockConfigurationByIdSchema.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { z } from 'zod'; - -import { BaseZodDictionary, DocumentBlocksDictionary } from '../utils'; - -import buildBlockConfigurationSchema from './buildBlockConfigurationSchema'; - -/** - * @param blocks Main DocumentBlocksDictionary - * @returns zod schema that can parse arbitrary objects into { [id]: BlockConfiguration } pairs - */ -export default function buildBlockConfigurationByIdSchema( - blocks: DocumentBlocksDictionary -) { - const schema = buildBlockConfigurationSchema(blocks); - return z.record(z.string(), schema); -} diff --git a/src/builders/buildDocumentEditor.ts b/src/builders/buildDocumentEditor.ts deleted file mode 100644 index 65b4d17..0000000 --- a/src/builders/buildDocumentEditor.ts +++ /dev/null @@ -1,68 +0,0 @@ -import React, { createContext, useContext, useMemo, useState } from 'react'; -import { z } from 'zod'; - -import { BaseZodDictionary, BlockNotFoundError, DocumentBlocksDictionary } from '../utils'; - -import buildBlockComponent from './buildBlockComponent'; -import buildBlockConfigurationByIdSchema from './buildBlockConfigurationByIdSchema'; - -/** - * @typedef {Object} DocumentEditor - * @property DocumentEditorProvider - Entry point to the DocumentEditor - * @property DocumentConfigurationSchema - zod schema compatible with the value that DocumentReaderProvider expects - * @property Block - Component to render a block given an id - * @property useDocumentState - Hook that returns the current DocumentState and a setter - * @property useBlockState - Hook that returns the Block value and setter given an id - */ - -/** - * @param {DocumentBlocksDictionary} blocks root configuration - * @returns {DocumentEditor} - */ -export default function buildDocumentEditor(blocks: DocumentBlocksDictionary) { - const schema = buildBlockConfigurationByIdSchema(blocks); - const BlockComponent = buildBlockComponent(blocks); - - type TValue = z.infer; - type TDocumentContextState = [value: TValue, setValue: (v: TValue) => void]; - - const Context = createContext([{}, () => {}]); - - type TProviderProps = { - value: z.infer; - children?: Parameters[0]['children']; - }; - - const useDocumentState = () => useContext(Context); - const useBlockState = (id: string | null | undefined) => { - const [value, setValue] = useDocumentState(); - return useMemo(() => { - if (id === null || id === undefined) { - return null; - } - return [ - value[id], - (block: TValue[string]) => { - setValue({ ...value, [id]: block }); - }, - ] as const; - }, [value, setValue, id]); - }; - return { - useDocumentState, - useBlockState, - DocumentConfigurationSchema: schema, - Block: ({ id }: { id: string }) => { - const state = useBlockState(id); - if (state === null || !state[0]) { - throw new BlockNotFoundError(id); - } - const { type, data } = state[0]; - return React.createElement(BlockComponent, { type, data }); - }, - DocumentEditorProvider: ({ value, children }: TProviderProps) => { - const state = useState(value); - return React.createElement(Context.Provider, { value: state, children }); - }, - }; -} diff --git a/src/builders/buildDocumentReader.ts b/src/builders/buildDocumentReader.ts deleted file mode 100644 index 7992d20..0000000 --- a/src/builders/buildDocumentReader.ts +++ /dev/null @@ -1,56 +0,0 @@ -import React, { createContext, useContext, useMemo } from 'react'; -import { z } from 'zod'; - -import { BaseZodDictionary, BlockNotFoundError, DocumentBlocksDictionary } from '../utils'; - -import buildBlockComponent from './buildBlockComponent'; -import buildBlockConfigurationByIdSchema from './buildBlockConfigurationByIdSchema'; - -/** - * @typedef {Object} DocumentReader - * @property DocumentReaderProvider - Entry point to the DocumentReader - * @property DocumentConfigurationSchema - zod schema compatible with the value that DocumentReaderProvider expects - * @property Block - Component to render a block given an id - * @property useDocument - Hook that returns the current Document - * @property useBlock - Hook that returns the block given an id - */ - -/** - * @param {DocumentBlocksDictionary} blocks root configuration - * @returns {DocumentReader} - */ -export default function buildDocumentReader(blocks: DocumentBlocksDictionary) { - const schema = buildBlockConfigurationByIdSchema(blocks); - const BlockComponent = buildBlockComponent(blocks); - - type TValue = z.infer; - type TDocumentContextState = { value: TValue }; - - const Context = createContext({ value: {} }); - - type TProviderProps = { - value: z.infer; - children?: Parameters[0]['children']; - }; - - const useDocument = () => useContext(Context).value; - const useBlock = (id: string) => useDocument()[id]; - - return { - useDocument, - useBlock, - DocumentConfigurationSchema: schema, - Block: ({ id }: { id: string }) => { - const block = useBlock(id); - if (!block) { - throw new BlockNotFoundError(id); - } - const { type, data } = block; - return React.createElement(BlockComponent, { type, data }); - }, - DocumentReaderProvider: ({ value, children }: TProviderProps) => { - const v = useMemo(() => ({ value }), [value]); - return React.createElement(Context.Provider, { value: v, children }); - }, - }; -} diff --git a/src/index.tsx b/src/index.tsx deleted file mode 100644 index 3f73871..0000000 --- a/src/index.tsx +++ /dev/null @@ -1,6 +0,0 @@ -export { default as buildBlockComponent } from './builders/buildBlockComponent'; -export { default as buildBlockConfigurationSchema } from './builders/buildBlockConfigurationSchema'; -export { default as buildBlockConfigurationByIdSchema } from './builders/buildBlockConfigurationByIdSchema'; -export { default as buildDocumentReader } from './builders/buildDocumentReader'; -export { default as buildDocumentEditor } from './builders/buildDocumentEditor'; -export { BlockConfiguration, DocumentBlocksDictionary } from './utils'; diff --git a/tests/builder/buildBlockConfigurationByIdSchema.spec.tsx b/tests/builder/buildBlockConfigurationByIdSchema.spec.tsx deleted file mode 100644 index 1f2d8d7..0000000 --- a/tests/builder/buildBlockConfigurationByIdSchema.spec.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import React from 'react'; -import { z } from 'zod'; - -import buildBlockConfigurationByIdSchema from '../../src/builders/buildBlockConfigurationByIdSchema'; - -describe('builders/buildBlockConfigurationByIdSchema', () => { - it('parses an object with id as keys and BlockConfiguration as body', () => { - const schema = buildBlockConfigurationByIdSchema({ - SampleBlock: { - schema: z.object({ text: z.string() }), - Component: ({ text }) =>
{text.toUpperCase()}
, - }, - }); - const parsedData = schema.safeParse({ - 'my id': { - type: 'SampleBlock', - data: { text: 'Test text!' }, - }, - }); - expect(parsedData).toEqual({ - success: true, - data: { - 'my id': { - type: 'SampleBlock', - data: { text: 'Test text!' }, - }, - }, - }); - }); -}); diff --git a/tests/builder/buildDocumentEditor.spec.tsx b/tests/builder/buildDocumentEditor.spec.tsx deleted file mode 100644 index 4fd14fb..0000000 --- a/tests/builder/buildDocumentEditor.spec.tsx +++ /dev/null @@ -1,114 +0,0 @@ -import React from 'react'; -import { z } from 'zod'; - -import { act, render } from '@testing-library/react'; - -import buildDocumentEditor from '../../src/builders/buildDocumentEditor'; - -describe('builders/buildDocumentEditor', () => { - const { useDocumentState, useBlockState, Block, DocumentEditorProvider } = buildDocumentEditor({ - SampleBlock: { - schema: z.object({ text: z.string() }), - Component: ({ text }) =>
{text.toUpperCase()}
, - }, - }); - - const SAMPLE_DATA = { - 'my id': { - id: 'my id', - type: 'SampleBlock' as const, - data: { text: 'Test text!' }, - }, - }; - - describe('#useDocumentState', () => { - it('returns a getter and a setter tuple', () => { - let tuple: any; - const ViewBlockConfig = () => { - tuple = useDocumentState(); - return
{JSON.stringify(tuple[0])}
; - }; - - const NODE = ( - - - - ); - const { rerender } = render(NODE); - expect(tuple[0]).toEqual({ - 'my id': { - id: 'my id', - type: 'SampleBlock', - data: { text: 'Test text!' }, - }, - }); - - act(() => { - tuple[1]({ - 'another id': { - id: 'another id', - type: 'SampleBlock', - data: { text: 'changed text?' }, - }, - }); - }); - - rerender(NODE); - expect(tuple[0]).toEqual({ - 'another id': { - id: 'another id', - type: 'SampleBlock' as const, - data: { text: 'changed text?' }, - }, - }); - }); - }); - - describe('#useBlockState', () => { - it('returns a getter and a setter tuple', () => { - let tuple: any; - const ViewBlockConfig = () => { - tuple = useBlockState('my id'); - return
{JSON.stringify(tuple[0])}
; - }; - - const NODE = ( - - - - ); - const { rerender } = render(NODE); - expect(tuple[0]).toEqual({ - id: 'my id', - type: 'SampleBlock', - data: { text: 'Test text!' }, - }); - - act(() => { - tuple[1]({ - ...tuple[0], - data: { text: 'changed text?' }, - }); - }); - - rerender(NODE); - expect(tuple[0]).toEqual({ - id: 'my id', - type: 'SampleBlock', - data: { text: 'changed text?' }, - }); - }); - }); - - describe('#Block', () => { - it('renders the component from the BlocksConfiguration', () => { - expect( - render( - - - - ).queryAllByText('TEST TEXT!') - ).toHaveLength(1); - }); - }); -}); diff --git a/tests/builder/buildDocumentReader.spec.tsx b/tests/builder/buildDocumentReader.spec.tsx deleted file mode 100644 index 35d7dee..0000000 --- a/tests/builder/buildDocumentReader.spec.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import React from 'react'; -import { z } from 'zod'; - -import { render } from '@testing-library/react'; - -import buildDocumentReader from '../../src/builders/buildDocumentReader'; - -describe('builders/buildDocumentReader', () => { - const { DocumentReaderProvider, Block, useBlock, useDocument } = buildDocumentReader({ - SampleBlock: { - schema: z.object({ text: z.string() }), - Component: ({ text }) =>
{text.toUpperCase()}
, - }, - }); - - const SAMPLE_DATA = { - 'my id': { - id: 'my id', - type: 'SampleBlock' as const, - data: { text: 'Test text!' }, - }, - }; - - describe('#useDocument', () => { - it('gets the configurations dictionary', () => { - let RESULT; - const ViewBlockConfig = () => { - RESULT = useDocument(); - return
{JSON.stringify(RESULT)}
; - }; - render( - - - - ); - expect(RESULT).toEqual({ - 'my id': { - id: 'my id', - type: 'SampleBlock', - data: { text: 'Test text!' }, - }, - }); - }); - }); - - describe('#useBlock', () => { - it('gets the value given an id', () => { - let RESULT; - const ViewBlockConfig = () => { - RESULT = useBlock('my id'); - return
{JSON.stringify(RESULT)}
; - }; - render( - - - - ); - expect(RESULT).toEqual({ - id: 'my id', - type: 'SampleBlock', - data: { text: 'Test text!' }, - }); - }); - }); - - describe('#Block', () => { - it('renders the component from the BlocksConfiguration', () => { - expect( - render( - - - - ).queryAllByText('TEST TEXT!') - ).toHaveLength(1); - }); - }); -}); diff --git a/tsconfig.json b/tsconfig.json index 48113e0..f2ad8c5 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,7 +5,6 @@ "lib": [], "moduleResolution": "node", "jsx": "react", - "outDir": "dist", "strict": true, "sourceMap": true, "esModuleInterop": true, @@ -15,6 +14,5 @@ "noImplicitReturns": true, "noFallthroughCasesInSwitch": true }, - "include": ["jest.config.ts", "src/**/*.ts", "src/**/*.tsx", "tests/**/*.ts", "tests/**/*.tsx"], "exclude": ["node_modules", "dist"] }