Skip to content

Commit

Permalink
feat: Add parser
Browse files Browse the repository at this point in the history
  • Loading branch information
wwerner authored Sep 16, 2022
1 parent f1b526b commit 4acc574
Show file tree
Hide file tree
Showing 15 changed files with 6,358 additions and 1 deletion.
4 changes: 4 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
indent_style = tab
indent_size = tab
tab_width = 2
end_of_line = lf
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* text=auto eol=lf
5 changes: 5 additions & 0 deletions .github/workflows/.prettierrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = {
trailingComma: "es5",
semi: false,
singleQuote: true,
};
32 changes: 32 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: CI
on: [push]
jobs:
build:
name: Build, lint, and test on Node ${{ matrix.node }} and ${{ matrix.os }}

runs-on: ${{ matrix.os }}
strategy:
matrix:
node: ['14.x','16.x','18.x']
os: [ubuntu-latest, windows-latest, macOS-latest]

steps:
- name: Checkout repo
uses: actions/checkout@v2

- name: Use Node ${{ matrix.node }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node }}

- name: Install deps and build (with cache)
uses: bahmutov/npm-install@v1

- name: Lint
run: yarn lint

- name: Test
run: yarn test --ci --coverage --maxWorkers=2

- name: Build
run: yarn build
12 changes: 12 additions & 0 deletions .github/workflows/size.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
name: size
on: [pull_request]
jobs:
size:
runs-on: ubuntu-latest
env:
CI_JOB_NUMBER: 1
steps:
- uses: actions/checkout@v3
- uses: andresz1/size-limit-action@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
*.log
.DS_Store
node_modules
dist
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
v16.15.0
99 changes: 98 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,99 @@
# simple-adf-formatter
# Simple ADF Formatter

A light-weight parser and formatter framework for converting Atlassian Document Format to arbitrary outputs.

## Commands

TSDX scaffolds your new library inside `/src`.

To run TSDX, use:

```bash
npm start # or yarn start
```

This builds to `/dist` and runs the project in watch mode so any edits you save inside `src` causes a rebuild to `/dist`.

To do a one-off build, use `npm run build` or `yarn build`.

To run tests, use `npm test` or `yarn test`.

## Configuration

Code quality is set up for you with `prettier`, `husky`, and `lint-staged`. Adjust the respective fields in `package.json` accordingly.

### Jest

Jest tests are set up to run with `npm test` or `yarn test`.

### Bundle Analysis

