├── packages
├── .gitkeep
├── dom
│ ├── src
│ │ ├── index.ts
│ │ └── lib
│ │ │ ├── utilities
│ │ │ ├── update-html.ts
│ │ │ ├── domhandler.ts
│ │ │ └── index.ts
│ │ │ ├── serializers.ts
│ │ │ ├── dom.ts
│ │ │ ├── config
│ │ │ ├── slateDemo.ts
│ │ │ ├── types.ts
│ │ │ ├── payload.ts
│ │ │ └── default.ts
│ │ │ └── dom.spec.ts
│ ├── docs
│ │ └── nx.md
│ ├── README.md
│ ├── package.json
│ ├── tsconfig.lib.json
│ ├── tsconfig.spec.json
│ ├── jest.config.ts
│ ├── .eslintrc.json
│ ├── tsconfig.json
│ ├── CHANGELOG.md
│ └── project.json
├── html
│ ├── src
│ │ ├── index.ts
│ │ └── lib
│ │ │ ├── serializers
│ │ │ ├── slateToHtml
│ │ │ │ ├── index.ts
│ │ │ │ ├── slateDemo.spec.ts
│ │ │ │ └── payload.spec.ts
│ │ │ ├── htmlToSlate
│ │ │ │ ├── config
│ │ │ │ │ ├── payload.ts
│ │ │ │ │ ├── slateDemo.ts
│ │ │ │ │ ├── types.ts
│ │ │ │ │ └── default.ts
│ │ │ │ ├── whitespace.ts
│ │ │ │ ├── whitespace.spec.ts
│ │ │ │ ├── slateDemo.spec.ts
│ │ │ │ └── index.spec.ts
│ │ │ └── snapshots
│ │ │ │ └── elementTags.spec.ts
│ │ │ ├── tests
│ │ │ ├── slateToHtml
│ │ │ │ └── configuration
│ │ │ │ │ ├── markMap.spec.ts
│ │ │ │ │ ├── markTransforms.spec.ts
│ │ │ │ │ ├── elementTransforms.spec.ts
│ │ │ │ │ └── elementMap.spec.ts
│ │ │ └── htmlToSlate
│ │ │ │ └── configuration
│ │ │ │ ├── textTagsVselementTags.spec.ts
│ │ │ │ ├── textTags.spec.ts
│ │ │ │ ├── elementAttributeTransform.spec.ts
│ │ │ │ ├── convertBrToLineBreak.spec.ts
│ │ │ │ └── elementTags.spec.ts
│ │ │ ├── html.ts
│ │ │ └── utilities
│ │ │ └── blocks.ts
│ ├── package.json
│ ├── docs
│ │ └── nx.md
│ ├── tsconfig.lib.json
│ ├── tsconfig.spec.json
│ ├── jest.config.ts
│ ├── .eslintrc.json
│ ├── tsconfig.json
│ ├── CHANGELOG.md
│ └── project.json
├── react
│ ├── src
│ │ ├── index.ts
│ │ └── lib
│ │ │ ├── config
│ │ │ ├── slateDemo.tsx
│ │ │ ├── types.ts
│ │ │ ├── default.tsx
│ │ │ └── payload.tsx
│ │ │ ├── react.spec.tsx
│ │ │ ├── react.tsx
│ │ │ ├── serializers.tsx
│ │ │ └── __tests__
│ │ │ ├── default.spec.tsx
│ │ │ └── payload-crowdin-sync-dev
│ │ │ └── payload-internal-link.spec.tsx
│ ├── package.json
│ ├── README.md
│ ├── .babelrc
│ ├── CHANGELOG.md
│ ├── .eslintrc.json
│ ├── jest.config.ts
│ ├── tsconfig.json
│ ├── tsconfig.spec.json
│ ├── tsconfig.lib.json
│ └── project.json
├── tests
│ ├── src
│ │ ├── index.ts
│ │ └── lib
│ │ │ ├── react
│ │ │ ├── testComponents
│ │ │ │ └── Button.tsx
│ │ │ ├── __snapshots__
│ │ │ │ ├── style-object.spec.tsx.snap
│ │ │ │ └── combined.spec.tsx.snap
│ │ │ ├── style-object.spec.tsx
│ │ │ ├── combined.spec.tsx
│ │ │ ├── combined-payload.spec.tsx
│ │ │ └── components.spec.tsx
│ │ │ ├── tests.ts
│ │ │ ├── fixtures
│ │ │ ├── styles-in-leaf.ts
│ │ │ ├── style-object.ts
│ │ │ ├── textTags.ts
│ │ │ └── elementTags.ts
│ │ │ ├── template
│ │ │ ├── combined-payload.spec.ts
│ │ │ └── __snapshots__
│ │ │ │ └── combined-payload.spec.ts.snap
│ │ │ └── html
│ │ │ ├── sameHtmlSlateBothWays.spec.ts
│ │ │ ├── withStylesInLeaf.spec.ts
│ │ │ ├── expectedDifferencesHtmlSlateBothWays.spec.ts
│ │ │ └── withStyleObject.spec.ts
│ ├── package.json
│ ├── .babelrc
│ ├── README.md
│ ├── tsconfig.lib.json
│ ├── tsconfig.spec.json
│ ├── CHANGELOG.md
│ ├── .eslintrc.json
│ ├── jest.config.ts
│ ├── tsconfig.json
│ └── project.json
├── template
│ ├── src
│ │ ├── index.ts
│ │ └── lib
│ │ │ ├── config
│ │ │ ├── default.ts
│ │ │ ├── slateDemo.ts
│ │ │ ├── payload.ts
│ │ │ └── types.ts
│ │ │ ├── template.ts
│ │ │ ├── serializers.ts
│ │ │ └── __tests__
│ │ │ └── default.spec.ts
│ ├── package.json
│ ├── CHANGELOG.md
│ ├── docs
│ │ └── nx.md
│ ├── tsconfig.lib.json
│ ├── tsconfig.spec.json
│ ├── jest.config.ts
│ ├── .eslintrc.json
│ ├── tsconfig.json
│ ├── project.json
│ └── README.md
├── slate-serializers
│ ├── src
│ │ ├── index.ts
│ │ └── lib
│ │ │ ├── slate-serializers.ts
│ │ │ └── slate-serializers.spec.ts
│ ├── package.json
│ ├── CHANGELOG.md
│ ├── tsconfig.lib.json
│ ├── docs
│ │ └── nx.md
│ ├── tsconfig.spec.json
│ ├── .eslintrc.json
│ ├── jest.config.ts
│ ├── tsconfig.json
│ ├── README.md
│ └── project.json
└── utilities
│ ├── src
│ ├── index.ts
│ └── lib
│ │ ├── style-object.ts
│ │ └── utilities.ts
│ ├── package.json
│ ├── README.md
│ ├── tsconfig.lib.json
│ ├── tsconfig.spec.json
│ ├── jest.config.ts
│ ├── .eslintrc.json
│ ├── tsconfig.json
│ ├── CHANGELOG.md
│ └── project.json
├── .eslintignore
├── .prettierrc
├── .npmrc
├── .commitlintrc.json
├── jest.preset.js
├── .prettierignore
├── jest.config.ts
├── .vscode
└── extensions.json
├── .env
├── .release-please-manifest.json
├── .editorconfig
├── tools
├── tsconfig.tools.json
├── sync-versions.js
└── scripts
│ └── publish.mjs
├── .github
└── workflows
│ ├── release-please.js.yml
│ └── node.js.yml
├── project.json
├── .verdaccio
└── config.yml
├── .gitignore
├── release-please-config.json
├── tsconfig.base.json
├── LICENSE.md
├── .eslintrc.json
├── nx.json
├── docs
├── nx.md
└── config
│ ├── slateToDom.md
│ └── htmlToSlate.md
├── README.md
├── migrations.json
└── package.json
/packages/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true
3 | }
4 |
--------------------------------------------------------------------------------
/packages/dom/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './lib/dom';
2 |
--------------------------------------------------------------------------------
/packages/html/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './lib/html';
2 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | //registry.npmjs.org/:_authToken=${SS_NPM_TOKEN}
2 |
--------------------------------------------------------------------------------
/packages/react/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './lib/react';
2 |
--------------------------------------------------------------------------------
/packages/tests/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './lib/tests';
2 |
--------------------------------------------------------------------------------
/packages/template/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './lib/template';
2 |
--------------------------------------------------------------------------------
/packages/slate-serializers/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './lib/slate-serializers';
2 |
--------------------------------------------------------------------------------
/.commitlintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["@commitlint/config-conventional"],
3 | "rules": {}
4 | }
5 |
--------------------------------------------------------------------------------
/packages/react/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@slate-serializers/react",
3 | "version": "2.3.0"
4 | }
--------------------------------------------------------------------------------
/packages/utilities/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './lib/style-object';
2 | export * from './lib/utilities';
--------------------------------------------------------------------------------
/jest.preset.js:
--------------------------------------------------------------------------------
1 | const nxPreset = require('@nx/jest/preset').default;
2 |
3 | module.exports = { ...nxPreset };
4 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | # Add files here to ignore them from prettier formatting
2 | /dist
3 | /coverage
4 | /.nx/workspace-data
--------------------------------------------------------------------------------
/packages/html/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@slate-serializers/html",
3 | "version": "2.3.0",
4 | "type": "commonjs"
5 | }
--------------------------------------------------------------------------------
/packages/tests/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@slate-serializers/tests",
3 | "version": "2.3.0",
4 | "type": "commonjs"
5 | }
--------------------------------------------------------------------------------
/packages/slate-serializers/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "slate-serializers",
3 | "version": "2.3.0",
4 | "type": "commonjs"
5 | }
--------------------------------------------------------------------------------
/packages/template/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@slate-serializers/template",
3 | "version": "2.3.0",
4 | "type": "commonjs"
5 | }
--------------------------------------------------------------------------------
/packages/utilities/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@slate-serializers/utilities",
3 | "version": "2.3.0",
4 | "type": "commonjs"
5 | }
--------------------------------------------------------------------------------
/jest.config.ts:
--------------------------------------------------------------------------------
1 | import { getJestProjectsAsync } from '@nx/jest';
2 |
3 | export default async () => ({
4 | projects: await getJestProjectsAsync(),
5 | });
6 |
--------------------------------------------------------------------------------
/packages/template/src/lib/config/default.ts:
--------------------------------------------------------------------------------
1 | import { Config } from './types'
2 | import { slateToDomConfig } from '@slate-serializers/dom'
3 |
4 | export const config: Config = slateToDomConfig
5 |
--------------------------------------------------------------------------------
/packages/react/src/lib/config/slateDemo.tsx:
--------------------------------------------------------------------------------
1 | import { Config } from './types'
2 | import { config as defaultConfig } from './default'
3 |
4 | export const config: Config = {
5 | ...defaultConfig,
6 | }
7 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/packages/react/README.md:
--------------------------------------------------------------------------------
1 | # react
2 |
3 | This library was generated with [Nx](https://nx.dev).
4 |
5 | ## Running unit tests
6 |
7 | Run `nx test react` to execute the unit tests via [Jest](https://jestjs.io).
8 |
--------------------------------------------------------------------------------
/packages/template/src/lib/config/slateDemo.ts:
--------------------------------------------------------------------------------
1 | import { Config } from './types'
2 | import { slateDemoSlateToDomConfig } from '@slate-serializers/dom'
3 |
4 | export const config: Config = slateDemoSlateToDomConfig
5 |
--------------------------------------------------------------------------------
/.env:
--------------------------------------------------------------------------------
1 | # Nx 18 enables using plugins to infer targets by default
2 | # This is disabled for existing workspaces to maintain compatibility
3 | # For more info, see: https://nx.dev/concepts/inferred-tasks
4 | NX_ADD_PLUGINS=false
--------------------------------------------------------------------------------
/.release-please-manifest.json:
--------------------------------------------------------------------------------
1 | {".":"2.3.0","packages/dom":"2.3.0","packages/html":"2.3.0","packages/react":"2.3.0","packages/slate-serializers":"2.3.0","packages/template":"2.3.0","packages/tests":"2.3.0","packages/utilities":"2.3.0"}
2 |
--------------------------------------------------------------------------------
/packages/react/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "@nx/react/babel",
5 | {
6 | "runtime": "automatic",
7 | "useBuiltIns": "usage"
8 | }
9 | ]
10 | ],
11 | "plugins": []
12 | }
13 |
--------------------------------------------------------------------------------
/packages/tests/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "@nx/react/babel",
5 | {
6 | "runtime": "automatic",
7 | "useBuiltIns": "usage"
8 | }
9 | ]
10 | ],
11 | "plugins": []
12 | }
13 |
--------------------------------------------------------------------------------
/packages/template/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## [2.3.0](https://github.com/thompsonsj/slate-serializers/compare/template-v2.2.3...template-v2.3.0) (2025-05-16)
4 |
5 |
6 | ### Miscellaneous Chores
7 |
8 | * **template:** Synchronize all versions
9 |
--------------------------------------------------------------------------------
/packages/dom/docs/nx.md:
--------------------------------------------------------------------------------
1 | # dom
2 |
3 | This library was generated with [Nx](https://nx.dev).
4 |
5 | ## Building
6 |
7 | Run `nx build dom` to build the library.
8 |
9 | ## Running unit tests
10 |
11 | Run `nx test dom` to execute the unit tests via [Jest](https://jestjs.io).
12 |
--------------------------------------------------------------------------------
/packages/html/docs/nx.md:
--------------------------------------------------------------------------------
1 | # html
2 |
3 | This library was generated with [Nx](https://nx.dev).
4 |
5 | ## Building
6 |
7 | Run `nx build html` to build the library.
8 |
9 | ## Running unit tests
10 |
11 | Run `nx test html` to execute the unit tests via [Jest](https://jestjs.io).
12 |
--------------------------------------------------------------------------------
/packages/slate-serializers/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## [2.3.0](https://github.com/thompsonsj/slate-serializers/compare/slate-serializers-v2.2.3...slate-serializers-v2.3.0) (2025-05-16)
4 |
5 |
6 | ### Miscellaneous Chores
7 |
8 | * **slate-serializers:** Synchronize all versions
9 |
--------------------------------------------------------------------------------
/packages/tests/README.md:
--------------------------------------------------------------------------------
1 | # tests
2 |
3 | This library was generated with [Nx](https://nx.dev).
4 |
5 | ## Building
6 |
7 | Run `nx build tests` to build the library.
8 |
9 | ## Running unit tests
10 |
11 | Run `nx test tests` to execute the unit tests via [Jest](https://jestjs.io).
12 |
--------------------------------------------------------------------------------
/packages/dom/README.md:
--------------------------------------------------------------------------------
1 | # @slate-serializers/dom
2 |
3 | `slateToDom` is used by `slateToHtml` in [`@slate-serializers/html`](packages/html/README.md) before serializing to HTML.
4 |
5 | It is made available as a separate serializer for cases where DOM manipulation is desired before serializing to HTML.
6 |
--------------------------------------------------------------------------------
/packages/utilities/README.md:
--------------------------------------------------------------------------------
1 | # utilities
2 |
3 | This library was generated with [Nx](https://nx.dev).
4 |
5 | ## Building
6 |
7 | Run `nx build utilities` to build the library.
8 |
9 | ## Running unit tests
10 |
11 | Run `nx test utilities` to execute the unit tests via [Jest](https://jestjs.io).
12 |
--------------------------------------------------------------------------------
/packages/template/docs/nx.md:
--------------------------------------------------------------------------------
1 | # NX documentation
2 |
3 | This library was generated with [Nx](https://nx.dev).
4 |
5 | ## Building
6 |
7 | Run `nx build template` to build the library.
8 |
9 | ## Running unit tests
10 |
11 | Run `nx test template` to execute the unit tests via [Jest](https://jestjs.io).
12 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/packages/dom/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@slate-serializers/dom",
3 | "version": "2.3.0",
4 | "description": "Serialize Slate JSON objects to the DOM. Can be used with `htmlparser2` and associated utilities to modify the DOM and generate HTML. Used by other serializers in this monorepo.",
5 | "type": "commonjs"
6 | }
--------------------------------------------------------------------------------
/packages/dom/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 |
--------------------------------------------------------------------------------
/packages/html/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 |
--------------------------------------------------------------------------------
/packages/tests/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 |
--------------------------------------------------------------------------------
/packages/template/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 |
--------------------------------------------------------------------------------
/packages/utilities/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/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 |
--------------------------------------------------------------------------------
/packages/slate-serializers/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 |
--------------------------------------------------------------------------------
/packages/template/src/lib/config/payload.ts:
--------------------------------------------------------------------------------
1 | import { payloadSlateToDomConfig } from '@slate-serializers/dom'
2 | import { Config as SlateToTemplateConfig } from './types'
3 |
4 | /**
5 | * Configuration for Payload CMS
6 | *
7 | * Tested for v1.1.21
8 | */
9 |
10 | export const config: SlateToTemplateConfig = payloadSlateToDomConfig
--------------------------------------------------------------------------------
/packages/slate-serializers/docs/nx.md:
--------------------------------------------------------------------------------
1 | # slate-serializers
2 |
3 | This library was generated with [Nx](https://nx.dev).
4 |
5 | ## Building
6 |
7 | Run `nx build slate-serializers` to build the library.
8 |
9 | ## Running unit tests
10 |
11 | Run `nx test slate-serializers` to execute the unit tests via [Jest](https://jestjs.io).
12 |
--------------------------------------------------------------------------------
/packages/dom/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 |
--------------------------------------------------------------------------------
/packages/html/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 |
--------------------------------------------------------------------------------
/packages/react/src/lib/react.spec.tsx:
--------------------------------------------------------------------------------
1 | import { render } from '@testing-library/react';
2 |
3 | import { SlateToReact } from '../lib/react';
4 |
5 | describe('React', () => {
6 | it('should render successfully', () => {
7 | const { baseElement } = render();
8 | expect(baseElement).toBeTruthy();
9 | });
10 | });
11 |
--------------------------------------------------------------------------------
/packages/tests/src/lib/react/testComponents/Button.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC, ReactNode } from 'react'
2 |
3 | interface IButton {
4 | onClick?: () => void
5 | children?: ReactNode
6 | }
7 |
8 | const Button: FC = ({ onClick, children }) => {
9 | return
10 | }
11 |
12 | export default Button
13 |
--------------------------------------------------------------------------------
/packages/template/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 |
--------------------------------------------------------------------------------
/packages/tests/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 |
--------------------------------------------------------------------------------
/packages/utilities/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 |
--------------------------------------------------------------------------------
/packages/slate-serializers/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 |
--------------------------------------------------------------------------------
/packages/react/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## [2.3.0](https://github.com/thompsonsj/slate-serializers/compare/react-v2.2.3...react-v2.3.0) (2025-05-16)
4 |
5 |
6 | ### Features
7 |
8 | * **react:** flatten config ([#191](https://github.com/thompsonsj/slate-serializers/issues/191)) ([761c651](https://github.com/thompsonsj/slate-serializers/commit/761c651ad49fda360b416ab561b9aac72ed7aa8d))
9 |
--------------------------------------------------------------------------------
/packages/tests/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## [2.3.0](https://github.com/thompsonsj/slate-serializers/compare/tests-v2.2.3...tests-v2.3.0) (2025-05-16)
4 |
5 |
6 | ### Features
7 |
8 | * **react:** flatten config ([#191](https://github.com/thompsonsj/slate-serializers/issues/191)) ([761c651](https://github.com/thompsonsj/slate-serializers/commit/761c651ad49fda360b416ab561b9aac72ed7aa8d))
9 |
--------------------------------------------------------------------------------
/packages/tests/src/lib/tests.ts:
--------------------------------------------------------------------------------
1 | export { fixtures as combinedFixtures } from './fixtures/combined'
2 | export { fixtures as elementFixtures } from './fixtures/elementTags'
3 | export { fixtures as textFixtures } from './fixtures/textTags'
4 | export { fixtures as styleObjectFixtures } from './fixtures/style-object'
5 | export { fixtures as stylesMixedInFixtures } from './fixtures/styles-in-leaf'
6 |
--------------------------------------------------------------------------------
/packages/dom/jest.config.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | export default {
3 | displayName: 'dom',
4 | preset: '../../jest.preset.js',
5 | testEnvironment: 'node',
6 | transform: {
7 | '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }],
8 | },
9 | moduleFileExtensions: ['ts', 'js', 'html'],
10 | coverageDirectory: '../../coverage/packages/dom',
11 | };
12 |
--------------------------------------------------------------------------------
/packages/html/jest.config.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | export default {
3 | displayName: 'html',
4 | preset: '../../jest.preset.js',
5 | testEnvironment: 'node',
6 | transform: {
7 | '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }],
8 | },
9 | moduleFileExtensions: ['ts', 'js', 'html'],
10 | coverageDirectory: '../../coverage/packages/html',
11 | };
12 |
--------------------------------------------------------------------------------
/packages/template/jest.config.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | export default {
3 | displayName: 'template',
4 | preset: '../../jest.preset.js',
5 | testEnvironment: 'node',
6 | transform: {
7 | '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }],
8 | },
9 | moduleFileExtensions: ['ts', 'js', 'html'],
10 | coverageDirectory: '../../coverage/packages/template',
11 | };
12 |
--------------------------------------------------------------------------------
/packages/utilities/jest.config.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | export default {
3 | displayName: 'utilities',
4 | preset: '../../jest.preset.js',
5 | testEnvironment: 'node',
6 | transform: {
7 | '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }],
8 | },
9 | moduleFileExtensions: ['ts', 'js', 'html'],
10 | coverageDirectory: '../../coverage/packages/utilities',
11 | };
12 |
--------------------------------------------------------------------------------
/packages/dom/.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/html/.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/src/lib/react.tsx:
--------------------------------------------------------------------------------
1 | // Serializers
2 | export { SlateToReact } from './serializers'
3 |
4 | // Configuration objects
5 | export type { Config as SlateToReactConfig } from './config/types'
6 | export { config as slateToReactConfig } from './config/default'
7 | export { config as payloadSlateToReactConfig } from './config/payload'
8 | export { config as slateDemoSlateToReactConfig } from './config/slateDemo'
9 |
--------------------------------------------------------------------------------
/packages/tests/.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/template/.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/utilities/.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/slate-serializers/.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/.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 |
--------------------------------------------------------------------------------
/packages/slate-serializers/jest.config.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | export default {
3 | displayName: 'slate-serializers',
4 | preset: '../../jest.preset.js',
5 | testEnvironment: 'node',
6 | transform: {
7 | '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }],
8 | },
9 | moduleFileExtensions: ['ts', 'js', 'html'],
10 | coverageDirectory: '../../coverage/packages/slate-serializers',
11 | };
12 |
--------------------------------------------------------------------------------
/packages/dom/src/lib/utilities/update-html.ts:
--------------------------------------------------------------------------------
1 | import { getChildren } from 'domutils'
2 | import { Element } from 'domhandler'
3 | import { HtmlUpdaterFunctionMap } from '../config/types'
4 |
5 | export const renameTag = (tagName: string, replacementTagName: string): HtmlUpdaterFunctionMap => ({
6 | [tagName]: (element: Element) => {
7 | return new Element(replacementTagName, element.attribs, getChildren(element))
8 | },
9 | })
10 |
--------------------------------------------------------------------------------
/packages/react/jest.config.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | export default {
3 | displayName: 'react',
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/packages/react',
11 | };
12 |
--------------------------------------------------------------------------------
/packages/tests/jest.config.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | export default {
3 | displayName: 'react',
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/packages/react',
11 | };
12 |
--------------------------------------------------------------------------------
/packages/react/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "jsx": "react-jsx",
4 | "allowJs": false,
5 | "esModuleInterop": false,
6 | "allowSyntheticDefaultImports": true,
7 | "strict": true
8 | },
9 | "files": [],
10 | "include": [],
11 | "references": [
12 | {
13 | "path": "./tsconfig.lib.json"
14 | },
15 | {
16 | "path": "./tsconfig.spec.json"
17 | }
18 | ],
19 | "extends": "../../tsconfig.base.json"
20 | }
21 |
--------------------------------------------------------------------------------
/packages/template/src/lib/config/types.ts:
--------------------------------------------------------------------------------
1 | import { SlateToDomConfig } from '@slate-serializers/dom'
2 |
3 | export type ElementSerializer =
4 | ({
5 | node,
6 | }: {
7 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
8 | node?: any
9 | }) => unknown
10 |
11 |
12 | interface ElementSerializers {
13 | [key: string]: ElementSerializer
14 | }
15 |
16 | export interface Config extends SlateToDomConfig {
17 | customElementSerializers?: ElementSerializers
18 | }
19 |
--------------------------------------------------------------------------------
/.github/workflows/release-please.js.yml:
--------------------------------------------------------------------------------
1 | on:
2 | push:
3 | branches:
4 | - main
5 |
6 | permissions:
7 | contents: write
8 | pull-requests: write
9 |
10 | name: release-please
11 |
12 | jobs:
13 | release-please:
14 | runs-on: ubuntu-latest
15 | steps:
16 | - uses: googleapis/release-please-action@v4
17 | with:
18 | token: ${{ secrets.GITHUB_TOKEN }}
19 | config-file: release-please-config.json
20 | manifest-file: .release-please-manifest.json
--------------------------------------------------------------------------------
/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 | "jest.config.ts",
10 | "src/**/*.test.ts",
11 | "src/**/*.spec.ts",
12 | "src/**/*.test.tsx",
13 | "src/**/*.spec.tsx",
14 | "src/**/*.test.js",
15 | "src/**/*.spec.js",
16 | "src/**/*.test.jsx",
17 | "src/**/*.spec.jsx",
18 | "src/**/*.d.ts"
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/packages/template/src/lib/template.ts:
--------------------------------------------------------------------------------
1 | // Serializers
2 | export { slateToTemplate } from './serializers'
3 |
4 | // Configuration objects
5 | // slateToDom
6 | export type { Config as SlateToTemplateConfig } from './config/types'
7 | export { config as slateToTemplateConfig } from './config/default'
8 | export { config as payloadSlateToTemplateConfig } from './config/payload'
9 | export { config as slateDemoSlateToTemplateConfig } from './config/slateDemo'
10 |
11 | // Useful types
12 | export type { ElementSerializer } from './config/types'
13 |
--------------------------------------------------------------------------------
/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@slate-serializers/source",
3 | "$schema": "node_modules/nx/schemas/project-schema.json",
4 | "targets": {
5 | "local-registry": {
6 | "executor": "@nx/js:verdaccio",
7 | "options": {
8 | "port": 4873,
9 | "config": ".verdaccio/config.yml",
10 | "storage": "tmp/local-registry/storage"
11 | }
12 | },
13 | "version": {
14 | "executor": "@jscutlery/semver:version",
15 | "options": {
16 | "preset": "conventional"
17 | }
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/packages/html/src/lib/serializers/slateToHtml/index.ts:
--------------------------------------------------------------------------------
1 | import serializer from 'dom-serializer'
2 |
3 | import { slateToDom, slateToDomConfig as defaultConfig, type SlateToDomConfig } from '@slate-serializers/dom'
4 |
5 | type SlateToHtml = (node: any[], config?: SlateToDomConfig) => string
6 |
7 | export const slateToHtml: SlateToHtml = (node: any[], config = defaultConfig) => {
8 | const document = slateToDom(node, config)
9 | return serializer(document, {
10 | encodeEntities: 'encodeEntities' in config ? config.encodeEntities : false,
11 | })
12 | }
13 |
--------------------------------------------------------------------------------
/packages/dom/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "module": "commonjs",
5 | "forceConsistentCasingInFileNames": true,
6 | "strict": true,
7 | "noImplicitOverride": true,
8 | "noPropertyAccessFromIndexSignature": true,
9 | "noImplicitReturns": true,
10 | "noFallthroughCasesInSwitch": true
11 | },
12 | "files": [],
13 | "include": [],
14 | "references": [
15 | {
16 | "path": "./tsconfig.lib.json"
17 | },
18 | {
19 | "path": "./tsconfig.spec.json"
20 | }
21 | ]
22 | }
23 |
--------------------------------------------------------------------------------
/packages/html/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "module": "commonjs",
5 | "forceConsistentCasingInFileNames": true,
6 | "strict": true,
7 | "noImplicitOverride": true,
8 | "noPropertyAccessFromIndexSignature": true,
9 | "noImplicitReturns": true,
10 | "noFallthroughCasesInSwitch": true
11 | },
12 | "files": [],
13 | "include": [],
14 | "references": [
15 | {
16 | "path": "./tsconfig.lib.json"
17 | },
18 | {
19 | "path": "./tsconfig.spec.json"
20 | }
21 | ]
22 | }
23 |
--------------------------------------------------------------------------------
/packages/tests/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "module": "commonjs",
5 | "forceConsistentCasingInFileNames": true,
6 | "strict": true,
7 | "noImplicitOverride": true,
8 | "noPropertyAccessFromIndexSignature": true,
9 | "noImplicitReturns": true,
10 | "noFallthroughCasesInSwitch": true
11 | },
12 | "files": [],
13 | "include": [],
14 | "references": [
15 | {
16 | "path": "./tsconfig.lib.json"
17 | },
18 | {
19 | "path": "./tsconfig.spec.json"
20 | }
21 | ]
22 | }
23 |
--------------------------------------------------------------------------------
/packages/template/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "module": "commonjs",
5 | "forceConsistentCasingInFileNames": true,
6 | "strict": true,
7 | "noImplicitOverride": true,
8 | "noPropertyAccessFromIndexSignature": true,
9 | "noImplicitReturns": true,
10 | "noFallthroughCasesInSwitch": true
11 | },
12 | "files": [],
13 | "include": [],
14 | "references": [
15 | {
16 | "path": "./tsconfig.lib.json"
17 | },
18 | {
19 | "path": "./tsconfig.spec.json"
20 | }
21 | ]
22 | }
23 |
--------------------------------------------------------------------------------
/packages/utilities/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "module": "commonjs",
5 | "forceConsistentCasingInFileNames": true,
6 | "strict": true,
7 | "noImplicitOverride": true,
8 | "noPropertyAccessFromIndexSignature": true,
9 | "noImplicitReturns": true,
10 | "noFallthroughCasesInSwitch": true
11 | },
12 | "files": [],
13 | "include": [],
14 | "references": [
15 | {
16 | "path": "./tsconfig.lib.json"
17 | },
18 | {
19 | "path": "./tsconfig.spec.json"
20 | }
21 | ]
22 | }
23 |
--------------------------------------------------------------------------------
/packages/slate-serializers/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "module": "commonjs",
5 | "forceConsistentCasingInFileNames": true,
6 | "strict": true,
7 | "noImplicitOverride": true,
8 | "noPropertyAccessFromIndexSignature": true,
9 | "noImplicitReturns": true,
10 | "noFallthroughCasesInSwitch": true
11 | },
12 | "files": [],
13 | "include": [],
14 | "references": [
15 | {
16 | "path": "./tsconfig.lib.json"
17 | },
18 | {
19 | "path": "./tsconfig.spec.json"
20 | }
21 | ]
22 | }
23 |
--------------------------------------------------------------------------------
/packages/utilities/src/lib/style-object.ts:
--------------------------------------------------------------------------------
1 | import postcss from 'postcss'
2 | import { parse as parser, objectify } from 'postcss-js'
3 |
4 | export const transformStyleObjectToString = (style: { [key: string]: any }) => {
5 | const postcssOptions = {
6 | parser,
7 | from: undefined,
8 | }
9 | return postcss()
10 | .process(style, postcssOptions as any)
11 | .css.replace(/(\r\n|\n|\r)/gm, ' ')
12 | .replace(/\s\s+/g, ' ')
13 | }
14 |
15 | export const transformStyleStringToObject = (style: string) => {
16 | const root = postcss.parse(style)
17 | return objectify(root)
18 | }
19 |
--------------------------------------------------------------------------------
/packages/react/tsconfig.lib.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "types": [
6 | "node",
7 |
8 | "@nx/react/typings/cssmodule.d.ts",
9 | "@nx/react/typings/image.d.ts"
10 | ]
11 | },
12 | "exclude": [
13 | "jest.config.ts",
14 | "src/**/*.spec.ts",
15 | "src/**/*.test.ts",
16 | "src/**/*.spec.tsx",
17 | "src/**/*.test.tsx",
18 | "src/**/*.spec.js",
19 | "src/**/*.test.js",
20 | "src/**/*.spec.jsx",
21 | "src/**/*.test.jsx"
22 | ],
23 | "include": ["src/**/*.js", "src/**/*.jsx", "src/**/*.ts", "src/**/*.tsx"]
24 | }
25 |
--------------------------------------------------------------------------------
/packages/react/src/lib/config/types.ts:
--------------------------------------------------------------------------------
1 | import { ReactNode } from 'react'
2 | import { BaseConfig } from '@slate-serializers/dom'
3 |
4 | interface MarkTagTransform {
5 | [key: string]: ({ node, attribs }: { node?: any; attribs?: { [key: string]: string } }) => ReactNode
6 | }
7 |
8 | interface ElementTagTransform {
9 | [key: string]: ({
10 | node,
11 | attribs,
12 | children,
13 | }: {
14 | node?: any
15 | attribs?: { [key: string]: string }
16 | children?: ReactNode
17 | }) => ReactNode
18 | }
19 |
20 | export interface Config extends BaseConfig {
21 | markTransforms?: MarkTagTransform
22 | elementTransforms: ElementTagTransform
23 | }
24 |
--------------------------------------------------------------------------------
/packages/html/src/lib/serializers/htmlToSlate/config/payload.ts:
--------------------------------------------------------------------------------
1 | import { Config } from './types'
2 | import { config as defaultConfig } from './default'
3 | import { getAttributeValue } from 'domutils'
4 |
5 | /**
6 | * Configuration for Payload CMS
7 | *
8 | * Tested for v1.1.21
9 | */
10 |
11 | export const config: Config = {
12 | ...defaultConfig,
13 | elementTags: {
14 | ...defaultConfig.elementTags,
15 | a: (el) => ({
16 | type: 'link',
17 | linkType: el && getAttributeValue(el, 'data-link-type'),
18 | newTab: el && getAttributeValue(el, 'target') === '_blank',
19 | url: el && getAttributeValue(el, 'href'),
20 | }),
21 | },
22 | }
23 |
--------------------------------------------------------------------------------
/tools/sync-versions.js:
--------------------------------------------------------------------------------
1 | const mainPackageJson = require('../package.json');
2 | const glob = require('glob');
3 | const fs = require('fs');
4 |
5 | glob.sync('./packages/**/package.json')
6 | .forEach(location => {
7 | const packageJson = JSON.parse(fs.readFileSync(location))
8 | fs.writeFileSync(location, JSON.stringify({
9 | ...packageJson,
10 | version: mainPackageJson.version,
11 | ...(packageJson.dependencies?.['@slate-serializers/dom'] && {
12 | dependencies: {
13 | ...packageJson.dependencies,
14 | "@slate-serializers/dom": mainPackageJson.version
15 | }
16 | }),
17 | }, null, 3))
18 | }
19 | );
20 |
--------------------------------------------------------------------------------
/packages/dom/src/lib/serializers.ts:
--------------------------------------------------------------------------------
1 | import { AnyNode, Document } from 'domhandler'
2 |
3 | import { config as defaultConfig } from './config/default'
4 | import { Config } from './config/types'
5 | import { convertSlate } from './utilities/convert-slate'
6 |
7 | type SlateToDom = (node: any[], config?: Config) => AnyNode | ArrayLike
8 |
9 | export const slateToDom: SlateToDom = (node: any[], config = defaultConfig) => {
10 | if (!Array.isArray(node)) {
11 | return new Document([])
12 | }
13 | const document = node.map((n, index) =>
14 | convertSlate({
15 | node: n,
16 | config,
17 | isLastNodeInDocument: index === node.length - 1,
18 | }),
19 | )
20 | return document
21 | }
22 |
--------------------------------------------------------------------------------
/packages/html/src/lib/serializers/htmlToSlate/config/slateDemo.ts:
--------------------------------------------------------------------------------
1 | import { Config } from './types'
2 | import { config as defaultConfig } from './default'
3 |
4 | export const config: Config = {
5 | ...defaultConfig,
6 | elementTags: {
7 | blockquote: () => ({
8 | type: 'block-quote',
9 | }),
10 | h1: () => ({
11 | type: 'heading-one',
12 | }),
13 | h2: () => ({
14 | type: 'heading-two',
15 | }),
16 | li: () => ({
17 | type: 'list-item',
18 | }),
19 | ol: () => ({
20 | type: 'numbered-list',
21 | }),
22 | ul: () => ({
23 | type: 'bulleted-list',
24 | }),
25 | p: () => ({
26 | type: 'paragraph',
27 | }),
28 | },
29 | }
30 |
--------------------------------------------------------------------------------
/packages/html/src/lib/tests/slateToHtml/configuration/markMap.spec.ts:
--------------------------------------------------------------------------------
1 | import { slateToHtml, slateToHtmlConfig } from '@slate-serializers/html'
2 |
3 | describe("slateToHtml markMap", () => {
4 | it('processes a mark map value', () => {
5 | const html = 'Subscript text
'
6 | const slate = [
7 | {
8 | type: 'p',
9 | children: [
10 | {
11 | text: 'Subscript text',
12 | subScript: true,
13 | },
14 | ],
15 | },
16 | ]
17 | const config = {
18 | ...slateToHtmlConfig,
19 | markMap: {
20 | subScript: ['sub'],
21 | },
22 | }
23 | expect(slateToHtml(slate, config)).toEqual(html)
24 | })
25 | })
26 |
--------------------------------------------------------------------------------
/.verdaccio/config.yml:
--------------------------------------------------------------------------------
1 | # path to a directory with all packages
2 | storage: ../tmp/local-registry/storage
3 |
4 | # a list of other known repositories we can talk to
5 | uplinks:
6 | npmjs:
7 | url: https://registry.npmjs.org/
8 | maxage: 60m
9 |
10 | packages:
11 | '**':
12 | # give all users (including non-authenticated users) full access
13 | # because it is a local registry
14 | access: $all
15 | publish: $all
16 | unpublish: $all
17 |
18 | # if package is not available locally, proxy requests to npm registry
19 | proxy: npmjs
20 |
21 | # log settings
22 | logs:
23 | type: stdout
24 | format: pretty
25 | level: warn
26 |
27 | publish:
28 | allow_offline: true # set offline to true to allow publish offline
29 |
--------------------------------------------------------------------------------
/packages/dom/src/lib/dom.ts:
--------------------------------------------------------------------------------
1 | // Serializers
2 | export { slateToDom } from './serializers'
3 |
4 | // Configuration objects
5 | // slateToDom
6 | export type { BaseConfig, Config as SlateToDomConfig } from './config/types'
7 | export { config as slateToDomConfig } from './config/default'
8 | export { config as payloadSlateToDomConfig } from './config/payload'
9 | export { config as slateDemoSlateToDomConfig } from './config/slateDemo'
10 |
11 | // Useful types
12 | export type { ElementTransform, MarkTransform } from './config/types'
13 |
14 | // Slate to DOM utilities
15 | export { convertSlate } from './utilities/convert-slate'
16 | export { extractCssFromStyle } from './utilities/domhandler'
17 | export { isEmptyObject, styleMapToAttribs } from './utilities'
18 |
--------------------------------------------------------------------------------
/packages/utilities/src/lib/utilities.ts:
--------------------------------------------------------------------------------
1 | export const hasLineBreak = (str: string) => str.match(/[\r\n]+/) !== null
2 |
3 | export const prependSpace = (str: string) => str && ` ${str.trim()}`
4 |
5 | export const isEmptyObject = (obj: any) =>
6 | obj && Object.keys(obj).length === 0 && Object.getPrototypeOf(obj) === Object.prototype
7 |
8 | export const removeEmpty = (obj: {}): {} => {
9 | return Object.fromEntries(Object.entries(obj).filter(([_, v]) => v != null))
10 | }
11 |
12 | /**
13 | *
14 | * @param obj an object of any dimension
15 | * @param args property list to check
16 | * @returns undefined or property value
17 | */
18 | export const getNested = (obj: any, ...args: string[]) => {
19 | return args.reduce((o, level) => o && o[level], obj)
20 | }
21 |
--------------------------------------------------------------------------------
/packages/template/src/lib/serializers.ts:
--------------------------------------------------------------------------------
1 | import { config as slateToTemplateConfig } from './config/default'
2 | import type { Config as SlateToTemplateConfig } from './config/types'
3 | import { slateToHtml } from '@slate-serializers/html'
4 |
5 | export const slateToTemplate = (node: any[], config: SlateToTemplateConfig = slateToTemplateConfig) => {
6 | if (!Array.isArray(node)) {
7 | return
8 | }
9 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
10 | return node.map((child: any) => {
11 | const type = child.type
12 | if (type && config.customElementSerializers?.[type]) {
13 | return config.customElementSerializers?.[type]({ node: child })
14 | } else {
15 | return slateToHtml([child], config)
16 | }
17 | })
18 | }
19 |
--------------------------------------------------------------------------------
/packages/html/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
4 |
5 | ## [2.3.0](https://github.com/thompsonsj/slate-serializers/compare/html-v2.2.3...html-v2.3.0) (2025-05-16)
6 |
7 |
8 | ### Miscellaneous Chores
9 |
10 | * **html:** Synchronize all versions
11 |
12 | ## 0.1.0 (2023-07-19)
13 |
14 |
15 | ### Features
16 |
17 | * version management, better exports and types ([c8f049a](https://github.com/thompsonsj/slate-serializers/commit/c8f049ad24b4fefa07b71f091d202dd6e72ce10b))
18 |
19 |
20 | ### Code Refactoring
21 |
22 | * migrate to nx integrated monorepo ([#81](https://github.com/thompsonsj/slate-serializers/issues/81)) ([e089f7c](https://github.com/thompsonsj/slate-serializers/commit/e089f7cfc6e4616f209189807404ae84bc691eba))
23 |
--------------------------------------------------------------------------------
/packages/utilities/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
4 |
5 | ## [2.3.0](https://github.com/thompsonsj/slate-serializers/compare/utilities-v2.2.3...utilities-v2.3.0) (2025-05-16)
6 |
7 |
8 | ### Miscellaneous Chores
9 |
10 | * **utilities:** Synchronize all versions
11 |
12 | ## 0.1.0 (2023-07-19)
13 |
14 |
15 | ### Features
16 |
17 | * version management, better exports and types ([c8f049a](https://github.com/thompsonsj/slate-serializers/commit/c8f049ad24b4fefa07b71f091d202dd6e72ce10b))
18 |
19 |
20 | ### Code Refactoring
21 |
22 | * migrate to nx integrated monorepo ([#81](https://github.com/thompsonsj/slate-serializers/issues/81)) ([e089f7c](https://github.com/thompsonsj/slate-serializers/commit/e089f7cfc6e4616f209189807404ae84bc691eba))
23 |
--------------------------------------------------------------------------------
/.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 |
41 | # Additions
42 | .nx/*
43 |
44 | vite.config.*.timestamp*
45 | vitest.config.*.timestamp*
46 | .cursor/rules/nx-rules.mdc
47 | .github/instructions/nx.instructions.md
48 |
--------------------------------------------------------------------------------
/release-please-config.json:
--------------------------------------------------------------------------------
1 | {
2 | "bootstrap-sha": "24e1311e8fab931be3f003511dd58da710d9cc63",
3 | "packages": {
4 | ".": {},
5 | "packages/dom": {
6 | "component": "dom"
7 | },
8 | "packages/html": {
9 | "component": "html"
10 | },
11 | "packages/react": {
12 | "component": "react"
13 | },
14 | "packages/slate-serializers": {
15 | "component": "slate-serializers"
16 | },
17 | "packages/template": {
18 | "component": "template"
19 | },
20 | "packages/tests": {
21 | "component": "tests"
22 | },
23 | "packages/utilities": {
24 | "component": "utilities"
25 | }
26 | },
27 | "plugins": [
28 | {
29 | "type": "linked-versions",
30 | "groupName": "all",
31 | "components": ["dom", "html", "react", "slate-serializers", "template", "utilities"]
32 | }
33 | ]
34 | }
35 |
--------------------------------------------------------------------------------
/packages/tests/src/lib/react/__snapshots__/style-object.spec.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
2 |
3 | exports[`style attribute css transforms with postcss mark transform 1`] = `
4 |
5 |
6 |
7 | Paragraph
8 |
9 |
10 |
11 | `;
12 |
13 | exports[`style attribute css transforms with postcss mark transforms on multiple marks 1`] = `
14 |
15 |
16 | This is editable
17 |
18 | rich
19 |
20 | text,
21 |
22 | much
23 |
24 | better than a
25 |
26 | <textarea>
27 |
28 | !
29 |
30 |
31 | `;
32 |
33 | exports[`style attribute css transforms with postcss p tag element transform 1`] = `
34 |
35 |
36 |
37 | Paragraph
38 |
39 |
40 |
41 | `;
42 |
--------------------------------------------------------------------------------
/packages/tests/src/lib/fixtures/styles-in-leaf.ts:
--------------------------------------------------------------------------------
1 | interface Ifixture {
2 | name: string
3 | html: string
4 | slate: object[]
5 | }
6 |
7 | export const fixtures: Ifixture[] = [
8 | {
9 | name: 'p tag element transform',
10 | html: 'some title!
',
11 | slate: [
12 | {
13 | type: 'p',
14 | children: [
15 | {
16 | backgroundColor: '#38761D',
17 | color: '#FE9900',
18 | fontFamily: "arial narrow",
19 | fontSize: "20px",
20 | text: "some title!",
21 | bold: true,
22 | italic: true,
23 | underline: true,
24 | },
25 | ]
26 | }
27 | ]
28 | },
29 | ]
30 |
--------------------------------------------------------------------------------
/packages/slate-serializers/src/lib/slate-serializers.ts:
--------------------------------------------------------------------------------
1 | // Serializers
2 | export { htmlToSlate, slateToHtml } from '@slate-serializers/html'
3 | export { slateToDom } from '@slate-serializers/dom'
4 |
5 | // Configuration objects
6 | // slateToDom
7 | export { SlateToDomConfig } from '@slate-serializers/dom'
8 | export { slateToDomConfig } from '@slate-serializers/dom'
9 | export { payloadSlateToDomConfig } from '@slate-serializers/dom'
10 | export { slateDemoSlateToDomConfig } from '@slate-serializers/dom'
11 |
12 | // htmlToSlate
13 | export { HtmlToSlateConfig } from '@slate-serializers/html'
14 | export { htmlToSlateConfig } from '@slate-serializers/html'
15 | export { payloadHtmlToSlateConfig } from '@slate-serializers/html'
16 | export { slateDemoHtmlToSlateConfig } from '@slate-serializers/html'
17 |
18 | // Useful types
19 | export { ElementTransform as ElementTagTransformFunction } from '@slate-serializers/dom'
20 |
--------------------------------------------------------------------------------
/packages/tests/src/lib/react/style-object.spec.tsx:
--------------------------------------------------------------------------------
1 | import '@testing-library/jest-dom';
2 | import { render } from '@testing-library/react';
3 | import { SlateToReact, slateToReactConfig as defaultReactConfig, SlateToReactConfig } from '@slate-serializers/react'
4 | import { styleObjectFixtures } from './../tests'
5 | import { transformStyleObjectToString } from '@slate-serializers/utilities'
6 |
7 | const reactConfig: SlateToReactConfig = {
8 | ...defaultReactConfig,
9 | elementAttributeTransform: ({ node }) => {
10 | const style = transformStyleObjectToString(node.style)
11 | return style ? { style } : undefined
12 | }
13 | }
14 |
15 | describe('style attribute css transforms with postcss', () => {
16 | for (const fixture of styleObjectFixtures) {
17 | it(`${fixture.name}`, () => {
18 | const tree = render()
19 | expect(tree.container).toMatchSnapshot()
20 | })
21 | }
22 | })
23 |
--------------------------------------------------------------------------------
/.github/workflows/node.js.yml:
--------------------------------------------------------------------------------
1 | # This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs
3 |
4 | name: Node.js CI
5 |
6 | on:
7 | push:
8 | branches: [ "main" ]
9 | pull_request:
10 | branches: [ "main" ]
11 |
12 | jobs:
13 | build:
14 |
15 | runs-on: ubuntu-latest
16 |
17 | strategy:
18 | matrix:
19 | node-version: [23.x]
20 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/
21 |
22 | steps:
23 | - uses: actions/checkout@v3
24 | - name: Use Node.js ${{ matrix.node-version }}
25 | uses: actions/setup-node@v3
26 | with:
27 | node-version: ${{ matrix.node-version }}
28 | cache: 'npm'
29 | - run: npm ci
30 | - run: npm run build --if-present
31 | - run: npm test
32 |
--------------------------------------------------------------------------------
/packages/dom/src/lib/config/slateDemo.ts:
--------------------------------------------------------------------------------
1 | import { Config } from './types'
2 | import { isEmptyObject, styleMapToAttribs } from '../utilities'
3 |
4 | const ELEMENT_NAME_TAG_MAP = {
5 | ['block-quote']: 'blockquote',
6 | ['heading-one']: 'h1',
7 | ['heading-two']: 'h2',
8 | ['list-item']: 'li',
9 | ['numbered-list']: 'ol',
10 | ['bulleted-list']: 'ul',
11 | paragraph: 'p',
12 | }
13 |
14 | const MARK_ELEMENT_TAG_MAP = {
15 | strikethrough: ['s'],
16 | bold: ['strong'],
17 | underline: ['u'],
18 | italic: ['i'],
19 | code: ['pre', 'code'],
20 | }
21 |
22 | export const config: Config = {
23 | markMap: MARK_ELEMENT_TAG_MAP,
24 | elementMap: ELEMENT_NAME_TAG_MAP,
25 | elementTransforms: {},
26 | elementAttributeTransform: ({ node }) => {
27 | const elementStyleMap: { [key: string]: string } = {
28 | align: 'textAlign',
29 | }
30 | const attribs = styleMapToAttribs({elementStyleMap, node})
31 | return isEmptyObject(attribs) ? {} : attribs
32 | },
33 | encodeEntities: true,
34 | }
35 |
--------------------------------------------------------------------------------
/packages/html/src/lib/tests/slateToHtml/configuration/markTransforms.spec.ts:
--------------------------------------------------------------------------------
1 | import { Element } from 'domhandler'
2 | import { slateToHtml, slateToHtmlConfig, SlateToHtmlConfig } from "@slate-serializers/html"
3 |
4 | describe("slateToHtml markTransforms", () => {
5 | it('processes a mark transform', () => {
6 | const html = 'Paragraph
'
7 | const slate = [
8 | {
9 | type: 'p',
10 | children: [
11 | {
12 | bold: true,
13 | fontSize: '96px',
14 | text: 'Paragraph',
15 | },
16 | ],
17 | },
18 | ]
19 | const config: SlateToHtmlConfig = {
20 | ...slateToHtmlConfig,
21 | markTransforms: {
22 | ...slateToHtmlConfig.markTransforms,
23 | fontSize: ({ node }) => {
24 | return new Element('span', {
25 | style: `font-size:${node.fontSize};`,
26 | })
27 | },
28 | },
29 | }
30 | expect(slateToHtml(slate, config)).toEqual(html)
31 | })
32 | })
33 |
--------------------------------------------------------------------------------
/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": ["es2020", "dom"],
14 | "skipLibCheck": true,
15 | "skipDefaultLibCheck": true,
16 | "baseUrl": ".",
17 | "paths": {
18 | "@slate-serializers/dom": ["packages/dom/src/index.ts"],
19 | "@slate-serializers/html": ["packages/html/src/index.ts"],
20 | "@slate-serializers/react": ["packages/react/src/index.ts"],
21 | "@slate-serializers/template": ["packages/template/src/index.ts"],
22 | "@slate-serializers/tests": ["packages/tests/src/index.ts"],
23 | "@slate-serializers/utilities": ["packages/utilities/src/index.ts"],
24 | "slate-serializers": ["packages/slate-serializers/src/index.ts"]
25 | }
26 | },
27 | "exclude": ["node_modules", "tmp"]
28 | }
29 |
--------------------------------------------------------------------------------
/packages/html/src/lib/tests/slateToHtml/configuration/elementTransforms.spec.ts:
--------------------------------------------------------------------------------
1 | import { Element } from 'domhandler'
2 | import { slateToHtml, slateToHtmlConfig } from "@slate-serializers/html"
3 |
4 | describe("slateToHtml elementTransforms", () => {
5 | it('processes an element transform', () => {
6 | const html = 'Paragraph
'
7 | const slate = [
8 | {
9 | type: 'p',
10 | children: [
11 | {
12 | text: 'Paragraph',
13 | },
14 | ],
15 | },
16 | {
17 | type: 'image',
18 | url: 'https://picsum.photos/id/237/200/300',
19 | },
20 | ]
21 | const config = {
22 | ...slateToHtmlConfig,
23 | elementTransforms: {
24 | ...slateToHtmlConfig.elementTransforms,
25 | image: ({ node }: { node?: any }) => {
26 | return new Element('img', {
27 | src: node.url,
28 | })
29 | },
30 | },
31 | }
32 | expect(slateToHtml(slate, config)).toEqual(html)
33 | })
34 | })
35 |
--------------------------------------------------------------------------------
/packages/html/src/lib/html.ts:
--------------------------------------------------------------------------------
1 | // Serializers
2 | export { htmlToSlate } from './serializers/htmlToSlate'
3 | export { slateToHtml } from './serializers/slateToHtml'
4 |
5 | // Configuration objects
6 | // slateToDom - renamed to slateToHtml
7 | // slateToHtml uses the same config as slateToDom but it makes sense to rename the config objects in case we want to add a slateToHtml specific config in the future
8 | export {
9 | type SlateToDomConfig as SlateToHtmlConfig,
10 | slateToDomConfig as slateToHtmlConfig,
11 | payloadSlateToDomConfig as payloadSlateToHtmlConfig,
12 | slateDemoSlateToDomConfig as slateDemoSlateToHtmlConfig,
13 | type ElementTransform,
14 | type MarkTransform,
15 | } from '@slate-serializers/dom'
16 |
17 | // htmlToSlate
18 | export type { Config as HtmlToSlateConfig } from './serializers/htmlToSlate/config/types'
19 | export { config as htmlToSlateConfig } from './serializers/htmlToSlate/config/default'
20 | export { config as payloadHtmlToSlateConfig } from './serializers/htmlToSlate/config/payload'
21 | export { config as slateDemoHtmlToSlateConfig } from './serializers/htmlToSlate/config/slateDemo'
22 |
--------------------------------------------------------------------------------
/packages/html/src/lib/serializers/slateToHtml/slateDemo.spec.ts:
--------------------------------------------------------------------------------
1 | import { slateToHtml } from '.'
2 | import { slateDemoSlateToDomConfig } from '@slate-serializers/dom'
3 |
4 | describe('slateToHtml expected behaviour', () => {
5 | it('adds the `text-align:right;` css property/value to style', () => {
6 | const html = 'This is a right aligned paragraph.
'
7 | const slate = [
8 | {
9 | align: 'right',
10 | children: [
11 | {
12 | text: 'This is a right aligned paragraph.',
13 | },
14 | ],
15 | type: 'paragraph',
16 | },
17 | ]
18 | expect(slateToHtml(slate, slateDemoSlateToDomConfig)).toEqual(html)
19 | })
20 |
21 | it('does not add empty style attributes', () => {
22 | const html = 'This is a right aligned paragraph.
'
23 | const slate = [
24 | {
25 | children: [
26 | {
27 | text: 'This is a right aligned paragraph.',
28 | },
29 | ],
30 | type: 'paragraph',
31 | },
32 | ]
33 | expect(slateToHtml(slate, slateDemoSlateToDomConfig)).toEqual(html)
34 | })
35 | })
36 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 [Steven Thompson](https://github.com/thompsonsj)
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.
--------------------------------------------------------------------------------
/packages/tests/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "tests",
3 | "$schema": "../../node_modules/nx/schemas/project-schema.json",
4 | "sourceRoot": "packages/tests/src",
5 | "projectType": "library",
6 | "targets": {
7 | "build": {
8 | "executor": "@nx/js:tsc",
9 | "outputs": ["{options.outputPath}"],
10 | "options": {
11 | "outputPath": "dist/packages/tests",
12 | "main": "packages/tests/src/index.ts",
13 | "tsConfig": "packages/tests/tsconfig.lib.json",
14 | "assets": ["packages/tests/*.md"]
15 | }
16 | },
17 | "lint": {
18 | "executor": "@nx/linter:eslint",
19 | "outputs": ["{options.outputFile}"],
20 | "options": {
21 | "lintFilePatterns": ["packages/tests/**/*.ts"]
22 | }
23 | },
24 | "test": {
25 | "executor": "@nx/jest:jest",
26 | "outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
27 | "options": {
28 | "jestConfig": "packages/tests/jest.config.ts",
29 | "passWithNoTests": true
30 | },
31 | "configurations": {
32 | "ci": {
33 | "ci": true,
34 | "codeCoverage": true
35 | }
36 | }
37 | }
38 | },
39 | "tags": []
40 | }
41 |
--------------------------------------------------------------------------------
/packages/tests/src/lib/template/combined-payload.spec.ts:
--------------------------------------------------------------------------------
1 | import { combinedFixtures, elementFixtures, textFixtures } from '../tests'
2 | import { payloadSlateToTemplateConfig, slateToTemplate } from '@slate-serializers/template'
3 |
4 | describe('Slate JSON to React transforms', () => {
5 | describe('Element tags', () => {
6 | const fixtures = elementFixtures
7 | for (const fixture of fixtures) {
8 | it(`${fixture.name}`, () => {
9 | const tree = slateToTemplate(fixture.slate, payloadSlateToTemplateConfig)
10 | expect(tree).toMatchSnapshot()
11 | })
12 | }
13 | })
14 | describe('Text tags', () => {
15 | const fixtures = textFixtures
16 | for (const fixture of fixtures) {
17 | it(`${fixture.name}`, () => {
18 | const tree = slateToTemplate(fixture.slate, payloadSlateToTemplateConfig)
19 | expect(tree).toMatchSnapshot()
20 | })
21 | }
22 | })
23 | describe('Combined', () => {
24 | const fixtures = combinedFixtures
25 | for (const fixture of fixtures) {
26 | it(`${fixture.name}`, () => {
27 | const tree = slateToTemplate(fixture.slateOriginal, payloadSlateToTemplateConfig)
28 | expect(tree).toMatchSnapshot()
29 | })
30 | }
31 | })
32 | })
33 |
--------------------------------------------------------------------------------
/.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 | "@typescript-eslint/no-extra-semi": "error",
29 | "no-extra-semi": "off"
30 | }
31 | },
32 | {
33 | "files": ["*.js", "*.jsx"],
34 | "extends": ["plugin:@nx/javascript"],
35 | "rules": {
36 | "@typescript-eslint/no-extra-semi": "error",
37 | "no-extra-semi": "off"
38 | }
39 | },
40 | {
41 | "files": ["*.spec.ts", "*.spec.tsx", "*.spec.js", "*.spec.jsx"],
42 | "env": {
43 | "jest": true
44 | },
45 | "rules": {}
46 | }
47 | ]
48 | }
49 |
--------------------------------------------------------------------------------
/packages/tests/src/lib/react/combined.spec.tsx:
--------------------------------------------------------------------------------
1 | import '@testing-library/jest-dom';
2 | import { render } from '@testing-library/react';
3 | import { combinedFixtures, elementFixtures, textFixtures } from '../tests'
4 | import { SlateToReact } from '@slate-serializers/react'
5 |
6 | describe('Slate JSON to React transforms', () => {
7 | describe('Element tags', () => {
8 | const fixtures = elementFixtures
9 | for (const fixture of fixtures) {
10 | it(`${fixture.name}`, () => {
11 | const tree = render()
12 | expect(tree.container).toMatchSnapshot()
13 | })
14 | }
15 | })
16 | describe('Text tags', () => {
17 | const fixtures = textFixtures
18 | for (const fixture of fixtures) {
19 | it(`${fixture.name}`, () => {
20 | const tree = render()
21 | expect(tree.container).toMatchSnapshot()
22 | })
23 | }
24 | })
25 | describe('Combined', () => {
26 | const fixtures = combinedFixtures
27 | for (const fixture of fixtures) {
28 | it(`${fixture.name}`, () => {
29 | const tree = render()
30 | expect(tree.container).toMatchSnapshot()
31 | })
32 | }
33 | })
34 | })
35 |
--------------------------------------------------------------------------------
/packages/react/src/lib/config/default.tsx:
--------------------------------------------------------------------------------
1 | import { ReactNode } from 'react'
2 | import { Config } from './types'
3 | import { ulid } from 'ulidx'
4 | import { slateToDomConfig } from '@slate-serializers/dom'
5 |
6 | const BlockQuote = ({ children }: { children: ReactNode }) => (
7 |
8 | {children}
9 |
10 | )
11 |
12 | export const config: Config = {
13 | markMap: slateToDomConfig.markMap,
14 | elementMap: slateToDomConfig.elementMap,
15 | elementAttributeTransform: slateToDomConfig.elementAttributeTransform,
16 | defaultTag: slateToDomConfig.defaultTag,
17 | encodeEntities: slateToDomConfig.encodeEntities,
18 | alwaysEncodeBreakingEntities: slateToDomConfig.alwaysEncodeBreakingEntities,
19 | alwaysEncodeCodeEntities: slateToDomConfig.alwaysEncodeCodeEntities,
20 | convertLineBreakToBr: slateToDomConfig.convertLineBreakToBr,
21 | elementTransforms: {
22 | quote: ({ children }) => {
23 | return {children}
24 | },
25 | link: ({ node, children = [] }) => {
26 | const attrs: {[key: string]: string} = {}
27 | if (node.newTab) {
28 | attrs.target = '_blank'
29 | }
30 | return (
31 |
32 | {children}
33 |
34 | )
35 | },
36 | },
37 | }
38 |
--------------------------------------------------------------------------------
/packages/slate-serializers/README.md:
--------------------------------------------------------------------------------
1 | # slate-serializers
2 |
3 | This package has been split into separate packages.
4 |
5 | There is no need to change - `slate-serializers` will continue to be maintained alongside the new serializers. However, upgrading will keep the dependency tree minimal for your project.
6 |
7 | Original docs available at [packages/slate-serializers/docs/original.md](https://github.com/thompsonsj/slate-serializers/blob/main/packages/slate-serializers/docs/original.md).
8 |
9 | ## New packages
10 |
11 | An overview is available at [README.md](https://github.com/thompsonsj/slate-serializers/blob/main/README.md).
12 |
13 | ## Upgrade
14 |
15 | ### HTML serializers
16 |
17 | Change import from `slate-serializers` to `@slate-serializers/html`.
18 |
19 | If importing configuration objects, change the name as follows.
20 | - `slateToDomConfig` to `slateToHtmlConfig`.
21 | - `payloadSlateToDomConfig` to `payloadSlateToHtmlConfig`.
22 | - `slateDemoSlateToDomConfig` to `slateDemoSlateToHtmlConfig`
23 |
24 | ### DOM serializer
25 |
26 | Change import from `slate-serializers` to `@slate-serializers/dom`.
27 |
28 | ## Rationale for splitting the packages
29 |
30 | As more serializers are introduced, the number of dependencies increase. It makes sense to maintain separate packages to keep the serializers as efficient as possible for their desired use case.
31 |
--------------------------------------------------------------------------------
/packages/dom/src/lib/utilities/domhandler.ts:
--------------------------------------------------------------------------------
1 | import { getAttributeValue } from 'domutils'
2 | import { Element, Text } from 'domhandler'
3 | import serializer from 'dom-serializer'
4 | import { parseStyleCssText } from '.'
5 |
6 | /**
7 | * Generate nested mark elements
8 | *
9 | * nestedMarkElements should be recursive, but it works
10 | * so leaving it for now. Can handle a maximum of 5
11 | * elements. Really shouldn't be any more than that!
12 | */
13 |
14 | export const nestedMarkElementsString = (els: Element[], text: string) => {
15 | return serializer(nestedMarkElements(els, new Text(text)))
16 | }
17 |
18 | export const nestedMarkElements = (els: Element[], element: Element | Text) => {
19 | while (els && els.length > 0) {
20 | const el = els.pop()
21 | if (el) {
22 | el.children = [element]
23 | element = el
24 | }
25 | }
26 | return element
27 | }
28 |
29 | /**
30 | * Extract css value from style attribute
31 | * @param el domhandler Element
32 | * @param attribute css attribute in camelCase
33 | * @returns css value or null
34 | */
35 | export const extractCssFromStyle = (el: Element, attribute: string): string | null => {
36 | const cssText = el && getAttributeValue(el, 'style')
37 | if (cssText) {
38 | const css = parseStyleCssText(cssText)
39 | if (css[attribute]) {
40 | return css[attribute]
41 | }
42 | }
43 | return null
44 | }
45 |
--------------------------------------------------------------------------------
/packages/tests/src/lib/react/combined-payload.spec.tsx:
--------------------------------------------------------------------------------
1 | import '@testing-library/jest-dom';
2 | import { render } from '@testing-library/react';
3 | import { combinedFixtures, elementFixtures, textFixtures } from '../tests'
4 | import { SlateToReact, payloadSlateToReactConfig } from '@slate-serializers/react'
5 |
6 | describe('Slate JSON to React transforms', () => {
7 | describe('Element tags', () => {
8 | const fixtures = elementFixtures
9 | for (const fixture of fixtures) {
10 | it(`${fixture.name}`, () => {
11 | const tree = render()
12 | expect(tree.container).toMatchSnapshot()
13 | })
14 | }
15 | })
16 | describe('Text tags', () => {
17 | const fixtures = textFixtures
18 | for (const fixture of fixtures) {
19 | it(`${fixture.name}`, () => {
20 | const tree = render()
21 | expect(tree.container).toMatchSnapshot()
22 | })
23 | }
24 | })
25 | describe('Combined', () => {
26 | const fixtures = combinedFixtures
27 | for (const fixture of fixtures) {
28 | it(`${fixture.name}`, () => {
29 | const tree = render()
30 | expect(tree.container).toMatchSnapshot()
31 | })
32 | }
33 | })
34 | })
35 |
--------------------------------------------------------------------------------
/packages/react/src/lib/config/payload.tsx:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line @nx/enforce-module-boundaries
2 | import { config as slateToReactConfig } from './default'
3 | import { payloadSlateToDomConfig } from '@slate-serializers/dom'
4 | import { Config as SlateToReactConfig } from './types'
5 | import { ulid } from 'ulidx'
6 |
7 | /**
8 | * Configuration for Payload CMS
9 | *
10 | * Tested for v1.1.21
11 | */
12 | export const config: SlateToReactConfig = {
13 | markMap: payloadSlateToDomConfig.markMap,
14 | elementMap: payloadSlateToDomConfig.elementMap,
15 | elementAttributeTransform: payloadSlateToDomConfig.elementAttributeTransform,
16 | defaultTag: payloadSlateToDomConfig.defaultTag,
17 | encodeEntities: payloadSlateToDomConfig.encodeEntities,
18 | alwaysEncodeBreakingEntities: payloadSlateToDomConfig.alwaysEncodeBreakingEntities,
19 | alwaysEncodeCodeEntities: payloadSlateToDomConfig.alwaysEncodeCodeEntities,
20 | convertLineBreakToBr: payloadSlateToDomConfig.convertLineBreakToBr,
21 | elementTransforms: {
22 | ...slateToReactConfig.elementTransforms,
23 | link: ({ node, children = [] }) => {
24 | const attrs: {[key: string]: string} = {}
25 | if (node.linkType) {
26 | attrs['data-link-type'] = node.linkType
27 | }
28 | if (node.newTab) {
29 | attrs.target = '_blank'
30 | }
31 | return (
32 |
33 | {children}
34 |
35 | )
36 | },
37 | },
38 | }
39 |
--------------------------------------------------------------------------------
/packages/dom/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
4 |
5 | ## [2.3.0](https://github.com/thompsonsj/slate-serializers/compare/dom-v2.2.3...dom-v2.3.0) (2025-05-16)
6 |
7 |
8 | ### Features
9 |
10 | * **react:** flatten config ([#191](https://github.com/thompsonsj/slate-serializers/issues/191)) ([761c651](https://github.com/thompsonsj/slate-serializers/commit/761c651ad49fda360b416ab561b9aac72ed7aa8d))
11 |
12 | ## 0.1.0 (2023-07-19)
13 |
14 |
15 | ### Features
16 |
17 | * version management, better exports and types ([#82](https://github.com/thompsonsj/slate-serializers/issues/82)) ([fc3c828](https://github.com/thompsonsj/slate-serializers/commit/fc3c828b13dae411acdb985753986e451d114f1d))
18 |
19 |
20 | ### Code Refactoring
21 |
22 | * migrate to nx integrated monorepo ([#81](https://github.com/thompsonsj/slate-serializers/issues/81)) ([e089f7c](https://github.com/thompsonsj/slate-serializers/commit/e089f7cfc6e4616f209189807404ae84bc691eba))
23 |
24 | ## 0.1.0 (2023-07-19)
25 |
26 |
27 | ### Features
28 |
29 | * version management, better exports and types ([c8f049a](https://github.com/thompsonsj/slate-serializers/commit/c8f049ad24b4fefa07b71f091d202dd6e72ce10b))
30 |
31 |
32 | ### Code Refactoring
33 |
34 | * migrate to nx integrated monorepo ([#81](https://github.com/thompsonsj/slate-serializers/issues/81)) ([e089f7c](https://github.com/thompsonsj/slate-serializers/commit/e089f7cfc6e4616f209189807404ae84bc691eba))
35 |
--------------------------------------------------------------------------------
/packages/html/src/lib/serializers/htmlToSlate/config/types.ts:
--------------------------------------------------------------------------------
1 | import { Element } from 'domhandler'
2 |
3 | interface ItagMap {
4 | [key: string]: (a?: Element) => object
5 | }
6 |
7 | export type AttributeTransform = ({
8 | el,
9 | }: {
10 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
11 | el: Element
12 | }) => { [key: string]: unknown } | undefined
13 |
14 | /**
15 | * For details on configuration options:
16 | * @see /docs/config/htmlToSlate.md
17 | */
18 | export interface Config {
19 | /* Use a custom function to generate Slate node attributes based on every Element passed through the serializer */
20 | elementAttributeTransform?: AttributeTransform
21 | /* Map HTML element tags to Slate JSON object attributes */
22 | elementTags: ItagMap
23 | /* Map HTML text tags to Slate JSON object attributes */
24 | textTags: ItagMap
25 | /* Perform string operations on the html string before it is parsed to a DOM Document Object Model */
26 | htmlPreProcessString?: (html: string) => string
27 | /* Pass updater functions to modify HTML */
28 | htmlUpdaterMap?: HtmlUpdaterFunctionMap
29 | /* Remove whitespace that does not contribute meaning */
30 | filterWhitespaceNodes: boolean
31 | /* Convert br tags to a new line character (\n) */
32 | convertBrToLineBreak?: boolean
33 | /* Replace multiple whitespace characters with a single space. */
34 | trimWhiteSpace?: boolean
35 | }
36 |
37 | type UpdaterFunction = (el: Element) => Element | string
38 |
39 | export type HtmlUpdaterFunctionMap = Record
40 |
--------------------------------------------------------------------------------
/packages/dom/src/lib/dom.spec.ts:
--------------------------------------------------------------------------------
1 | import { slateToDom } from './dom'
2 | import { selectAll } from 'css-select'
3 | import { parseDocument } from 'htmlparser2'
4 | import { replaceElement } from 'domutils'
5 | import { AnyNode, Element, Node } from 'domhandler'
6 | import serializer from 'dom-serializer'
7 |
8 | describe('slateToDom expected behaviour', () => {
9 | it('can modify resulting DOM document object', () => {
10 | const html = `Heading 2
Paragraph.
Second Heading 2
`
11 | const slate = [
12 | {
13 | children: [
14 | {
15 | text: 'Heading 2',
16 | },
17 | ],
18 | type: 'h2',
19 | },
20 | {
21 | children: [
22 | {
23 | text: 'Paragraph.',
24 | },
25 | ],
26 | type: 'p',
27 | },
28 | {
29 | children: [
30 | {
31 | text: 'Second Heading 2',
32 | },
33 | ],
34 | type: 'h2',
35 | },
36 | ]
37 | // serializer and parseDocument are required operations before
38 | // replaceElement will take effect. This negates the usefulness
39 | // of slateToDom in this use case.
40 | const document = parseDocument(serializer(slateToDom(slate)))
41 | selectAll('h2', document as any).forEach((element: any) => {
42 | replaceElement(element, new Element('h2', { ...element.attribs, class: 'heading-two' }, element.children))
43 | })
44 | expect(serializer(document as AnyNode)).toEqual(html)
45 | })
46 | })
47 |
--------------------------------------------------------------------------------
/packages/tests/src/lib/html/sameHtmlSlateBothWays.spec.ts:
--------------------------------------------------------------------------------
1 | import { htmlToSlate, slateToHtml } from '@slate-serializers/html'
2 | import { elementFixtures, textFixtures } from '../tests'
3 |
4 | /**
5 | * Run tests both ways by using the same fixtures
6 | *
7 | * * textFixtures
8 | * * elementFixtures
9 | *
10 | * Test we get expected results in all fixtures using
11 | * * htmlToSlate
12 | * * slateToHtml
13 | */
14 |
15 | describe('HTML to Slate JSON transforms', () => {
16 | describe('Element tags', () => {
17 | const fixtures = elementFixtures
18 | for (const fixture of fixtures) {
19 | it(`${fixture.name}`, () => {
20 | expect(htmlToSlate(fixture.html)).toEqual(fixture.slate)
21 | })
22 | }
23 | })
24 | describe('Text tags', () => {
25 | const fixtures = textFixtures
26 | for (const fixture of fixtures) {
27 | it(`${fixture.name}`, () => {
28 | expect(htmlToSlate(fixture.html)).toEqual(fixture.slate)
29 | })
30 | }
31 | })
32 | })
33 |
34 | describe('Slate JSON to HTML transforms', () => {
35 | describe('Element tags', () => {
36 | const fixtures = elementFixtures
37 | for (const fixture of fixtures) {
38 | it(`${fixture.name}`, () => {
39 | expect(slateToHtml(fixture.slate)).toEqual(fixture.htmlFromSlate || fixture.html)
40 | })
41 | }
42 | })
43 | describe('Text tags', () => {
44 | const fixtures = textFixtures
45 | for (const fixture of fixtures) {
46 | it(`${fixture.name}`, () => {
47 | expect(slateToHtml(fixture.slate)).toEqual(fixture.html)
48 | })
49 | }
50 | })
51 | })
52 |
--------------------------------------------------------------------------------
/packages/dom/src/lib/config/types.ts:
--------------------------------------------------------------------------------
1 | import { ChildNode, Element } from 'domhandler'
2 |
3 | export type MarkTransform = ({ node, attribs }: {
4 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
5 | node?: any;
6 | attribs?: { [key: string]: string }
7 | }) => Element | undefined
8 |
9 | interface MarkTransforms {
10 | [key: string]: MarkTransform
11 | }
12 |
13 | export type ElementTransform = ({
14 | node,
15 | attribs,
16 | children,
17 | }: {
18 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
19 | node?: any
20 | attribs?: { [key: string]: string }
21 | children?: ChildNode[]
22 | }) => Element | undefined
23 |
24 | export type AttributeTransform = ({
25 | node,
26 | }: {
27 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
28 | node: any
29 | }) => { [key: string]: string } | undefined
30 |
31 | interface ElementTransforms {
32 | [key: string]: ElementTransform
33 | }
34 |
35 | export interface BaseConfig {
36 | markMap: { [key: string]: string[] }
37 | elementMap: { [key: string]: string }
38 | // markAttributeTransform?: AttributeTransform
39 | elementAttributeTransform?: AttributeTransform
40 | defaultTag?: string
41 | encodeEntities?: boolean
42 | alwaysEncodeBreakingEntities?: boolean
43 | alwaysEncodeCodeEntities?: boolean
44 | convertLineBreakToBr?: boolean
45 | }
46 |
47 | export interface Config extends BaseConfig {
48 | markTransforms?: MarkTransforms
49 | elementTransforms: ElementTransforms
50 | }
51 |
52 | type UpdaterFunction = (el: Element) => Element | string
53 |
54 | export type HtmlUpdaterFunctionMap = Record
55 |
--------------------------------------------------------------------------------
/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 | "tags": [],
7 | "targets": {
8 | "deploy": {
9 | "executor": "ngx-deploy-npm:deploy",
10 | "options": {
11 | "distFolderPath": "dist/packages/react",
12 | "access": "public"
13 | },
14 | "dependsOn": ["build"]
15 | },
16 | "lint": {
17 | "executor": "@nx/eslint:lint",
18 | "outputs": ["{options.outputFile}"]
19 | },
20 | "build": {
21 | "executor": "@nx/rollup:rollup",
22 | "outputs": ["{options.outputPath}"],
23 | "options": {
24 | "outputPath": "dist/packages/react",
25 | "tsConfig": "packages/react/tsconfig.lib.json",
26 | "project": "packages/react/package.json",
27 | "entryFile": "packages/react/src/index.ts",
28 | "external": ["react", "react-dom", "react/jsx-runtime"],
29 | "rollupConfig": "@nx/react/plugins/bundle-rollup",
30 | "compiler": "babel",
31 | "assets": [
32 | {
33 | "glob": "packages/react/README.md",
34 | "input": ".",
35 | "output": "."
36 | }
37 | ],
38 | "updateBuildableProjectDepsInPackageJson": true,
39 | "buildableProjectDepsInPackageJsonType": "dependencies"
40 | }
41 | },
42 | "test": {
43 | "executor": "@nx/jest:jest",
44 | "outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
45 | "options": {
46 | "jestConfig": "packages/react/jest.config.ts"
47 | }
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/packages/dom/src/lib/config/payload.ts:
--------------------------------------------------------------------------------
1 | import { Element, Text } from 'domhandler'
2 | import { config as defaultConfig } from './default'
3 | import { SlateToDomConfig } from '../..'
4 | import { styleToString } from '../utilities';
5 |
6 | /**
7 | * Configuration for Payload CMS
8 | *
9 | * Tested for v1.1.21
10 | */
11 |
12 | export const config: SlateToDomConfig = {
13 | ...defaultConfig,
14 | elementAttributeTransform: ({ node }) => {
15 | if (node.align || node.textAlign) {
16 | return {
17 | style: styleToString({
18 | ['text-align']: node.align || node.textAlign,
19 | })
20 | }
21 | }
22 | return
23 | },
24 | elementTransforms: {
25 | ...defaultConfig.elementTransforms,
26 | link: ({ node, children = [] }) => {
27 | const attrs: {[key: string]: string} = {}
28 | if (node.linkType) {
29 | attrs['data-link-type'] = node.linkType
30 | }
31 | if (node.newTab) {
32 | attrs['target'] = '_blank'
33 | }
34 | return new Element(
35 | 'a',
36 | {
37 | href: node.url,
38 | ...attrs,
39 | },
40 | children,
41 | )
42 | },
43 | upload: ({ node }) => {
44 | if (node.value?.mimeType && node.value?.url) {
45 | if (node.value?.mimeType.match(/^image/)) {
46 | return new Element('img', {
47 | src: node.value?.url,
48 | })
49 | }
50 | return new Element(
51 | 'a',
52 | {
53 | href: node.value?.url,
54 | },
55 | [new Text(node.value?.filename)],
56 | )
57 | }
58 | return
59 | },
60 | },
61 | defaultTag: 'p',
62 | }
63 |
--------------------------------------------------------------------------------
/packages/template/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "template",
3 | "$schema": "../../node_modules/nx/schemas/project-schema.json",
4 | "sourceRoot": "packages/template/src",
5 | "projectType": "library",
6 | "tags": [],
7 | "targets": {
8 | "build": {
9 | "executor": "@nx/js:tsc",
10 | "outputs": ["{options.outputPath}"],
11 | "options": {
12 | "outputPath": "dist/packages/template",
13 | "main": "packages/template/src/index.ts",
14 | "tsConfig": "packages/template/tsconfig.lib.json",
15 | "assets": ["packages/template/*.md"],
16 | "updateBuildableProjectDepsInPackageJson": true,
17 | "buildableProjectDepsInPackageJsonType": "dependencies"
18 | }
19 | },
20 | "publish": {
21 | "command": "node tools/scripts/publish.mjs template {args.ver} {args.tag}",
22 | "dependsOn": ["build"]
23 | },
24 | "lint": {
25 | "executor": "@nx/linter:eslint",
26 | "outputs": ["{options.outputFile}"],
27 | "options": {
28 | "lintFilePatterns": ["packages/template/**/*.ts"]
29 | }
30 | },
31 | "test": {
32 | "executor": "@nx/jest:jest",
33 | "outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
34 | "options": {
35 | "jestConfig": "packages/template/jest.config.ts",
36 | "passWithNoTests": true
37 | },
38 | "configurations": {
39 | "ci": {
40 | "ci": true,
41 | "codeCoverage": true
42 | }
43 | }
44 | },
45 | "deploy": {
46 | "executor": "ngx-deploy-npm:deploy",
47 | "options": {
48 | "distFolderPath": "dist/packages/template",
49 | "access": "public"
50 | },
51 | "dependsOn": ["build"]
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/packages/utilities/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "utilities",
3 | "$schema": "../../node_modules/nx/schemas/project-schema.json",
4 | "sourceRoot": "packages/utilities/src",
5 | "projectType": "library",
6 | "tags": [],
7 | "targets": {
8 | "build": {
9 | "executor": "@nx/js:tsc",
10 | "outputs": ["{options.outputPath}"],
11 | "options": {
12 | "outputPath": "dist/packages/utilities",
13 | "main": "packages/utilities/src/index.ts",
14 | "tsConfig": "packages/utilities/tsconfig.lib.json",
15 | "assets": ["packages/utilities/*.md"],
16 | "updateBuildableProjectDepsInPackageJson": true,
17 | "buildableProjectDepsInPackageJsonType": "dependencies"
18 | }
19 | },
20 | "lint": {
21 | "executor": "@nx/linter:eslint",
22 | "outputs": ["{options.outputFile}"],
23 | "options": {
24 | "lintFilePatterns": ["packages/utilities/**/*.ts"]
25 | }
26 | },
27 | "test": {
28 | "executor": "@nx/jest:jest",
29 | "outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
30 | "options": {
31 | "jestConfig": "packages/utilities/jest.config.ts",
32 | "passWithNoTests": true
33 | },
34 | "configurations": {
35 | "ci": {
36 | "ci": true,
37 | "codeCoverage": true
38 | }
39 | }
40 | },
41 | "deploy": {
42 | "executor": "ngx-deploy-npm:deploy",
43 | "options": {
44 | "distFolderPath": "dist/packages/utilities",
45 | "access": "public"
46 | },
47 | "dependsOn": ["build"]
48 | },
49 | "version": {
50 | "executor": "@jscutlery/semver:version",
51 | "options": {
52 | "preset": "conventional"
53 | }
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/packages/dom/src/lib/config/default.ts:
--------------------------------------------------------------------------------
1 | import { Element } from 'domhandler'
2 | import { Config } from './types'
3 | import { styleToString } from '../utilities'
4 |
5 | // Map Slate element names to HTML tag names
6 | // Staightforward transform - no attributes are considered
7 | // Use transforms instead for more complex operations
8 | const ELEMENT_NAME_TAG_MAP = {
9 | p: 'p',
10 | paragraph: 'p',
11 | h1: 'h1',
12 | h2: 'h2',
13 | h3: 'h3',
14 | h4: 'h4',
15 | h5: 'h5',
16 | h6: 'h6',
17 | ul: 'ul',
18 | ol: 'ol',
19 | li: 'li',
20 | blockquote: 'blockquote',
21 | }
22 |
23 | const MARK_ELEMENT_TAG_MAP = {
24 | strikethrough: ['s'],
25 | bold: ['strong'],
26 | underline: ['u'],
27 | italic: ['i'],
28 | code: ['pre', 'code'],
29 | }
30 |
31 | export const config: Config = {
32 | markMap: MARK_ELEMENT_TAG_MAP,
33 | elementMap: ELEMENT_NAME_TAG_MAP,
34 | elementAttributeTransform: ({ node }) => {
35 | if (node.align) {
36 | return {
37 | style: styleToString({
38 | ['text-align']: node.align,
39 | })
40 | }
41 | }
42 | return
43 | },
44 | elementTransforms: {
45 | quote: ({ children = [] }) => {
46 | const p = [new Element('p', {}, children)]
47 | return new Element('blockquote', {}, p)
48 | },
49 | link: ({ node, children = [] }) => {
50 | const attrs: any = {}
51 | if (node.newTab) {
52 | attrs.target = '_blank'
53 | }
54 | return new Element(
55 | 'a',
56 | {
57 | href: node.url,
58 | ...attrs,
59 | },
60 | children,
61 | )
62 | },
63 | },
64 | encodeEntities: false,
65 | alwaysEncodeBreakingEntities: true,
66 | alwaysEncodeCodeEntities: false,
67 | convertLineBreakToBr: false,
68 | }
69 |
--------------------------------------------------------------------------------
/nx.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/nx/schemas/nx-schema.json",
3 | "tasksRunnerOptions": {
4 | "default": {
5 | "runner": "nx-cloud",
6 | "options": {
7 | "cacheableOperations": ["build", "lint", "test", "e2e"],
8 | "accessToken": "MzFjMDBlNWQtZDQ0YS00NzljLTlhNzEtZDA5NTI4NGUzNjcwfHJlYWQtd3JpdGU="
9 | }
10 | }
11 | },
12 | "targetDefaults": {
13 | "build": {
14 | "dependsOn": ["^build"],
15 | "inputs": ["production", "^production"]
16 | },
17 | "lint": {
18 | "inputs": [
19 | "default",
20 | "{workspaceRoot}/.eslintrc.json",
21 | "{workspaceRoot}/.eslintignore"
22 | ]
23 | },
24 | "test": {
25 | "inputs": ["default", "^production", "{workspaceRoot}/jest.preset.js"]
26 | },
27 | "@nx/eslint:lint": {
28 | "inputs": [
29 | "default",
30 | "{workspaceRoot}/.eslintrc.json",
31 | "{workspaceRoot}/.eslintignore"
32 | ],
33 | "cache": true
34 | }
35 | },
36 | "namedInputs": {
37 | "default": ["{projectRoot}/**/*", "sharedGlobals"],
38 | "production": [
39 | "default",
40 | "!{projectRoot}/.eslintrc.json",
41 | "!{projectRoot}/**/?(*.)+(spec|test).[jt]s?(x)?(.snap)",
42 | "!{projectRoot}/tsconfig.spec.json",
43 | "!{projectRoot}/jest.config.[jt]s",
44 | "!{projectRoot}/src/test-setup.[jt]s"
45 | ],
46 | "sharedGlobals": []
47 | },
48 | "workspaceLayout": {
49 | "appsDir": "packages",
50 | "libsDir": "packages"
51 | },
52 | "generators": {
53 | "@nx/react": {
54 | "application": {
55 | "babel": true
56 | },
57 | "library": {
58 | "unitTestRunner": "jest"
59 | }
60 | }
61 | },
62 | "pluginsConfig": {
63 | "@nx/js": {
64 | "analyzeSourceFiles": true
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/packages/dom/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "dom",
3 | "$schema": "../../node_modules/nx/schemas/project-schema.json",
4 | "sourceRoot": "packages/dom/src",
5 | "projectType": "library",
6 | "tags": [],
7 | "targets": {
8 | "build": {
9 | "executor": "@nx/js:tsc",
10 | "outputs": ["{options.outputPath}"],
11 | "options": {
12 | "outputPath": "dist/packages/dom",
13 | "main": "packages/dom/src/index.ts",
14 | "tsConfig": "packages/dom/tsconfig.lib.json",
15 | "assets": ["packages/dom/*.md"],
16 | "updateBuildableProjectDepsInPackageJson": true,
17 | "buildableProjectDepsInPackageJsonType": "dependencies"
18 | }
19 | },
20 | "publish": {
21 | "command": "node tools/scripts/publish.mjs dom {args.ver} {args.tag}",
22 | "dependsOn": ["build"]
23 | },
24 | "lint": {
25 | "executor": "@nx/linter:eslint",
26 | "outputs": ["{options.outputFile}"],
27 | "options": {
28 | "lintFilePatterns": ["packages/dom/**/*.ts"]
29 | }
30 | },
31 | "test": {
32 | "executor": "@nx/jest:jest",
33 | "outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
34 | "options": {
35 | "jestConfig": "packages/dom/jest.config.ts",
36 | "passWithNoTests": true
37 | },
38 | "configurations": {
39 | "ci": {
40 | "ci": true,
41 | "codeCoverage": true
42 | }
43 | }
44 | },
45 | "deploy": {
46 | "executor": "ngx-deploy-npm:deploy",
47 | "options": {
48 | "distFolderPath": "dist/packages/dom",
49 | "access": "public"
50 | },
51 | "dependsOn": ["build"]
52 | },
53 | "version": {
54 | "executor": "@jscutlery/semver:version",
55 | "options": {
56 | "preset": "conventional"
57 | }
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/packages/html/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "html",
3 | "$schema": "../../node_modules/nx/schemas/project-schema.json",
4 | "sourceRoot": "packages/html/src",
5 | "projectType": "library",
6 | "tags": [],
7 | "targets": {
8 | "build": {
9 | "executor": "@nx/js:tsc",
10 | "outputs": ["{options.outputPath}"],
11 | "options": {
12 | "outputPath": "dist/packages/html",
13 | "main": "packages/html/src/index.ts",
14 | "tsConfig": "packages/html/tsconfig.lib.json",
15 | "assets": ["packages/html/*.md"],
16 | "updateBuildableProjectDepsInPackageJson": true,
17 | "buildableProjectDepsInPackageJsonType": "dependencies"
18 | }
19 | },
20 | "publish": {
21 | "command": "node tools/scripts/publish.mjs html {args.ver} {args.tag}",
22 | "dependsOn": ["build"]
23 | },
24 | "lint": {
25 | "executor": "@nx/linter:eslint",
26 | "outputs": ["{options.outputFile}"],
27 | "options": {
28 | "lintFilePatterns": ["packages/html/**/*.ts"]
29 | }
30 | },
31 | "test": {
32 | "executor": "@nx/jest:jest",
33 | "outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
34 | "options": {
35 | "jestConfig": "packages/html/jest.config.ts",
36 | "passWithNoTests": true
37 | },
38 | "configurations": {
39 | "ci": {
40 | "ci": true,
41 | "codeCoverage": true
42 | }
43 | }
44 | },
45 | "deploy": {
46 | "executor": "ngx-deploy-npm:deploy",
47 | "options": {
48 | "distFolderPath": "dist/packages/html",
49 | "access": "public"
50 | },
51 | "dependsOn": ["build"]
52 | },
53 | "version": {
54 | "executor": "@jscutlery/semver:version",
55 | "options": {
56 | "preset": "conventional"
57 | }
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/packages/template/README.md:
--------------------------------------------------------------------------------
1 | # @slate-serializers/template
2 |
3 | Render Slate JSON as a combination of HTML and custom serializers that you pass to the configuration.
4 |
5 | Returns an array. By default, each array element is the output of `@slate-serializers/html`. If you pass a custom serializers, your serialized output will be included in this array. These serializers match on top-level Slate nodes only using the `type` attribute.
6 |
7 | ## Usage
8 |
9 | ### slateToTemplate
10 |
11 | ```ts
12 | import { slateToTemplate } from '@slate-serializers/template'
13 |
14 | const slate = [
15 | {
16 | children: [
17 | {
18 | text: 'Heading 1',
19 | },
20 | ],
21 | type: 'h1',
22 | },
23 | {
24 | children: [
25 | {
26 | text: 'Paragraph 1',
27 | },
28 | ],
29 | type: 'p',
30 | },
31 | ]
32 |
33 | const serializedToArray = slateToTemplate(slate)
34 | // output
35 | // ["Heading 1
", "Paragraph 1
"]
36 | ```
37 |
38 | ### Configuration
39 |
40 | Define a custom serializer to include any output for top-level Slate nodes of a given type.
41 |
42 | ```ts
43 | const slate = [
44 | {
45 | children: [
46 | {
47 | text: 'Paragraph',
48 | },
49 | ],
50 | type: 'p',
51 | },
52 | {
53 | children: [
54 | {
55 | buttonType: 'primary',
56 | text: 'Button',
57 | },
58 | ],
59 | type: 'button',
60 | },
61 | ];
62 |
63 | const config: SlateToTemplateConfig = {
64 | ...defaultTemplateConfig,
65 | customElementSerializers: {
66 | button: ({ node }) => {
67 | return () =>
68 | ``;
69 | },
70 | },
71 | };
72 |
73 | const serializedToArray = slateToTemplate(slate)
74 | // output
75 | // ["Paragraph
", [Function]]
76 | ```
77 |
--------------------------------------------------------------------------------
/packages/html/src/lib/tests/htmlToSlate/configuration/textTagsVselementTags.spec.ts:
--------------------------------------------------------------------------------
1 | import {
2 | htmlToSlate,
3 | HtmlToSlateConfig,
4 | htmlToSlateConfig,
5 | } from '@slate-serializers/html';
6 | import { getAttributeValue } from 'domutils';
7 |
8 | describe('htmlToSlate configuration: textTags & elementTags', () => {
9 | it('converts element/text tags and their attributes to Slate nodes/node attributes using a custom configuration', () => {
10 | const html =
11 | 'Published:
';
12 | const config: HtmlToSlateConfig = {
13 | ...htmlToSlateConfig,
14 | textTags: {
15 | ...htmlToSlateConfig.textTags,
16 | time: (el) => ({
17 | ...(el && {
18 | datetime: getAttributeValue(el, 'datetime'),
19 | }),
20 | time: true,
21 | }),
22 | },
23 | elementTags: {
24 | ...htmlToSlateConfig.elementTags,
25 | article: (el) => ({
26 | ...(el && {
27 | id: getAttributeValue(el, 'id'),
28 | }),
29 | type: 'article',
30 | }),
31 | },
32 | };
33 | expect(htmlToSlate(html, config)).toMatchInlineSnapshot(`
34 | [
35 | {
36 | "children": [
37 | {
38 | "children": [
39 | {
40 | "text": "Published: ",
41 | },
42 | {
43 | "bold": true,
44 | "datetime": "2016-01-20",
45 | "text": "20 January 2016",
46 | "time": true,
47 | },
48 | ],
49 | "type": "p",
50 | },
51 | ],
52 | "id": "main",
53 | "type": "article",
54 | },
55 | ]
56 | `);
57 | });
58 | });
59 |
--------------------------------------------------------------------------------
/packages/html/src/lib/serializers/htmlToSlate/config/default.ts:
--------------------------------------------------------------------------------
1 | import { getAttributeValue } from 'domutils'
2 | import { Config } from './types'
3 | import { extractCssFromStyle } from '@slate-serializers/dom'
4 |
5 | export const config: Config = {
6 | elementAttributeTransform: ({ el }) => {
7 | const attrs: { [key: string]: string } = {}
8 | const elementStyleMap: { [key: string]: string } = {
9 | align: 'textAlign',
10 | }
11 | Object.keys(elementStyleMap).forEach((slateKey) => {
12 | const cssProperty = elementStyleMap[slateKey]
13 | const cssValue = extractCssFromStyle(el, cssProperty)
14 | if (cssValue) {
15 | attrs[slateKey] = cssValue
16 | }
17 | })
18 | return attrs
19 | },
20 | elementTags: {
21 | a: (el) => ({
22 | type: 'link',
23 | newTab: el && getAttributeValue(el, 'target') === '_blank',
24 | url: el && getAttributeValue(el, 'href'),
25 | }),
26 | blockquote: () => ({ type: 'blockquote' }),
27 | h1: () => ({ type: 'h1' }),
28 | h2: () => ({ type: 'h2' }),
29 | h3: () => ({ type: 'h3' }),
30 | h4: () => ({ type: 'h4' }),
31 | h5: () => ({ type: 'h5' }),
32 | h6: () => ({ type: 'h6' }),
33 | li: () => ({ type: 'li' }),
34 | ol: () => ({ type: 'ol' }),
35 | p: () => ({ type: 'p' }),
36 | ul: () => ({ type: 'ul' }),
37 | },
38 | textTags: {
39 | code: () => ({ code: true }),
40 | pre: () => ({ code: true }),
41 | del: () => ({ strikethrough: true }),
42 | em: () => ({ italic: true }),
43 | i: () => ({ italic: true }),
44 | s: () => ({ strikethrough: true }),
45 | strong: () => ({ bold: true }),
46 | u: () => ({ underline: true }),
47 | },
48 | htmlPreProcessString: (html) => html.replace(/]*>/g, '').replace(/<\/pre>/g, ''),
49 | filterWhitespaceNodes: true,
50 | convertBrToLineBreak: true,
51 | trimWhiteSpace: true,
52 | }
53 |
--------------------------------------------------------------------------------
/packages/tests/src/lib/html/withStylesInLeaf.spec.ts:
--------------------------------------------------------------------------------
1 | import { slateToHtml, htmlToSlateConfig, HtmlToSlateConfig, htmlToSlate } from '@slate-serializers/html'
2 | import { Element } from 'domhandler'
3 | import { SlateToDomConfig, slateToDomConfig } from '@slate-serializers/dom'
4 | import { transformStyleStringToObject } from '@slate-serializers/utilities'
5 | import { stylesMixedInFixtures as fixtures } from '../tests'
6 |
7 | export const slateToDomConfigStyleObject: SlateToDomConfig = {
8 | ...slateToDomConfig,
9 | markTransforms: {
10 | ...slateToDomConfig.markTransforms,
11 | backgroundColor: ({ node }) => {
12 | return new Element('span', {
13 | style: `background-color:${node.backgroundColor};`
14 | })
15 | },
16 | color: ({ node }) => {
17 | return new Element('span', {
18 | style: `color:${node.color};`
19 | })
20 | },
21 | fontFamily: ({ node }) => {
22 | return new Element('span', {
23 | style: `font-family:${node.fontFamily};`
24 | })
25 | },
26 | fontSize: ({ node }) => {
27 | return new Element('span', {
28 | style: `font-size:${node.fontSize};`
29 | })
30 | }
31 | },
32 | defaultTag: 'p',
33 | }
34 |
35 | export const htmlToSlateConfigStyleObject: HtmlToSlateConfig = {
36 | ...htmlToSlateConfig,
37 | textTags: {
38 | ...htmlToSlateConfig.textTags,
39 | span: (el) => {
40 | const style = el?.attribs && transformStyleStringToObject(el.attribs['style'] || ``)
41 | return {
42 | ...(style && style)
43 | }
44 | }
45 | },
46 | }
47 |
48 | describe('styles as attributes on a leaf', () => {
49 | for (const fixture of fixtures) {
50 | it(`${fixture.name}`, () => {
51 | expect(slateToHtml(fixture.slate, slateToDomConfigStyleObject )).toEqual(fixture.html)
52 | expect(htmlToSlate(fixture.html, htmlToSlateConfigStyleObject)).toEqual(fixture.slate)
53 | })
54 | }
55 | })
56 |
--------------------------------------------------------------------------------
/packages/tests/src/lib/html/expectedDifferencesHtmlSlateBothWays.spec.ts:
--------------------------------------------------------------------------------
1 | import { htmlToSlate, slateToHtml, payloadHtmlToSlateConfig } from '@slate-serializers/html'
2 |
3 | import { combinedFixtures } from './../tests'
4 |
5 | import { slateToDomConfig, payloadSlateToDomConfig } from '@slate-serializers/dom'
6 |
7 | describe('HTML to Slate JSON transforms', () => {
8 | describe('Combined', () => {
9 | const fixtures = combinedFixtures
10 | for (const fixture of fixtures) {
11 | it(`${fixture.name}`, () => {
12 | expect(slateToHtml(fixture.slateOriginal, { ...slateToDomConfig, defaultTag: 'p' })).toEqual(fixture.html)
13 | expect(htmlToSlate(fixture.html)).toEqual(fixture.slateReserialized)
14 | expect(slateToHtml(fixture.slateReserialized)).toEqual(fixture.html)
15 | })
16 | }
17 | })
18 | })
19 |
20 | describe('attribute mapping', () => {
21 | const slate = [
22 | {
23 | children: [
24 | {
25 | text: 'Some text before an inline link ',
26 | },
27 | {
28 | type: 'link',
29 | linkType: 'custom',
30 | url: 'https://github.com/thompsonsj/slate-serializers',
31 | newTab: true,
32 | children: [
33 | {
34 | text: 'slate-serializers | GitHub',
35 | },
36 | ],
37 | },
38 | {
39 | text: '.',
40 | },
41 | ],
42 | type: 'p',
43 | },
44 | ]
45 | const html =
46 | 'Some text before an inline link slate-serializers | GitHub.
'
47 |
48 | it('slateToHtml adds a custom data attribute', () => {
49 | expect(slateToHtml(slate, payloadSlateToDomConfig)).toEqual(html)
50 | })
51 |
52 | it('htmlToSlate adds a custom data attribute', () => {
53 | expect(htmlToSlate(html, payloadHtmlToSlateConfig)).toEqual(slate)
54 | })
55 | })
56 |
--------------------------------------------------------------------------------
/packages/slate-serializers/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "slate-serializers",
3 | "$schema": "../../node_modules/nx/schemas/project-schema.json",
4 | "sourceRoot": "packages/slate-serializers/src",
5 | "projectType": "library",
6 | "tags": [],
7 | "targets": {
8 | "build": {
9 | "executor": "@nx/js:tsc",
10 | "outputs": ["{options.outputPath}"],
11 | "options": {
12 | "outputPath": "dist/packages/slate-serializers",
13 | "main": "packages/slate-serializers/src/index.ts",
14 | "tsConfig": "packages/slate-serializers/tsconfig.lib.json",
15 | "assets": ["packages/slate-serializers/*.md"],
16 | "updateBuildableProjectDepsInPackageJson": true,
17 | "buildableProjectDepsInPackageJsonType": "dependencies"
18 | }
19 | },
20 | "publish": {
21 | "command": "node tools/scripts/publish.mjs slate-serializers {args.ver} {args.tag}",
22 | "dependsOn": ["build"]
23 | },
24 | "lint": {
25 | "executor": "@nx/linter:eslint",
26 | "outputs": ["{options.outputFile}"],
27 | "options": {
28 | "lintFilePatterns": ["packages/slate-serializers/**/*.ts"]
29 | }
30 | },
31 | "test": {
32 | "executor": "@nx/jest:jest",
33 | "outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
34 | "options": {
35 | "jestConfig": "packages/slate-serializers/jest.config.ts",
36 | "passWithNoTests": true
37 | },
38 | "configurations": {
39 | "ci": {
40 | "ci": true,
41 | "codeCoverage": true
42 | }
43 | }
44 | },
45 | "deploy": {
46 | "executor": "ngx-deploy-npm:deploy",
47 | "options": {
48 | "distFolderPath": "dist/packages/slate-serializers",
49 | "access": "public"
50 | },
51 | "dependsOn": ["build"]
52 | },
53 | "version": {
54 | "executor": "@jscutlery/semver:version",
55 | "options": {
56 | "preset": "conventional"
57 | }
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/packages/html/src/lib/serializers/htmlToSlate/whitespace.ts:
--------------------------------------------------------------------------------
1 | export type Context = 'preserve' | 'block' | 'inline' | ''
2 |
3 | import { isBlock } from "./../../utilities/blocks"
4 |
5 | interface IprocessTextValue {
6 | text: string
7 | context?: Context
8 | isInlineStart?: boolean
9 | isInlineEnd?: boolean
10 | isNextSiblingBlock?: boolean
11 | shouldTrimWhiteSpace?: boolean
12 | }
13 |
14 | export const processTextValue = ({
15 | text,
16 | context = '',
17 | isInlineStart = false,
18 | isInlineEnd = false,
19 | isNextSiblingBlock = false,
20 | shouldTrimWhiteSpace = true
21 | }: IprocessTextValue): string => {
22 | let parsed = text
23 | if (context === 'preserve') {
24 | return parsed
25 | }
26 | parsed = minifyText(parsed, shouldTrimWhiteSpace)
27 | if (context === 'block') {
28 | // is this the start of inline content after a block element?
29 | if (isInlineStart) {
30 | parsed = parsed.trimStart()
31 | }
32 | // is this the end of inline content in a block element?
33 | if (isInlineEnd || isNextSiblingBlock) {
34 | parsed = parsed.trimEnd()
35 | }
36 | }
37 | return parsed
38 | }
39 |
40 | export const minifyText = (str: string, shouldTrimWhiteSpace: boolean) => {
41 | return shouldTrimWhiteSpace ? reduceToSingleSpaces(replaceNewlines(str)) : replaceNewlines(str)
42 | }
43 |
44 | const replaceNewlines = (str: string) => {
45 | return str.replace(/(?:\r\n|\r|\n)/g, ' ')
46 | }
47 |
48 | const reduceToSingleSpaces = (str: string) => {
49 | return str.replace(/ +(?= )/g, '')
50 | }
51 |
52 | export const isAllWhitespace = (str: string) => {
53 | return !/[^\t\n\r ]/.test(str)
54 | }
55 |
56 | export const getContext = (tagName: string): Context => {
57 | if (!tagName || tagName.trim() === '') {
58 | return ''
59 | }
60 | if (preserveWhitespace(tagName)) {
61 | return 'preserve'
62 | }
63 | if (isBlock(tagName)) {
64 | return 'block'
65 | }
66 | return 'inline'
67 | }
68 |
69 | const preserveWhitespace = (tagName: string) => {
70 | return ['code', 'pre', 'xmp'].includes(tagName)
71 | }
72 |
--------------------------------------------------------------------------------
/packages/tests/src/lib/react/components.spec.tsx:
--------------------------------------------------------------------------------
1 | import Button from './testComponents/Button'
2 | import { render, fireEvent, cleanup } from '@testing-library/react'
3 | import { SlateToReact, slateToReactConfig as defaultReactConfig, SlateToReactConfig } from '@slate-serializers/react'
4 |
5 | afterEach(cleanup)
6 |
7 | const defaultProps = {
8 | onClick: jest.fn(),
9 | children: <>Submit>,
10 | }
11 |
12 | describe('simple button tests', () => {
13 | test('button renders with correct text', () => {
14 | const { queryByText, rerender } = render()
15 | expect(queryByText('Submit')).toBeTruthy()
16 |
17 | // Change props
18 | rerender(
",
62 | ]
63 | `);
64 | });
65 |
66 | test('render Slate node as p tag if defaultTag is set', async () => {
67 | const slate = [
68 | {
69 | children: [
70 | {
71 | text: 'Paragraph',
72 | },
73 | ],
74 | },
75 | ];
76 |
77 | const config = {
78 | ...defaultTemplateConfig,
79 | defaultTag: 'p',
80 | };
81 |
82 | const tree = slateToTemplate(slate, config);
83 | expect(tree).toMatchInlineSnapshot(`
84 | [
85 | "Paragraph
",
86 | ]
87 | `);
88 | });
89 |
90 | test('respects a function passed as an element transform', async () => {
91 | const slate = [
92 | {
93 | children: [
94 | {
95 | text: 'Paragraph',
96 | },
97 | ],
98 | type: 'p',
99 | },
100 | {
101 | children: [
102 | {
103 | buttonType: 'primary',
104 | text: 'Button',
105 | },
106 | ],
107 | type: 'button',
108 | },
109 | ];
110 |
111 | const config: SlateToTemplateConfig = {
112 | ...defaultTemplateConfig,
113 | customElementSerializers: {
114 | button: ({ node }) => {
115 | return () =>
116 | `Button HTML string generated by function`;
117 | },
118 | },
119 | };
120 |
121 | const tree = slateToTemplate(slate, config);
122 | expect(tree).toMatchInlineSnapshot(`
123 | [
124 | "Paragraph
",
125 | [Function],
126 | ]
127 | `);
128 | });
129 | });
130 |
--------------------------------------------------------------------------------
/packages/html/src/lib/tests/htmlToSlate/configuration/textTags.spec.ts:
--------------------------------------------------------------------------------
1 | import {
2 | htmlToSlate,
3 | HtmlToSlateConfig,
4 | htmlToSlateConfig,
5 | } from '@slate-serializers/html';
6 | import { getAttributeValue } from 'domutils';
7 |
8 | describe('htmlToSlate configuration: textTags', () => {
9 | it('converts text tags to Slate node attributes from the default configuration', () => {
10 | const html =
11 | 'I am bold text whereas I am italic bold text with the last four words having strikethrough.
';
12 | expect(htmlToSlate(html)).toMatchInlineSnapshot(`
13 | [
14 | {
15 | "children": [
16 | {
17 | "bold": true,
18 | "text": "I am bold text",
19 | },
20 | {
21 | "text": " whereas ",
22 | },
23 | {
24 | "bold": true,
25 | "italic": true,
26 | "text": "I am italic bold text with the last ",
27 | },
28 | {
29 | "bold": true,
30 | "italic": true,
31 | "strikethrough": true,
32 | "text": "four words having strikethrough",
33 | },
34 | {
35 | "text": ".",
36 | },
37 | ],
38 | "type": "p",
39 | },
40 | ]
41 | `);
42 | });
43 |
44 | it('converts text tags to Slate node attributes using a custom configuration', () => {
45 | const html =
46 | 'I am bold text whereas I am subscript italic bold text.
';
47 | const config = {
48 | ...htmlToSlateConfig,
49 | textTags: {
50 | ...htmlToSlateConfig.textTags,
51 | sub: () => ({ subscript: true }),
52 | },
53 | };
54 | expect(htmlToSlate(html, config)).toMatchInlineSnapshot(`
55 | [
56 | {
57 | "children": [
58 | {
59 | "bold": true,
60 | "text": "I am bold text",
61 | },
62 | {
63 | "text": " whereas ",
64 | },
65 | {
66 | "bold": true,
67 | "italic": true,
68 | "subscript": true,
69 | "text": "I am subscript italic bold text",
70 | },
71 | {
72 | "text": ".",
73 | },
74 | ],
75 | "type": "p",
76 | },
77 | ]
78 | `);
79 | });
80 |
81 | it('converts text tags and their attributes to Slate node attributes using a custom configuration', () => {
82 | const html =
83 | 'Published:
';
84 | const config: HtmlToSlateConfig = {
85 | ...htmlToSlateConfig,
86 | textTags: {
87 | ...htmlToSlateConfig.textTags,
88 | time: (el) => ({
89 | ...(el && {
90 | datetime: getAttributeValue(el, 'datetime'),
91 | }),
92 | time: true,
93 | }),
94 | },
95 | };
96 | expect(htmlToSlate(html, config)).toMatchInlineSnapshot(`
97 | [
98 | {
99 | "children": [
100 | {
101 | "text": "Published: ",
102 | },
103 | {
104 | "datetime": "2016-01-20",
105 | "text": "20 January 2016",
106 | "time": true,
107 | },
108 | ],
109 | "type": "p",
110 | },
111 | ]
112 | `);
113 | });
114 | });
115 |
--------------------------------------------------------------------------------
/packages/html/src/lib/tests/htmlToSlate/configuration/elementAttributeTransform.spec.ts:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line @nx/enforce-module-boundaries
2 | import {
3 | htmlToSlate,
4 | HtmlToSlateConfig,
5 | htmlToSlateConfig,
6 | } from '@slate-serializers/html';
7 | import { getAttributeValue } from 'domutils';
8 |
9 | describe('htmlToSlate configuration: elementAttributeTransform', () => {
10 | it('converts element tags to Slate nodes from the default configuration', () => {
11 | const html =
12 | 'Heading 1
Paragraph 1
Lists
- Unordered list item 1
- Unordered list item 2
- Ordered list item 1
- Ordered list item 2
Quotes
Quote
';
13 | const config: HtmlToSlateConfig = {
14 | ...htmlToSlateConfig,
15 | elementAttributeTransform: ({ el }) => {
16 | const attribs: { [key: string]: string } = {};
17 | const id = getAttributeValue(el, 'id');
18 | if (id) {
19 | attribs['id'] = id;
20 | }
21 | return attribs;
22 | },
23 | };
24 | expect(htmlToSlate(html, config)).toMatchInlineSnapshot(`
25 | [
26 | {
27 | "children": [
28 | {
29 | "text": "Heading 1",
30 | },
31 | ],
32 | "id": "h1-1",
33 | "type": "h1",
34 | },
35 | {
36 | "children": [
37 | {
38 | "text": "Paragraph 1",
39 | },
40 | ],
41 | "id": "p-1",
42 | "type": "p",
43 | },
44 | {
45 | "children": [
46 | {
47 | "text": "Lists",
48 | },
49 | ],
50 | "id": "h2-1",
51 | "type": "h2",
52 | },
53 | {
54 | "children": [
55 | {
56 | "children": [
57 | {
58 | "text": "Unordered list item 1",
59 | },
60 | ],
61 | "id": "li-1",
62 | "type": "li",
63 | },
64 | {
65 | "children": [
66 | {
67 | "text": "Unordered list item 2",
68 | },
69 | ],
70 | "id": "li-2",
71 | "type": "li",
72 | },
73 | ],
74 | "id": "ul-1",
75 | "type": "ul",
76 | },
77 | {
78 | "children": [
79 | {
80 | "children": [
81 | {
82 | "text": "Ordered list item 1",
83 | },
84 | ],
85 | "id": "li-3",
86 | "type": "li",
87 | },
88 | {
89 | "children": [
90 | {
91 | "text": "Ordered list item 2",
92 | },
93 | ],
94 | "id": "li-4",
95 | "type": "li",
96 | },
97 | ],
98 | "id": "ol-1",
99 | "type": "ol",
100 | },
101 | {
102 | "children": [
103 | {
104 | "text": "Quotes",
105 | },
106 | ],
107 | "id": "h2-2",
108 | "type": "h2",
109 | },
110 | {
111 | "children": [
112 | {
113 | "text": "Quote",
114 | },
115 | ],
116 | "id": "blockquote-1",
117 | "type": "blockquote",
118 | },
119 | ]
120 | `);
121 | });
122 | });
123 |
--------------------------------------------------------------------------------
/packages/html/src/lib/tests/slateToHtml/configuration/elementMap.spec.ts:
--------------------------------------------------------------------------------
1 | import { slateToHtml, slateToHtmlConfig } from '@slate-serializers/html'
2 |
3 | describe("slateToHtml elementMap", () => {
4 | it('processes an element map value', () => {
5 | const html = 'Heading 1
'
6 | const slate = [
7 | {
8 | type: 'heading-one',
9 | children: [
10 | {
11 | text: 'Heading 1',
12 | },
13 | ],
14 | },
15 | ]
16 | const config = {
17 | ...slateToHtmlConfig,
18 | elementMap: {
19 | ['heading-one']: 'h1',
20 | },
21 | }
22 | expect(slateToHtml(slate, config)).toEqual(html)
23 | })
24 |
25 | it('processes element map values for a table', () => {
26 | const html = '| Heading Column 1 | Heading Column 2 |
|---|
| Cell Row 1 Column 1 | Cell Row 1 Column 2 |
| Cell Row 2 Column 1 | Cell Row 2 Column 2 |
'
27 | const slate = [{
28 | "type": "table",
29 | "children": [
30 | {
31 | "type": "thead",
32 | "children": [
33 | {
34 | "type": "tr",
35 | "children": [
36 | {
37 | "type": "th",
38 | "children": [
39 | {
40 | "text": "Heading Column 1"
41 | }
42 | ]
43 | },
44 | {
45 | "type": "th",
46 | "children": [
47 | {
48 | "text": "Heading Column 2"
49 | }
50 | ]
51 | },
52 | ]
53 | }
54 | ]
55 | },
56 | {
57 | "type": "tbody",
58 | "children": [
59 | {
60 | "type": "tr",
61 | "children": [
62 | {
63 | "type": "td",
64 | "children": [
65 | {
66 | "text": "Cell Row 1 Column 1"
67 | }
68 | ]
69 | },
70 | {
71 | "type": "td",
72 | "children": [
73 | {
74 | "text": "Cell Row 1 Column 2"
75 | }
76 | ]
77 | },
78 | ]
79 | },
80 | {
81 | "type": "tbody",
82 | "children": [
83 | {
84 | "type": "tr",
85 | "children": [
86 | {
87 | "type": "td",
88 | "children": [
89 | {
90 | "text": "Cell Row 2 Column 1"
91 | }
92 | ]
93 | },
94 | {
95 | "type": "td",
96 | "children": [
97 | {
98 | "text": "Cell Row 2 Column 2"
99 | }
100 | ]
101 | },
102 | ]
103 | },
104 | ],
105 | },
106 | ],
107 | },
108 | ],
109 | }]
110 | const config = {
111 | ...slateToHtmlConfig,
112 | elementMap: {
113 | ...slateToHtmlConfig.elementMap,
114 | table: "table",
115 | tr: "tr",
116 | td: "td",
117 | thead: "thead",
118 | th: "th",
119 | tbody: "tbody",
120 | },
121 | }
122 | expect(slateToHtml(slate, config)).toEqual(html)
123 | })
124 | })
125 |
--------------------------------------------------------------------------------
/packages/html/src/lib/tests/htmlToSlate/configuration/convertBrToLineBreak.spec.ts:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line @nx/enforce-module-boundaries
2 | import {
3 | htmlToSlate,
4 | htmlToSlateConfig,
5 | } from '@slate-serializers/html';
6 | import { Descendant } from 'slate';
7 |
8 | describe("htmlToSlate configuration: convertBrToLineBreak", () => {
9 | it('converts a br tag to a line break', () => {
10 | const html = 'Line 1
Line 2'
11 | const slate = [
12 | {
13 | children: [
14 | {
15 | text: 'Line 1',
16 | },
17 | ],
18 | },
19 | {
20 | children: [
21 | {
22 | text: '',
23 | },
24 | ],
25 | },
26 | {
27 | children: [
28 | {
29 | text: 'Line 2',
30 | },
31 | ],
32 | },
33 | ]
34 | expect(htmlToSlate(html)).toEqual(slate)
35 | })
36 |
37 | it('converts a br tag in a paragraph to a line break', () => {
38 | const html = 'Paragraph with line
breaks.
'
39 | const slate = [
40 | {
41 | children: [
42 | {
43 | text: 'Paragraph with line',
44 | },
45 | {
46 | text: '\n',
47 | },
48 | {
49 | text: '\n',
50 | },
51 | {
52 | text: 'breaks.',
53 | },
54 | ],
55 | type: 'p',
56 | },
57 | ]
58 | expect(htmlToSlate(html)).toEqual(slate)
59 | })
60 |
61 | it('does nothing with a br tag if convertBrToLineBreak is false', () => {
62 | const html = '
'
63 | const slate: Descendant[] = []
64 | expect(htmlToSlate(html, { ...htmlToSlateConfig, convertBrToLineBreak: false })).toEqual(slate)
65 | })
66 |
67 | it('converts a br tag to a slate node if defined as an element tag', () => {
68 | const html = '
'
69 | const slate = [
70 | {
71 | children: [
72 | {
73 | text: '',
74 | },
75 | ],
76 | type: 'br',
77 | },
78 | ]
79 | expect(
80 | htmlToSlate(html, {
81 | ...htmlToSlateConfig,
82 | convertBrToLineBreak: false,
83 | elementTags: {
84 | ...htmlToSlateConfig.elementTags,
85 | br: () => ({ type: 'br' }),
86 | },
87 | }),
88 | ).toEqual(slate)
89 | })
90 |
91 | it('adds an empty text element in place of a br tag as a line break', () => {
92 | const html = 'Line 1
\nLine 2'
93 | const slate = [
94 | {
95 | children: [
96 | {
97 | text: 'Line 1',
98 | },
99 | ],
100 | },
101 | {
102 | children: [
103 | {
104 | text: '',
105 | },
106 | ],
107 | },
108 | {
109 | children: [
110 | {
111 | text: 'Line 2',
112 | },
113 | ],
114 | },
115 | ]
116 | expect(
117 | htmlToSlate(html, {
118 | ...htmlToSlateConfig,
119 | convertBrToLineBreak: true,
120 | }),
121 | ).toEqual(slate)
122 | })
123 |
124 | it('adds a line break in place of a br tag inside of a block element', () => {
125 | const html = 'Line 1
\nLine 2
'
126 | const slate = [
127 | {
128 | type: 'p',
129 | children: [
130 | {
131 | text: 'Line 1',
132 | },
133 | {
134 | text: '\n',
135 | },
136 | {
137 | text: 'Line 2',
138 | },
139 | ],
140 | },
141 | ]
142 | expect(
143 | htmlToSlate(html, {
144 | ...htmlToSlateConfig,
145 | convertBrToLineBreak: true,
146 | }),
147 | ).toEqual(slate)
148 | })
149 | })
150 |
--------------------------------------------------------------------------------
/packages/react/src/lib/__tests__/payload-crowdin-sync-dev/payload-internal-link.spec.tsx:
--------------------------------------------------------------------------------
1 | import '@testing-library/jest-dom';
2 | import { render } from '@testing-library/react';
3 | // eslint-disable-next-line @nx/enforce-module-boundaries
4 | import {
5 | SlateToReact,
6 | SlateToReactConfig,
7 | payloadSlateToReactConfig,
8 | } from '@slate-serializers/react';
9 | import { Post } from './payload-types';
10 |
11 | // Type guard for Post
12 | const isPost = (post: Post): post is Post => {
13 | return typeof post.id === 'string';
14 | };
15 |
16 | describe('React conversion - Payload CMS fixtures', () => {
17 | const fixture: Post['content'] = [
18 | {
19 | children: [
20 | {
21 | text: 'Example post content with an ',
22 | },
23 | {
24 | children: [
25 | {
26 | text: 'internal link to another post',
27 | },
28 | ],
29 | doc: {
30 | value: {
31 | id: '65cd31a44e1969ec392ec2d9',
32 | title: 'Example post',
33 | author: '65cd31594e1969ec392ec280',
34 | publishedDate: '2024-02-14T12:00:00.000Z',
35 | category: '65cd319e4e1969ec392ec2d5',
36 | content: [
37 | {
38 | children: [
39 | {
40 | text: 'Example post contents.',
41 | },
42 | ],
43 | },
44 | ],
45 | status: 'published',
46 | createdAt: '2024-02-14T21:33:24.860Z',
47 | updatedAt: '2024-02-14T21:33:24.860Z',
48 | },
49 | relationTo: 'posts',
50 | },
51 | linkType: 'internal',
52 | type: 'link',
53 | },
54 | {
55 | text: '.',
56 | },
57 | ],
58 | },
59 | ];
60 |
61 | test('convert internal link', async () => {
62 | const tree = render(
63 | ,
64 | );
65 | expect(tree.container).toMatchInlineSnapshot(`
66 |
77 | `);
78 | });
79 |
80 | test('convert internal link with customised config', async () => {
81 | const config: SlateToReactConfig = {
82 | ...payloadSlateToReactConfig,
83 | elementTransforms: {
84 | ...payloadSlateToReactConfig.elementTransforms,
85 | link: ({ node, children = [] }) => {
86 | const attrs: any = {};
87 | if (node.linkType) {
88 | attrs['data-link-type'] = node.linkType;
89 | }
90 | if (node.newTab) {
91 | attrs.target = '_blank';
92 | }
93 | const doc = node.doc?.value;
94 | if (isPost(doc) && node.linkType === 'internal') {
95 | attrs.href = `https://example.com/${doc.id}`;
96 | }
97 | return (
98 |
99 | {children}
100 |
101 | );
102 | },
103 | },
104 | };
105 |
106 | const tree = render();
107 | expect(tree.container).toMatchInlineSnapshot(`
108 |
120 | `);
121 | });
122 | });
123 |
--------------------------------------------------------------------------------
/packages/html/src/lib/serializers/htmlToSlate/whitespace.spec.ts:
--------------------------------------------------------------------------------
1 | import { processTextValue } from './whitespace'
2 |
3 | describe('processTextValue function', () => {
4 | describe('"preserve" context', () => {
5 | it('does not do anything if the context is "preserve"', () => {
6 | const fixture = ` after on space
7 | and then line breaks and spaces`
8 | expect(
9 | processTextValue({
10 | text: fixture,
11 | context: 'preserve',
12 | }),
13 | ).toEqual(fixture)
14 | })
15 | })
16 |
17 | describe('"block" context', () => {
18 | const context = 'block'
19 | const fixture = ' text '
20 | it('removes space from the start of a string if isInlineStart is true', () => {
21 | const expected = 'text '
22 | expect(
23 | processTextValue({
24 | text: fixture,
25 | context: context,
26 | isInlineStart: true,
27 | }),
28 | ).toEqual(expected)
29 | })
30 |
31 | it('removes space from the end of a string if isInlineEnd is true', () => {
32 | const expected = ' text'
33 | expect(
34 | processTextValue({
35 | text: fixture,
36 | context: context,
37 | isInlineEnd: true,
38 | }),
39 | ).toEqual(expected)
40 | })
41 |
42 | it('removes space from both ends of a string if isInlineStart and isInlineEnd is true', () => {
43 | const expected = 'text'
44 | expect(
45 | processTextValue({
46 | text: fixture,
47 | context: context,
48 | isInlineStart: true,
49 | isInlineEnd: true,
50 | }),
51 | ).toEqual(expected)
52 | })
53 | })
54 |
55 | describe('"inline" context', () => {
56 | const context = 'inline'
57 | const fixture = ' text '
58 | it('normalizes space at the start of a string if isInlineStart is true', () => {
59 | const expected = ' text '
60 | expect(
61 | processTextValue({
62 | text: fixture,
63 | context: context,
64 | isInlineStart: true,
65 | }),
66 | ).toEqual(expected)
67 | })
68 |
69 | it('normalizes space at the the end of a string if isInlineEnd is true', () => {
70 | const expected = ' text '
71 | expect(
72 | processTextValue({
73 | text: fixture,
74 | context: context,
75 | isInlineEnd: true,
76 | }),
77 | ).toEqual(expected)
78 | })
79 |
80 | it('normalizes space at both ends of a string if isInlineStart and isInlineEnd is true', () => {
81 | const expected = ' text '
82 | expect(
83 | processTextValue({
84 | text: fixture,
85 | context: context,
86 | isInlineStart: true,
87 | isInlineEnd: true,
88 | }),
89 | ).toEqual(expected)
90 | })
91 | })
92 |
93 | describe('"block", "inline" or "" context', () => {
94 | const fixture = `before line break
95 | after line break`
96 | const expected = 'before line break after line break'
97 | it('replaces line breaks with a space in an empty context', () => {
98 | expect(
99 | processTextValue({
100 | text: fixture,
101 | context: '',
102 | }),
103 | ).toEqual(expected)
104 | })
105 |
106 | it('replaces line breaks with a space in an "inline" context', () => {
107 | expect(
108 | processTextValue({
109 | text: fixture,
110 | context: 'inline',
111 | }),
112 | ).toEqual(expected)
113 | })
114 |
115 | it('replaces line breaks with a space in a "block context', () => {
116 | expect(
117 | processTextValue({
118 | text: fixture,
119 | context: 'block',
120 | }),
121 | ).toEqual(expected)
122 | })
123 | })
124 | })
125 |
--------------------------------------------------------------------------------
/packages/tests/src/lib/template/__snapshots__/combined-payload.spec.ts.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`Slate JSON to React transforms Combined larger document 1 1`] = `
4 | [
5 | "Heading 2
",
6 | "A regular paragraph.
",
7 | "A paragraph with various text marks.
",
8 | "Heading 3
",
9 | "- Item 1: From a list
- Item 2: From a list
",
10 | "Find out more about Slate.
",
11 | "Heading 4
",
12 | ]
13 | `;
14 |
15 | exports[`Slate JSON to React transforms Combined links nested in an unordered list 1`] = `
16 | [
17 | "",
18 | ]
19 | `;
20 |
21 | exports[`Slate JSON to React transforms Combined text with inline link 1`] = `
22 | [
23 | "Some text before an inline link slate-serializers | GitHub.
",
24 | ]
25 | `;
26 |
27 | exports[`Slate JSON to React transforms Element tags blockquote 1`] = `
28 | [
29 | "Blockquote
",
30 | ]
31 | `;
32 |
33 | exports[`Slate JSON to React transforms Element tags headings 1`] = `
34 | [
35 | "Heading 1
",
36 | "Heading 2
",
37 | "Heading 3
",
38 | "Heading 4
",
39 | "Heading 5
",
40 | "Heading 6
",
41 | ]
42 | `;
43 |
44 | exports[`Slate JSON to React transforms Element tags link 1`] = `
45 | [
46 | "Slate Serializers | GitHub",
47 | ]
48 | `;
49 |
50 | exports[`Slate JSON to React transforms Element tags nested unordered list 1`] = `
51 | [
52 | "- Item 1
- Nested item 1
- Nested item 2
- Item 2
",
53 | ]
54 | `;
55 |
56 | exports[`Slate JSON to React transforms Element tags ordered list 1`] = `
57 | [
58 | "- Item 1
- Item 2
",
59 | ]
60 | `;
61 |
62 | exports[`Slate JSON to React transforms Element tags paragraph 1`] = `
63 | [
64 | "Paragraph 1
",
65 | ]
66 | `;
67 |
68 | exports[`Slate JSON to React transforms Element tags unordered list 1`] = `
69 | [
70 | "",
71 | ]
72 | `;
73 |
74 | exports[`Slate JSON to React transforms Text tags idiomatic text 1`] = `
75 | [
76 | "Idiomatic Text
",
77 | ]
78 | `;
79 |
80 | exports[`Slate JSON to React transforms Text tags pre 1`] = `
81 | [
82 | "Pre
",
83 | ]
84 | `;
85 |
86 | exports[`Slate JSON to React transforms Text tags strikethrough 1`] = `
87 | [
88 | "Strikethrough
",
89 | ]
90 | `;
91 |
92 | exports[`Slate JSON to React transforms Text tags strong 1`] = `
93 | [
94 | "Bold
",
95 | ]
96 | `;
97 |
98 | exports[`Slate JSON to React transforms Text tags strong and idiomatic text 1`] = `
99 | [
100 | "Strong and Idiomatic Text
",
101 | ]
102 | `;
103 |
104 | exports[`Slate JSON to React transforms Text tags strong and unarticulated annotation 1`] = `
105 | [
106 | "Strong and Unarticulated Annotation
",
107 | ]
108 | `;
109 |
110 | exports[`Slate JSON to React transforms Text tags strong in paragraph 1`] = `
111 | [
112 | "Bold
",
113 | ]
114 | `;
115 |
116 | exports[`Slate JSON to React transforms Text tags strong mid-sentence 1`] = `
117 | [
118 | "Some
",
119 | "bold text
",
120 | " in a sentence.
",
121 | ]
122 | `;
123 |
124 | exports[`Slate JSON to React transforms Text tags unarticulated annotation 1`] = `
125 | [
126 | "Unarticulated Annotation
",
127 | ]
128 | `;
129 |
--------------------------------------------------------------------------------
/packages/tests/src/lib/fixtures/textTags.ts:
--------------------------------------------------------------------------------
1 | interface Ifixture {
2 | name: string
3 | html: string
4 | slate: object[]
5 | }
6 |
7 | export const fixtures: Ifixture[] = [
8 | {
9 | name: 'strong',
10 | html: 'Bold',
11 | slate: [
12 | {
13 | children: [
14 | {
15 | bold: true,
16 | text: 'Bold',
17 | },
18 | ],
19 | },
20 | ],
21 | },
22 | {
23 | name: 'unarticulated annotation',
24 | html: 'Unarticulated Annotation',
25 | slate: [
26 | {
27 | children: [
28 | {
29 | underline: true,
30 | text: 'Unarticulated Annotation',
31 | },
32 | ],
33 | },
34 | ],
35 | },
36 | {
37 | name: 'idiomatic text',
38 | html: 'Idiomatic Text',
39 | slate: [
40 | {
41 | children: [
42 | {
43 | italic: true,
44 | text: 'Idiomatic Text',
45 | },
46 | ],
47 | },
48 | ],
49 | },
50 | {
51 | name: 'strikethrough',
52 | html: 'Strikethrough',
53 | slate: [
54 | {
55 | children: [
56 | {
57 | strikethrough: true,
58 | text: 'Strikethrough',
59 | },
60 | ],
61 | },
62 | ],
63 | },
64 | {
65 | name: 'strong and idiomatic text',
66 | html: 'Strong and Idiomatic Text',
67 | slate: [
68 | {
69 | children: [
70 | {
71 | bold: true,
72 | italic: true,
73 | text: 'Strong and Idiomatic Text',
74 | },
75 | ],
76 | },
77 | ],
78 | },
79 | {
80 | name: 'strong and unarticulated annotation',
81 | html: 'Strong and Unarticulated Annotation',
82 | slate: [
83 | {
84 | children: [
85 | {
86 | bold: true,
87 | underline: true,
88 | text: 'Strong and Unarticulated Annotation',
89 | },
90 | ],
91 | },
92 | ],
93 | },
94 | /** nesting three text marks doesn't work - italic is not included */
95 | /*{
96 | name: "strong, unarticulated annotation and idiomatic text",
97 | html: "Strong, Unarticulated Annotation and Idiomatic Text",
98 | slate: [
99 | {
100 | children: [
101 | {
102 | bold: true,
103 | italic: true,
104 | underline: true,
105 | text: "Strong, Unarticulated Annotation and Idiomatic Text"
106 | }
107 | ],
108 | },
109 | ],
110 | },*/
111 | {
112 | name: 'strong in paragraph',
113 | html: 'Bold
',
114 | slate: [
115 | {
116 | children: [
117 | {
118 | bold: true,
119 | text: 'Bold',
120 | },
121 | ],
122 | type: 'p',
123 | },
124 | ],
125 | },
126 | {
127 | name: 'strong mid-sentence',
128 | html: 'Some bold text in a sentence.',
129 | /*
130 | Expected but not received:
131 | {
132 | "text": "Some "
133 | },
134 | {
135 | "text": "bold text",
136 | "bold": true
137 | },
138 | {
139 | "text": " in a sentence."
140 | }
141 | */
142 | slate: [
143 | {
144 | children: [
145 | {
146 | text: 'Some ',
147 | },
148 | ],
149 | },
150 | {
151 | children: [
152 | {
153 | bold: true,
154 | text: 'bold text',
155 | },
156 | ],
157 | },
158 | {
159 | children: [
160 | {
161 | text: ' in a sentence.',
162 | },
163 | ],
164 | },
165 | ],
166 | },
167 | {
168 | name: 'pre',
169 | html: 'Pre
',
170 | slate: [
171 | {
172 | children: [
173 | {
174 | code: true,
175 | text: 'Pre',
176 | },
177 | ],
178 | },
179 | ],
180 | },
181 | ]
182 |
--------------------------------------------------------------------------------
/packages/html/src/lib/serializers/htmlToSlate/slateDemo.spec.ts:
--------------------------------------------------------------------------------
1 | import { htmlToSlate } from '.'
2 | import { config as slateDemoHtmlToSlateConfig } from './config/slateDemo'
3 |
4 | describe('inline code and pre HTML elements', () => {
5 | // all fixtures should give the same Slate result
6 | // slate demo is configured to translate `code=true`
7 | // to `...
8 |
9 | // htmlparser2 seems to separate out `pre` into a new
10 | // block but keeps `code` tags inline. A workaround for
11 | // this scenario (hacky, but works) uses regex to replace
12 | // `pre` tags with `code`.
13 |
14 | // It is not possible to achieve this when the DOM is
15 | // parsed because this is the stage where the paragraph
16 | // is broken.
17 | const slate = [
18 | {
19 | type: 'paragraph',
20 | children: [
21 | {
22 | text: 'This is editable ',
23 | },
24 | {
25 | text: 'rich',
26 | bold: true,
27 | },
28 | {
29 | text: ' text, ',
30 | },
31 | {
32 | text: 'much',
33 | italic: true,
34 | },
35 | {
36 | text: ' better than a ',
37 | },
38 | {
39 | text: '