[`size-limit`](https://github.com/ai/size-limit) is set up to calculate the real cost of your library with `npm run size` and visualize the bundle with `npm run analyze`.

#### Setup Files

This is the folder structure we set up for you:

```txt
/src
index.tsx # EDIT THIS
/test
blah.test.tsx # EDIT THIS
.gitignore
package.json
README.md # EDIT THIS
tsconfig.json
```

### Rollup

TSDX uses [Rollup](https://rollupjs.org) as a bundler and generates multiple rollup configs for various module formats and build settings. See [Optimizations](#optimizations) for details.

### TypeScript

`tsconfig.json` is set up to interpret `dom` and `esnext` types, as well as `react` for `jsx`. Adjust according to your needs.

## Continuous Integration

### GitHub Actions

Two actions are added by default:

- `main` which installs deps w/ cache, lints, tests, and builds on all pushes against a Node and OS matrix
- `size` which comments cost comparison of your library on every pull request using [`size-limit`](https://github.com/ai/size-limit)

## Optimizations

Please see the main `tsdx` [optimizations docs](https://github.com/palmerhq/tsdx#optimizations). In particular, know that you can take advantage of development-only optimizations:

```js
// ./types/index.d.ts
declare var __DEV__: boolean;

// inside your code...
if (__DEV__) {
console.log('foo');
}
```

You can also choose to install and use [invariant](https://github.com/palmerhq/tsdx#invariant) and [warning](https://github.com/palmerhq/tsdx#warning) functions.

## Module Formats

CJS, ESModules, and UMD module formats are supported.

The appropriate paths are configured in `package.json` and `dist/index.js` accordingly. Please report if any issues are found.

## Named Exports

Per Palmer Group guidelines, [always use named exports.](https://github.com/palmerhq/typescript#exports) Code split inside your React app instead of your React library.

## Including Styles

There are many ways to ship styles, including with CSS-in-JS. TSDX has no opinion on this, configure how you like.

For vanilla CSS, you can include it at the root directory and add it to the `files` section in your `package.json`, so that it can be imported separately by your users and run through their bundler's loader.

## Publishing to NPM

We recommend using [np](https://github.com/sindresorhus/np).
67 changes: 67 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
{
"name": "simple-adf-formatter",
"version": "0.1.0",
"license": "Apache-2.0",
"author": "Wolfgang Werner",
"repository": {
"type": "git",
"url": "[email protected]:Dixa-public/simple-adf-formatter.git"
},
"bugs": {
"url": "https://github.com/Dixa-public/simple-adf-formatter/issues"
},
"main": "dist/index.js",
"module": "dist/simple-adf-formatter.esm.js",
"typings": "dist/index.d.ts",
"files": [
"dist",
"src"
],
"scripts": {
"analyze": "size-limit --why",
"build": "dts build",
"lint": "dts lint src test",
"prepare": "dts build",
"size": "size-limit",
"start": "dts watch",
"test": "dts test"
},
"husky": {
"hooks": {
"pre-commit": "dts lint src test"
}
},
"prettier": {
"printWidth": 80,
"semi": true,
"singleQuote": true,
"trailingComma": "es5"
},
"jest": {
"testEnvironment": "node"
},
"peerDependencies": {},
"engines": {
"node": ">=14.17.0",
"npm": ">=7"
},
"size-limit": [
{
"path": "dist/simple-adf-formatter.cjs.production.min.js",
"limit": "10 KB"
},
{
"path": "dist/simple-adf-formatter.esm.js",
"limit": "10 KB"
}
],
"devDependencies": {
"@size-limit/preset-small-lib": "^8.1.0",
"@tsconfig/recommended": "^1.0.1",
"dts-cli": "^1.6.0",
"husky": "^8.0.1",
"size-limit": "^8.1.0",
"tslib": "^2.4.0",
"typescript": "^4.8.3"
}
}
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './parser';
export * from './types';
50 changes: 50 additions & 0 deletions src/parser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { ADFEntity, Formatter } from './types';

/**
* Formats the given ADF node using the given formatter
* @param node @param formatter @returns Formatted object of type T
*/
export const formatAdf = <T>(node: ADFEntity, formatter: Formatter<T>): T => {
/*
* Composes the applicable mark format functions for the current node.
*
* @param content T @returns a function to mark up the node's content using
* the formatters mark functions for the node's marks
*/
const applyMarkup = (content: T) =>
(node.marks || [])
.map((mark) => ({
formatterFunction: formatter.marks[node.type]?.[mark.type],
mark,
}))
.reduce(
(prev, curr) => () =>
curr.formatterFunction?.(curr.mark, prev) || content,
() => content
)();

/*
* Returns a function recursing through the given node's children tree by
* calling itself for every child node.
*
* @param node the node to process @param formatter @returns a function to
* render the node's children to be used in the formatter (second parameter of
* NodeMapper)
*
* @returns parameter-less function to process children
*/
const processChildren =
(node: ADFEntity, formatter: Formatter<T>) => (): T[] =>
node.content // all block nodes have content
? node.content.map((child) => formatAdf(child, formatter))
: [];

/*
* Apply the composed markup function to the curried processChildren function
* and return the result.
*/
const formatterOrDefault = formatter.nodes[node.type] || formatter.default;
return applyMarkup(
formatterOrDefault(node, processChildren(node, formatter))
);
};
64 changes: 64 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
export type ADFNodeType =
| 'doc'
| 'blockquote'
| 'bulletList'
| 'codeBlock'
| 'emoji'
| 'hardBreak'
| 'heading'
| 'inlineCard'
| 'listItem'
| 'mediaGroup'
| 'mediaSingle'
| 'mention'
| 'orderedList'
| 'panel'
| 'paragraph'
| 'rule'
| 'table'
| 'tableCell'
| 'tableHeader'
| 'tableRow'
| 'text';

export type ADFMarkType =
| 'code'
| 'em'
| 'link'
| 'strike'
| 'strong'
| 'subsup'
| 'textColor'
| 'underline';

export interface ADFEntityMark {
type: ADFMarkType;
attrs?: {
[name: string]: string;
};
}

export interface ADFEntity {
type: ADFNodeType;
attrs?: {
[name: string]: unknown;
};
content?: Array<ADFEntity>;
marks?: Array<ADFEntityMark>;
text?: string;
[key: string]: any;
}

export type MarkMapper<T> = (mark: ADFEntityMark, next: () => T) => T;
export type NodeMapper<T> = (node: ADFEntity, processChildren: () => T[]) => T;
export type Formatter<T> = {
default: NodeMapper<T>;
nodes: {
[nodeType in ADFNodeType]?: NodeMapper<T>;
};
marks: {
[nodeType in ADFNodeType]?: {
[markType in ADFMarkType]?: MarkMapper<T>;
};
};
};
Loading

0 comments on commit 4acc574

Please sign in to comment.