diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 0fb11bbb..28aa0ac0 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -1,29 +1,35 @@ # GitHub Copilot Instructions for Markdown-Transform -This repository is **Accord Project markdown-transform** — a JavaScript npm-workspaces monorepo for parsing and transforming Markdown, CommonMark, CiceroMark, and TemplateMark. +This repository is **Accord Project markdown-transform** — a TypeScript npm-workspaces monorepo for parsing and transforming Markdown, CommonMark, CiceroMark, and TemplateMark. ## Project context -- Runtime: Node.js `>=18` +- Runtime: Node.js `>=22` - Package manager: `npm` (workspace root + package-level scripts) -- Languages: JavaScript (CommonJS) -- Linting: ESLint (`eslint:recommended` + strict local rules) -- Testing: Jest (most packages), Mocha (notably `packages/markdown-transform`) -- CI: GitHub Actions matrix on Ubuntu, macOS, and Windows +- Language: **TypeScript** (target `ES2020`, `module: commonjs`). Source lives in `packages/*/src/`; compiled `.js` + `.d.ts` are emitted to `packages/*/lib/`. +- Build: `tsc` per package (config extends `tsconfig.base.json`). +- Linting: ESLint with `@typescript-eslint` (4-space indent, single quotes, semicolons). +- Unit testing: **Jest 29 + ts-jest** across every package. The legacy mocha+chai suites were removed during the TS migration. +- Browser E2E: **Playwright** under `e2e/` exercises the UMD bundles in headless Chromium. +- Bundling: `webpack 5` produces UMD bundles for `markdown-html`, `markdown-template`, `markdown-transform` (the three user-facing entry points). The other packages are CommonJS library deps consumed via bundlers. +- CI: GitHub Actions matrix on Ubuntu, macOS, and Windows for unit tests; Ubuntu-only for Playwright e2e. ## Repository layout -- Root workspace packages live under `packages/` -- Primary packages include: +- `packages/` — eight publishable packages: - `markdown-common` - `markdown-cicero` - - `markdown-template` - - `markdown-html` + - `markdown-template` *(also UMD)* + - `markdown-html` *(also UMD)* - `markdown-it-cicero` - `markdown-it-template` - `markdown-cli` - - `markdown-transform` -- Utility scripts are under `scripts/` + - `markdown-transform` *(umbrella, also UMD)* +- `e2e/` — browser end-to-end tests (Playwright). Not published. +- `scripts/` — repo-level utilities (model generation, version bumping, coverage aggregation). +- `tsconfig.base.json` — shared compiler options inherited by every package. + +Concerto models for CommonMark/CiceroMark/TemplateMark are downloaded by `scripts/external/getExternalModels.js` (run via `npm run models:get` / triggered as `postinstall`) and emitted as TS into `packages/markdown-common/src/externalModels/`. Treat those files as generated. ## Non-negotiable contribution requirements @@ -35,31 +41,32 @@ This repository is **Accord Project markdown-transform** — a JavaScript npm-wo - Examples seen in this repo: - `fix: update broken CI badge to use GitHub Actions workflow URL` - `chore(deps): update package dependencies` - - `chore(actions): publish v0.16.25 to npm` + - `chore(actions): publish v1.0.0 to npm` 3. **Do not skip tests for behavior changes** - - Add or update unit tests when changing logic. + - Add or update unit tests when changing logic; add a Playwright e2e test if the change affects the browser bundle surface. ## Coding standards (repo-specific) -- Use **4-space indentation**. -- Use **single quotes**. -- Use **semicolons**. -- Avoid `var`; use `const`/`let`. -- Keep braces for control flow (`curly` rule). -- Keep strict equality (`eqeqeq`). -- Add JSDoc for classes, methods, and function declarations (`require-jsdoc`). +- TypeScript only for new code. Avoid reintroducing `.js` files in `src/`. +- 4-space indentation, single quotes, semicolons. +- Prefer `const`/`let` (no `var`), keep braces (`curly`), strict equality (`eqeqeq`). +- The TS config is pragmatic — `strict: false`, `noImplicitAny: false` — so visitor/AST code uses `any` liberally. That is intentional: don't tighten types in unrelated files while fixing something else. +- Don't add JSDoc that simply restates the function signature; reserve comments for non-obvious *why*. - Prefer minimal, surgical diffs; avoid unrelated formatting churn. ## Build and test workflow When changing code, run checks in this order: -1. `npm run build --workspaces --if-present` -2. `npm test` -3. If needed for coverage diagnostics: `npm run coverage` +1. `npm run build` — runs `tsc` per workspace (also rebuilds before tests via each package's `pretest`). +2. `npm test` — runs the full Jest suite across every package. +3. `npm run -w markdown-transform-e2e test` — Playwright browser tests; only needed if you changed source that ends up in a UMD bundle. +4. `npm run coverage` — coverage aggregation (only if investigating coverage). + +For package-level iteration, `cd packages/` and run `npm run build`, `npm test`, etc. directly. For the umbrella package, also run `npm run webpack` after `npm run build` to refresh the UMD bundle. -For package-level iteration, run the package scripts directly inside the package folder (for example lint/test in `packages/markdown-common` or `packages/markdown-transform`). +When migrating Concerto: `@accordproject/concerto-core` is on **v4**. `new ModelManager({ strict: true })` is no longer valid — drop the option, don't cast to `any`. The model manager defaults are equivalent in v4. ## Dependency management rules (critical) @@ -73,7 +80,7 @@ These are based on merged PR review feedback in this repository: - If downgrading is required, explain why in PR description and comments. 3. **Avoid adding new dependencies without clear rationale** - - Reviewers repeatedly asked “Why the new deps?” across multiple package manifests. + - Reviewers repeatedly asked "Why the new deps?" across multiple package manifests. - Prefer updating existing dependencies over adding new ones. 4. **For core Accord dependencies, prefer exact versions when the repo already pins exact versions** @@ -83,6 +90,15 @@ These are based on merged PR review feedback in this repository: 5. **Keep workspace dependency versions consistent across packages** - If bumping a shared dependency, align all affected package manifests and lockfiles in one change. +6. **Browser polyfills only when strictly needed** + - The webpack configs use `webpack.ProvidePlugin({ process: 'process/browser' })` and `resolve.alias = { jsdom: false }` to keep UMD bundles slim. Don't add Node polyfills unless a real test fails without them. + +## Publishing & npm packages + +- `package.json` `files` field for every publishable package is `["lib"]` (or `["lib", "umd"]` for the three UMD packages). `src/`, tests, snapshots, jest config, eslint config, and tsconfig stay out of the tarball. +- `main: "lib/index.js"`, `types: "lib/index.d.ts"`. The three UMD packages also set `browser: "umd/markdown-X.js"` so bundlers serving browser targets pick the UMD bundle automatically. +- Source maps (`*.js.map`) **are** shipped — keep `sourceMap: true` in `tsconfig.base.json` so consumer stack traces stay useful. + ## AI review behavior (adapted from best-practice guidance) Copilot suggestions should follow a **human-in-the-loop**, high-signal workflow: @@ -100,7 +116,7 @@ Copilot suggestions should follow a **human-in-the-loop**, high-signal workflow: - Do not trade security for convenience. 4. **Continuous learning loop** - - If a review pattern repeats (e.g., “why new dependency?”, “why downgrade?”), treat it as a standing rule for future changes. + - If a review pattern repeats (e.g., "why new dependency?", "why downgrade?"), treat it as a standing rule for future changes. - Prefer repository-established patterns over generic defaults. 5. **Human validation remains required** @@ -111,8 +127,9 @@ Copilot suggestions should follow a **human-in-the-loop**, high-signal workflow: Before proposing a PR-ready change: - [ ] Change scope is minimal and focused -- [ ] New/updated behavior has tests +- [ ] New/updated behavior has tests (unit and, where relevant, Playwright e2e) - [ ] Lint/build/tests pass +- [ ] `npm pack --dry-run` for any package whose contents changed shows only `lib/` (+ optional `umd/`) — no tests, snapshots, or configs leaking - [ ] Dependency changes are justified and minimal - [ ] No accidental downgrades or unnecessary added packages - [ ] Commit(s) use DCO sign-off @@ -120,9 +137,11 @@ Before proposing a PR-ready change: ## Common pitfalls in this repo +- Mixing `.js` and `.ts` in `src/` — the source tree is TypeScript only. +- Forgetting to rebuild UMD bundles (`npm run webpack -w …`) after source changes; the Playwright e2e tests will then test stale code. +- Adding broad type tightening (`noImplicitAny`, `strict`) in unrelated files while fixing a small bug — out of scope, expand `any` only where the change is needed. - Adding many dependency changes in one sweep without explaining each one. - Switching from exact to ranged versions for core dependencies without team agreement. -- Introducing dependency downgrades as side effects of automated tooling. -- Making large unrelated edits while addressing a small issue. +- Re-introducing `npm install`-time `prepare`/`build` scripts. Build is a separate explicit step now (`npm run build`), keeping `npm install` fast and resilient to broken intermediate states. When in doubt, prefer small, explicit, well-tested changes that match existing package patterns. diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d9dab32a..7299d467 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -61,6 +61,62 @@ jobs: parallel: true fail-on-error: false + e2e: + name: Browser E2E (Chromium) + runs-on: ubuntu-latest + needs: + - build + + steps: + - name: git checkout + uses: actions/checkout@v4 + + - name: Use Node.js 22.x + uses: actions/setup-node@v4 + with: + node-version: 22.x + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Build packages (dep order) + run: npm run build + + - name: Build UMD bundles + run: npm run -w markdown-transform-e2e build:bundles + + - name: Get Playwright version + id: playwright-version + run: | + echo "version=$(node -p "require('./e2e/node_modules/@playwright/test/package.json').version")" >> $GITHUB_OUTPUT + + - name: Cache Playwright browsers + id: playwright-cache + uses: actions/cache@v4 + with: + path: ~/.cache/ms-playwright + key: playwright-${{ runner.os }}-${{ steps.playwright-version.outputs.version }} + + - name: Install Playwright Chromium + if: steps.playwright-cache.outputs.cache-hit != 'true' + run: npx --no-install --prefix e2e playwright install --with-deps chromium + + - name: Install Playwright system deps only (cache hit) + if: steps.playwright-cache.outputs.cache-hit == 'true' + run: npx --no-install --prefix e2e playwright install-deps chromium + + - name: Run browser e2e tests + run: npm test -w markdown-transform-e2e -- --reporter=list + + - name: Upload Playwright report on failure + if: failure() + uses: actions/upload-artifact@v4 + with: + name: playwright-report + path: e2e/playwright-report + retention-days: 7 + notify: needs: - build diff --git a/README.md b/README.md index 5941fe08..534463fe 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,8 @@ A transformation and parsing framework for converting markdown content to HTML and other structured document object models (DOMs). +The codebase is written in TypeScript; each package ships compiled JavaScript alongside `.d.ts` type declarations. + ![Transformations](./packages/markdown-transform/transformations.png) ## Structure of the Code Repository @@ -37,23 +39,23 @@ Top level repository (markdown-transform), with sub packages. Each sub-package i The CommonMark DOM is a model for the elements of CommonMark (the specification for markdown text), expressed as a [Concerto schema](https://github.com/accordproject/concerto), and serialized as a JSON graph. -The schema is defined here: https://models.accordproject.org/markdown/commonmark@0.2.0.html +The schema is defined here: https://models.accordproject.org/markdown/commonmark@0.5.0.html ### CiceroMark DOM CiceroMark defines markdown documents with embedded clauses, where each clause is an instance of a template, specified using TemplateMark. -The CiceroMark DOM extends the CommonMark DOM, defining nodes for `Clause`, `Variable` and `Formula` etc. +The CiceroMark DOM extends the CommonMark DOM, defining nodes for `Clause`, `Variable`, `FormattedVariable`, `EnumVariable`, `Conditional`, `Optional`, `Formula` and `ListBlock`. -The schema is defined here: https://models.accordproject.org/markdown/ciceromark@0.3.0.html +The schema is defined here: https://models.accordproject.org/markdown/ciceromark@0.6.0.html ### TemplateMark DOM TemplateMark defines markdown documents with syntax for embedded variables, optional blocks, formulas etc. It is used to define Accord Project templates. -The TemplateMark DOM extends the CommonMark DOM, defining nodes for `ClauseDefinition`, `VariableDefinition` and `ForumulaDefinition` etc. +The TemplateMark DOM extends the CommonMark DOM, defining nodes for `ClauseDefinition`, `ContractDefinition`, `VariableDefinition`, `FormattedVariableDefinition`, `EnumVariableDefinition`, `ConditionalDefinition`, `OptionalDefinition`, `WithDefinition`, `JoinDefinition`, `ListBlockDefinition` and `FormulaDefinition`. -The schema is defined here: https://models.accordproject.org/markdown/templatemark.html +The schema is defined here: https://models.accordproject.org/markdown/templatemark@0.5.0.html ## Installation @@ -76,13 +78,14 @@ markus --help npm install ``` -Then run: +Then build all packages and run their tests: ``` +npm run build npm run test ``` -This command uses npm workspaces to run the tests for each package in the monorepo. +These commands use npm workspaces. `build` runs `tsc` for each package, producing JavaScript and `.d.ts` declarations into `packages/*/lib/`. `test` runs Jest. Requires Node 22 or later. --- diff --git a/e2e/.gitignore b/e2e/.gitignore new file mode 100644 index 00000000..e16c8f02 --- /dev/null +++ b/e2e/.gitignore @@ -0,0 +1,4 @@ +node_modules/ +test-results/ +playwright-report/ +playwright/.cache/ diff --git a/e2e/README.md b/e2e/README.md new file mode 100644 index 00000000..bdef4860 --- /dev/null +++ b/e2e/README.md @@ -0,0 +1,41 @@ +# Browser End-to-End Tests + +[Playwright](https://playwright.dev) tests that load the UMD bundles for `markdown-html`, `markdown-template` and `markdown-transform` into a real headless Chromium and call the public API. These tests exist to catch packaging/bundling regressions that unit tests miss — for example, accidentally pulling Node-only modules like `jsdom` into the browser bundle. + +## Run + +From the repository root: + +```bash +npm install --workspaces +npm run -w markdown-transform-e2e test +``` + +`npm test` from the e2e directory runs `pretest` first, which: +1. Builds each TS package (`tsc`) +2. Builds each UMD bundle (`webpack`) +3. Installs the Chromium browser used by Playwright (cached after first run) + +## What's covered + +| Spec | Asserts | +|------|---------| +| `markdown-html.spec.ts` | `HtmlTransformer` exported on the global; `toHtml`/`toCiceroMark` work using the native `DOMParser` (jsdom is **not** in the browser bundle) | +| `markdown-template.spec.ts` | `TemplateMarkTransformer` exported; `toTokens` and `normalizeNLs` work | +| `markdown-transform.spec.ts` | `transform`, `formatDescriptor`, `generateTransformationDiagram`, `TransformEngine` exported; markdown → commonmark and markdown → html transformations succeed | + +## Adding a test + +Each UMD bundle exports its API onto `window['']` (e.g. `window['markdown-html']`). Spec pattern: + +```ts +await page.setContent(''); +await page.addScriptTag({ path: path.resolve(__dirname, '../../packages//umd/.js') }); + +const result = await page.evaluate(() => { + const { Something } = (window as any)['']; + return new Something().doStuff(); +}); + +expect(result).toBe(/* … */); +``` diff --git a/e2e/package.json b/e2e/package.json new file mode 100644 index 00000000..27e29085 --- /dev/null +++ b/e2e/package.json @@ -0,0 +1,14 @@ +{ + "name": "markdown-transform-e2e", + "version": "1.0.0", + "private": true, + "description": "End-to-end tests that load the UMD browser bundles into a real headless Chromium and exercise the public API.", + "scripts": { + "build:bundles": "npm run -w @accordproject/markdown-html webpack && npm run -w @accordproject/markdown-template webpack && npm run -w @accordproject/markdown-transform webpack", + "pretest": "npm run build:bundles && npx --yes playwright install chromium", + "test": "playwright test" + }, + "devDependencies": { + "@playwright/test": "^1.49.0" + } +} diff --git a/packages/markdown-cicero/index.js b/e2e/playwright.config.ts similarity index 57% rename from packages/markdown-cicero/index.js rename to e2e/playwright.config.ts index f22556e7..7ed127b7 100644 --- a/packages/markdown-cicero/index.js +++ b/e2e/playwright.config.ts @@ -12,15 +12,19 @@ * limitations under the License. */ -'use strict'; +import { defineConfig, devices } from '@playwright/test'; -/** - * Export the framework and plugins - * @module markdown-transform - */ - -module.exports.CiceroMarkTransformer = require('./lib/CiceroMarkTransformer'); -module.exports.FromCiceroEditVisitor = require('./lib/FromCiceroEditVisitor'); -module.exports.ToCommonMarkVisitor = require('./lib/ToCommonMarkVisitor'); - -module.exports.Decorators = require('./lib/Decorators'); +export default defineConfig({ + testDir: './tests', + fullyParallel: true, + forbidOnly: !!process.env.CI, + retries: process.env.CI ? 1 : 0, + workers: process.env.CI ? 1 : undefined, + reporter: 'list', + use: { + trace: 'on-first-retry', + }, + projects: [ + { name: 'chromium', use: { ...devices['Desktop Chrome'] } }, + ], +}); diff --git a/e2e/tests/markdown-html.spec.ts b/e2e/tests/markdown-html.spec.ts new file mode 100644 index 00000000..4e681ae4 --- /dev/null +++ b/e2e/tests/markdown-html.spec.ts @@ -0,0 +1,73 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { test, expect } from '@playwright/test'; +import * as path from 'path'; + +const UMD_BUNDLE = path.resolve(__dirname, '../../packages/markdown-html/umd/markdown-html.js'); + +test.describe('@accordproject/markdown-html UMD', () => { + test('exposes HtmlTransformer on the global', async ({ page }) => { + await page.setContent(''); + await page.addScriptTag({ path: UMD_BUNDLE }); + + const result = await page.evaluate(() => { + const mod = (window as any)['markdown-html']; + return { + hasHtmlTransformer: typeof mod?.HtmlTransformer === 'function', + hasToHtmlStringVisitor: typeof mod?.ToHtmlStringVisitor === 'function', + }; + }); + + expect(result.hasHtmlTransformer).toBe(true); + expect(result.hasToHtmlStringVisitor).toBe(true); + }); + + test('toHtml renders a CommonMark Document', async ({ page }) => { + await page.setContent(''); + await page.addScriptTag({ path: UMD_BUNDLE }); + + const html = await page.evaluate(() => { + const { HtmlTransformer } = (window as any)['markdown-html']; + const transformer = new HtmlTransformer(); + return transformer.toHtml({ + $class: 'org.accordproject.commonmark@0.5.0.Document', + xmlns: 'http://commonmark.org/xml/1.0', + nodes: [{ + $class: 'org.accordproject.commonmark@0.5.0.Paragraph', + nodes: [{ + $class: 'org.accordproject.commonmark@0.5.0.Text', + text: 'Hello, browser!', + }], + }], + }); + }); + + expect(html).toContain('

Hello, browser!

'); + }); + + test('toCiceroMark parses HTML using the native DOMParser', async ({ page }) => { + await page.setContent(''); + await page.addScriptTag({ path: UMD_BUNDLE }); + + const dom = await page.evaluate(() => { + const { HtmlTransformer } = (window as any)['markdown-html']; + return new HtmlTransformer().toCiceroMark('

Roundtripped

'); + }); + + expect(dom.$class).toBe('org.accordproject.commonmark@0.5.0.Document'); + expect(dom.nodes[0].$class).toBe('org.accordproject.commonmark@0.5.0.Paragraph'); + expect(dom.nodes[0].nodes[0].text).toBe('Roundtripped'); + }); +}); diff --git a/e2e/tests/markdown-template.spec.ts b/e2e/tests/markdown-template.spec.ts new file mode 100644 index 00000000..80041d97 --- /dev/null +++ b/e2e/tests/markdown-template.spec.ts @@ -0,0 +1,62 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { test, expect } from '@playwright/test'; +import * as path from 'path'; + +const UMD_BUNDLE = path.resolve(__dirname, '../../packages/markdown-template/umd/markdown-template.js'); + +test.describe('@accordproject/markdown-template UMD', () => { + test('exposes TemplateMarkTransformer on the global', async ({ page }) => { + await page.setContent(''); + await page.addScriptTag({ path: UMD_BUNDLE }); + + const result = await page.evaluate(() => { + const mod = (window as any)['markdown-template']; + return { + hasTransformer: typeof mod?.TemplateMarkTransformer === 'function', + hasNormalize: typeof mod?.normalizeNLs === 'function', + }; + }); + + expect(result.hasTransformer).toBe(true); + expect(result.hasNormalize).toBe(true); + }); + + test('toTokens produces a markdown-it token stream', async ({ page }) => { + await page.setContent(''); + await page.addScriptTag({ path: UMD_BUNDLE }); + + const tokenCount = await page.evaluate(() => { + const { TemplateMarkTransformer } = (window as any)['markdown-template']; + const transformer = new TemplateMarkTransformer(); + const tokens = transformer.toTokens({ content: 'Hello {{name}}.' }); + return tokens.length; + }); + + expect(tokenCount).toBeGreaterThan(0); + }); + + test('normalizeNLs converts CRLF to LF', async ({ page }) => { + await page.setContent(''); + await page.addScriptTag({ path: UMD_BUNDLE }); + + const out = await page.evaluate(() => { + const { normalizeNLs } = (window as any)['markdown-template']; + return normalizeNLs('Hello\r\nWorld!'); + }); + + expect(out).toBe('Hello\nWorld!'); + }); +}); diff --git a/e2e/tests/markdown-transform.spec.ts b/e2e/tests/markdown-transform.spec.ts new file mode 100644 index 00000000..d5d381a4 --- /dev/null +++ b/e2e/tests/markdown-transform.spec.ts @@ -0,0 +1,70 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { test, expect } from '@playwright/test'; +import * as path from 'path'; + +const UMD_BUNDLE = path.resolve(__dirname, '../../packages/markdown-transform/umd/markdown-transform.js'); + +test.describe('@accordproject/markdown-transform UMD', () => { + test('exposes the transform API on the global', async ({ page }) => { + await page.setContent(''); + await page.addScriptTag({ path: UMD_BUNDLE }); + + const exports = await page.evaluate(() => { + const mod = (window as any)['markdown-transform']; + return { + hasTransform: typeof mod?.transform === 'function', + hasFormatDescriptor: typeof mod?.formatDescriptor === 'function', + hasGenerateDiagram: typeof mod?.generateTransformationDiagram === 'function', + hasTransformEngine: typeof mod?.TransformEngine === 'function', + }; + }); + + expect(exports).toEqual({ + hasTransform: true, + hasFormatDescriptor: true, + hasGenerateDiagram: true, + hasTransformEngine: true, + }); + }); + + test('markdown -> commonmark roundtrip', async ({ page }) => { + await page.setContent(''); + await page.addScriptTag({ path: UMD_BUNDLE }); + + const result = await page.evaluate(async () => { + const { transform } = (window as any)['markdown-transform']; + return transform('# Hello\n\nWorld.', 'markdown', ['commonmark']); + }); + + expect(result.$class).toBe('org.accordproject.commonmark@0.5.0.Document'); + const heading = result.nodes[0]; + expect(heading.$class).toBe('org.accordproject.commonmark@0.5.0.Heading'); + expect(heading.nodes[0].text).toBe('Hello'); + }); + + test('markdown -> html via ciceromark', async ({ page }) => { + await page.setContent(''); + await page.addScriptTag({ path: UMD_BUNDLE }); + + const html = await page.evaluate(async () => { + const { transform } = (window as any)['markdown-transform']; + return transform('# Hello\n\nWorld.', 'markdown', ['ciceromark_parsed', 'html']); + }); + + expect(html).toContain('

Hello

'); + expect(html).toContain('

World.

'); + }); +}); diff --git a/e2e/tsconfig.json b/e2e/tsconfig.json new file mode 100644 index 00000000..0f742a54 --- /dev/null +++ b/e2e/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../tsconfig.base.json", + "compilerOptions": { + "noEmit": true, + "types": ["node"] + }, + "include": ["tests/**/*.ts", "playwright.config.ts"] +} diff --git a/package-lock.json b/package-lock.json index c6a77c62..b9912ce1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,17 @@ { "name": "markdown-transform", - "version": "0.16.25", + "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "markdown-transform", - "version": "0.16.25", + "version": "1.0.0", "hasInstallScript": true, "license": "Apache-2.0", "workspaces": [ - "./packages/*" + "./packages/*", + "./e2e" ], "devDependencies": { "@accordproject/concerto-core": "^4.1.3", @@ -26,100 +27,17 @@ "typescript": "^5.9.3" }, "engines": { - "node": ">=18", + "node": ">=22", "npm": ">=9" } }, - "node_modules/@accordproject/concerto-codegen": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@accordproject/concerto-codegen/-/concerto-codegen-4.0.1.tgz", - "integrity": "sha512-ockVPV++IDu3j4WSnXoZejjWTBZFvX/096htu1YLc4KrtsvoF4umdnpfSgibzFBuDoIhH82X3QgHt9X9bdDPLg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@openapi-contrib/openapi-schema-to-json-schema": "4.0.0", - "ajv": "8.18.0", - "ajv-formats": "3.0.1", - "camelcase": "6.3.0", - "dayjs": "1.11.0", - "debug": "4.3.4", - "get-value": "3.0.1", - "json-schema-migrate": "2.0.0", - "pluralize": "8.0.0" - }, - "engines": { - "node": ">=18", - "npm": ">=6" - }, - "peerDependencies": { - "@accordproject/concerto-core": "^4.0.3", - "@accordproject/concerto-util": "^4.0.3", - "@accordproject/concerto-vocabulary": "^4.0.3" - } - }, - "node_modules/@accordproject/concerto-codegen/node_modules/ajv-formats": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", - "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/@accordproject/concerto-codegen/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@accordproject/concerto-codegen/node_modules/dayjs": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.0.tgz", - "integrity": "sha512-JLC809s6Y948/FuCZPm5IX8rRhQwOiyMb2TfVVQEixG7P8Lm/gt5S7yoQZmC8x1UehI9Pb7sksEt4xx14m+7Ug==", - "dev": true, - "license": "MIT" - }, - "node_modules/@accordproject/concerto-codegen/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "e2e": { + "name": "markdown-transform-e2e", + "version": "1.0.0", + "devDependencies": { + "@playwright/test": "^1.49.0" } }, - "node_modules/@accordproject/concerto-codegen/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true, - "license": "MIT" - }, "node_modules/@accordproject/concerto-core": { "version": "4.1.3", "dev": true, @@ -201,39 +119,6 @@ "version": "2.1.2", "license": "MIT" }, - "node_modules/@accordproject/concerto-vocabulary": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@accordproject/concerto-vocabulary/-/concerto-vocabulary-4.1.0.tgz", - "integrity": "sha512-5Y6C2ouX3ZCwoK/FpThnMSaYkE7KqpdjKe3pfpzEv4RTRY/fmWnsDyGfbuaA+CSzVWP7nbe2BE4TmLi6uYBS4w==", - "dev": true, - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "@accordproject/concerto-metamodel": "^3.13.0", - "yaml": "2.8.3" - }, - "engines": { - "node": ">=18", - "npm": ">=9" - } - }, - "node_modules/@accordproject/concerto-vocabulary/node_modules/yaml": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz", - "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", - "dev": true, - "license": "ISC", - "peer": true, - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14.6" - }, - "funding": { - "url": "https://github.com/sponsors/eemeli" - } - }, "node_modules/@accordproject/markdown-cicero": { "resolved": "packages/markdown-cicero", "link": true @@ -266,18 +151,6 @@ "resolved": "packages/markdown-transform", "link": true }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@asamuzakjp/css-color": { "version": "3.2.0", "license": "MIT", @@ -293,42 +166,6 @@ "version": "10.4.3", "license": "ISC" }, - "node_modules/@babel/cli": { - "version": "7.25.9", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", - "commander": "^6.2.0", - "convert-source-map": "^2.0.0", - "fs-readdir-recursive": "^1.1.0", - "glob": "^7.2.0", - "make-dir": "^2.1.0", - "slash": "^2.0.0" - }, - "bin": { - "babel": "bin/babel.js", - "babel-external-helpers": "bin/babel-external-helpers.js" - }, - "engines": { - "node": ">=6.9.0" - }, - "optionalDependencies": { - "@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents.3", - "chokidar": "^3.6.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/cli/node_modules/slash": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/@babel/code-frame": { "version": "7.29.0", "dev": true, @@ -402,17 +239,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.27.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.27.3" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-compilation-targets": { "version": "7.28.6", "dev": true, @@ -436,42 +262,34 @@ "semver": "bin/semver.js" } }, - "node_modules/@babel/helper-create-class-features-plugin": { + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { "version": "7.28.6", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-member-expression-to-functions": "^7.28.5", - "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/helper-replace-supers": "^7.28.6", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/traverse": "^7.28.6", - "semver": "^6.3.1" + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { - "version": "6.3.1", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" } }, - "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.28.5", + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "regexpu-core": "^6.3.1", - "semver": "^6.3.1" + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -480,359 +298,247 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": { - "version": "6.3.1", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.6.6", + "node_modules/@babel/helper-plugin-utils": { + "version": "7.28.6", "dev": true, "license": "MIT", - "dependencies": { - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "debug": "^4.4.3", - "lodash.debounce": "^4.0.8", - "resolve": "^1.22.11" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@babel/helper-define-polyfill-provider/node_modules/debug": { - "version": "4.4.3", + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", "dev": true, "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">=6.9.0" } }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.24.7", + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", "dev": true, "license": "MIT", - "dependencies": { - "@babel/types": "^7.24.7" - }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/helper-globals": { - "version": "7.28.0", + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.28.5", + "node_modules/@babel/helpers": { + "version": "7.28.6", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^7.28.5", - "@babel/types": "^7.28.5" + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/helper-module-imports": { - "version": "7.28.6", + "node_modules/@babel/parser": { + "version": "7.29.0", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^7.28.6", - "@babel/types": "^7.28.6" + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" }, "engines": { - "node": ">=6.9.0" + "node": ">=6.0.0" } }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.28.6", + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.28.6", - "@babel/helper-validator-identifier": "^7.28.5", - "@babel/traverse": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" + "@babel/helper-plugin-utils": "^7.8.0" }, "peerDependencies": { - "@babel/core": "^7.0.0" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.27.1", + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.27.1" + "@babel/helper-plugin-utils": "^7.8.0" }, - "engines": { - "node": ">=6.9.0" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.28.6", + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", "dev": true, "license": "MIT", - "engines": { - "node": ">=6.9.0" + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.27.1", + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-wrap-function": "^7.27.1", - "@babel/traverse": "^7.27.1" + "@babel/helper-plugin-utils": "^7.14.5" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^7.0.0" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/helper-replace-supers": { + "node_modules/@babel/plugin-syntax-import-attributes": { "version": "7.28.6", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.28.5", - "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/traverse": "^7.28.6" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^7.0.0" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.27.1", + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" + "@babel/helper-plugin-utils": "^7.10.4" }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/helper-wrap-function": { - "version": "7.28.6", + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.28.6", - "@babel/traverse": "^7.28.6", - "@babel/types": "^7.28.6" + "@babel/helper-plugin-utils": "^7.8.0" }, - "engines": { - "node": ">=6.9.0" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/helpers": { + "node_modules/@babel/plugin-syntax-jsx": { "version": "7.28.6", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.28.6", - "@babel/types": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.29.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.29.0" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { - "version": "7.28.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.28.5" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^7.0.0" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { - "version": "7.27.1", + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" + "@babel/helper-plugin-utils": "^7.10.4" }, "peerDependencies": { - "@babel/core": "^7.0.0" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.27.1", + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" + "@babel/helper-plugin-utils": "^7.8.0" }, "peerDependencies": { - "@babel/core": "^7.0.0" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.27.1", + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/plugin-transform-optional-chaining": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" + "@babel/helper-plugin-utils": "^7.10.4" }, "peerDependencies": { - "@babel/core": "^7.13.0" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.28.6", + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/traverse": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" + "@babel/helper-plugin-utils": "^7.8.0" }, "peerDependencies": { - "@babel/core": "^7.0.0" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-proposal-async-generator-functions": { - "version": "7.20.7", + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-remap-async-to-generator": "^7.18.9", - "@babel/plugin-syntax-async-generators": "^7.8.4" - }, - "engines": { - "node": ">=6.9.0" + "@babel/helper-plugin-utils": "^7.8.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-proposal-class-properties": { - "version": "7.18.6", + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" + "@babel/helper-plugin-utils": "^7.8.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-proposal-class-static-block": { - "version": "7.21.0", + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.21.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-class-static-block": "^7.14.5" + "@babel/helper-plugin-utils": "^7.14.5" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^7.12.0" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-proposal-dynamic-import": { - "version": "7.18.6", + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" + "@babel/helper-plugin-utils": "^7.14.5" }, "engines": { "node": ">=6.9.0" @@ -841,13 +547,12 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-proposal-export-namespace-from": { - "version": "7.18.9", + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.28.6", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -856,1407 +561,141 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-proposal-json-strings": { - "version": "7.18.6", + "node_modules/@babel/template": { + "version": "7.28.6", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-json-strings": "^7.8.3" + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-proposal-logical-assignment-operators": { - "version": "7.20.7", + "node_modules/@babel/traverse": { + "version": "7.29.0", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" }, "engines": { "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.18.6", + "node_modules/@babel/types": { + "version": "7.29.0", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" }, "engines": { "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-proposal-numeric-separator": { - "version": "7.18.6", + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", "dev": true, + "license": "MIT" + }, + "node_modules/@colors/colors": { + "version": "1.6.0", "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" - }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=0.1.90" } }, - "node_modules/@babel/plugin-proposal-object-rest-spread": { - "version": "7.20.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.20.5", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.20.7" - }, + "node_modules/@csstools/color-helpers": { + "version": "5.1.0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=18" } }, - "node_modules/@babel/plugin-proposal-optional-catch-binding": { - "version": "7.18.6", - "dev": true, + "node_modules/@csstools/css-calc": { + "version": "2.1.4", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" - }, "engines": { - "node": ">=6.9.0" + "node": ">=18" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" } }, - "node_modules/@babel/plugin-proposal-optional-chaining": { - "version": "7.21.0", - "dev": true, + "node_modules/@csstools/css-color-parser": { + "version": "3.1.0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" + "@csstools/color-helpers": "^5.1.0", + "@csstools/css-calc": "^2.1.4" }, "engines": { - "node": ">=6.9.0" + "node": ">=18" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" } }, - "node_modules/@babel/plugin-proposal-private-methods": { - "version": "7.18.6", - "dev": true, + "node_modules/@csstools/css-parser-algorithms": { + "version": "3.0.5", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, "engines": { - "node": ">=6.9.0" + "node": ">=18" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-private-property-in-object": { - "version": "7.21.0-placeholder-for-preset-env.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-unicode-property-regex": { - "version": "7.18.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-export-namespace-from": { - "version": "7.8.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.28.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.28.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.28.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.28.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-unicode-sets-regex": { - "version": "7.18.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.27.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.29.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-remap-async-to-generator": "^7.27.1", - "@babel/traverse": "^7.29.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.28.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-remap-async-to-generator": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.27.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.28.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.28.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.28.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.12.0" - } - }, - "node_modules/@babel/plugin-transform-classes": { - "version": "7.28.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-globals": "^7.28.0", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-replace-supers": "^7.28.6", - "@babel/traverse": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.28.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/template": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.28.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.28.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.28.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.28.5", - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.27.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { - "version": "7.29.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.28.5", - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-dynamic-import": { - "version": "7.27.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.28.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-export-namespace-from": { - "version": "7.27.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-for-of": { - "version": "7.27.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-function-name": { - "version": "7.27.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-compilation-targets": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-json-strings": { - "version": "7.28.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-literals": { - "version": "7.27.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.28.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.27.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.27.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.28.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.29.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-validator-identifier": "^7.28.5", - "@babel/traverse": "^7.29.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.27.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.29.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.28.5", - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-new-target": { - "version": "7.27.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.28.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.28.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.28.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/plugin-transform-destructuring": "^7.28.5", - "@babel/plugin-transform-parameters": "^7.27.7", - "@babel/traverse": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-object-super": { - "version": "7.27.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-replace-supers": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.28.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.28.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-parameters": { - "version": "7.27.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.28.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.28.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-create-class-features-plugin": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.27.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.29.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-regexp-modifiers": { - "version": "7.28.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.28.5", - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.27.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.27.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-spread": { - "version": "7.28.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.27.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.27.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.27.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.27.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-property-regex": { - "version": "7.28.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.28.5", - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.27.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-sets-regex": { - "version": "7.28.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.28.5", - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/preset-env": { - "version": "7.26.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.26.0", - "@babel/helper-compilation-targets": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-validator-option": "^7.25.9", - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.9", - "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.9", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.9", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.25.9", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.9", - "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", - "@babel/plugin-syntax-import-assertions": "^7.26.0", - "@babel/plugin-syntax-import-attributes": "^7.26.0", - "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.25.9", - "@babel/plugin-transform-async-generator-functions": "^7.25.9", - "@babel/plugin-transform-async-to-generator": "^7.25.9", - "@babel/plugin-transform-block-scoped-functions": "^7.25.9", - "@babel/plugin-transform-block-scoping": "^7.25.9", - "@babel/plugin-transform-class-properties": "^7.25.9", - "@babel/plugin-transform-class-static-block": "^7.26.0", - "@babel/plugin-transform-classes": "^7.25.9", - "@babel/plugin-transform-computed-properties": "^7.25.9", - "@babel/plugin-transform-destructuring": "^7.25.9", - "@babel/plugin-transform-dotall-regex": "^7.25.9", - "@babel/plugin-transform-duplicate-keys": "^7.25.9", - "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.9", - "@babel/plugin-transform-dynamic-import": "^7.25.9", - "@babel/plugin-transform-exponentiation-operator": "^7.25.9", - "@babel/plugin-transform-export-namespace-from": "^7.25.9", - "@babel/plugin-transform-for-of": "^7.25.9", - "@babel/plugin-transform-function-name": "^7.25.9", - "@babel/plugin-transform-json-strings": "^7.25.9", - "@babel/plugin-transform-literals": "^7.25.9", - "@babel/plugin-transform-logical-assignment-operators": "^7.25.9", - "@babel/plugin-transform-member-expression-literals": "^7.25.9", - "@babel/plugin-transform-modules-amd": "^7.25.9", - "@babel/plugin-transform-modules-commonjs": "^7.25.9", - "@babel/plugin-transform-modules-systemjs": "^7.25.9", - "@babel/plugin-transform-modules-umd": "^7.25.9", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.25.9", - "@babel/plugin-transform-new-target": "^7.25.9", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.25.9", - "@babel/plugin-transform-numeric-separator": "^7.25.9", - "@babel/plugin-transform-object-rest-spread": "^7.25.9", - "@babel/plugin-transform-object-super": "^7.25.9", - "@babel/plugin-transform-optional-catch-binding": "^7.25.9", - "@babel/plugin-transform-optional-chaining": "^7.25.9", - "@babel/plugin-transform-parameters": "^7.25.9", - "@babel/plugin-transform-private-methods": "^7.25.9", - "@babel/plugin-transform-private-property-in-object": "^7.25.9", - "@babel/plugin-transform-property-literals": "^7.25.9", - "@babel/plugin-transform-regenerator": "^7.25.9", - "@babel/plugin-transform-regexp-modifiers": "^7.26.0", - "@babel/plugin-transform-reserved-words": "^7.25.9", - "@babel/plugin-transform-shorthand-properties": "^7.25.9", - "@babel/plugin-transform-spread": "^7.25.9", - "@babel/plugin-transform-sticky-regex": "^7.25.9", - "@babel/plugin-transform-template-literals": "^7.25.9", - "@babel/plugin-transform-typeof-symbol": "^7.25.9", - "@babel/plugin-transform-unicode-escapes": "^7.25.9", - "@babel/plugin-transform-unicode-property-regex": "^7.25.9", - "@babel/plugin-transform-unicode-regex": "^7.25.9", - "@babel/plugin-transform-unicode-sets-regex": "^7.25.9", - "@babel/preset-modules": "0.1.6-no-external-plugins", - "babel-plugin-polyfill-corejs2": "^0.4.10", - "babel-plugin-polyfill-corejs3": "^0.10.6", - "babel-plugin-polyfill-regenerator": "^0.6.1", - "core-js-compat": "^3.38.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/preset-env/node_modules/semver": { - "version": "6.3.1", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/preset-modules": { - "version": "0.1.6-no-external-plugins", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/@babel/register": { - "version": "7.25.9", - "dev": true, - "license": "MIT", - "dependencies": { - "clone-deep": "^4.0.1", - "find-cache-dir": "^2.0.0", - "make-dir": "^2.1.0", - "pirates": "^4.0.6", - "source-map-support": "^0.5.16" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/template": { - "version": "7.28.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.28.6", - "@babel/parser": "^7.28.6", - "@babel/types": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.29.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/types": "^7.29.0", - "debug": "^4.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.29.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "dev": true, - "license": "MIT" - }, - "node_modules/@colors/colors": { - "version": "1.6.0", - "license": "MIT", - "engines": { - "node": ">=0.1.90" - } - }, - "node_modules/@csstools/color-helpers": { - "version": "5.1.0", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "engines": { - "node": ">=18" - } - }, - "node_modules/@csstools/css-calc": { - "version": "2.1.4", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4" - } - }, - "node_modules/@csstools/css-color-parser": { - "version": "3.1.0", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "dependencies": { - "@csstools/color-helpers": "^5.1.0", - "@csstools/css-calc": "^2.1.4" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4" - } - }, - "node_modules/@csstools/css-parser-algorithms": { - "version": "3.0.5", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-tokenizer": "^3.0.4" + "@csstools/css-tokenizer": "^3.0.4" } }, "node_modules/@csstools/css-tokenizer": { @@ -2398,6 +837,109 @@ "dev": true, "license": "BSD-3-Clause" }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "dev": true, @@ -2810,25 +1352,6 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@jsdoc/salty": { - "version": "0.2.12", - "resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.12.tgz", - "integrity": "sha512-TuB0x50EoAvEX/UEWITd8Mkn3WhiTjSvbTMCLj0BhsQEl5iUzjXdA0bETEVpTk+5TGTLR6QktI9H4hLviVeaAQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "lodash": "^4.18.1" - }, - "engines": { - "node": ">=v12.0.0" - } - }, - "node_modules/@nicolo-ribaudo/chokidar-2": { - "version": "2.1.8-no-fsevents.3", - "dev": true, - "license": "MIT", - "optional": true - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "dev": true, @@ -2861,14 +1384,31 @@ "node": ">= 8" } }, - "node_modules/@openapi-contrib/openapi-schema-to-json-schema": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@openapi-contrib/openapi-schema-to-json-schema/-/openapi-schema-to-json-schema-4.0.0.tgz", - "integrity": "sha512-HRLG6NCHbdjCv3hacJKhZe5Al3QCig90SCvcXAS+JLb85ypuwFqcVSDnGzBqaithlUibq9UyPwQH0ATitvBBTw==", + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", "dev": true, "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@playwright/test": { + "version": "1.60.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.60.0.tgz", + "integrity": "sha512-O71yZIbAh/PxDMNGns37GHBIfrVkEVyn+AXyIa5dOTfb4/xNvRWV+Vv/NMbNCtODB/pO7vLlF2OTmMVLhmr7Ag==", + "dev": true, + "license": "Apache-2.0", "dependencies": { - "fast-deep-equal": "^3.1.3" + "playwright": "1.60.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" } }, "node_modules/@sinclair/typebox": { @@ -2983,81 +1523,330 @@ "@types/node": "*" } }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.3", + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "29.5.14", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", + "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, + "node_modules/@types/jsdom": { + "version": "21.1.7", + "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-21.1.7.tgz", + "integrity": "sha512-yOriVnggzrnQ3a9OKOCxaVuSug3w3/SbOj5i7VwXWZEyUNl3bLF9V3MfxGbZKuwqJOQyRfqXyROBB1CoZLFWzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/tough-cookie": "*", + "parse5": "^7.0.0" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/linkify-it": { + "version": "5.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/markdown-it": { + "version": "14.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/linkify-it": "^5", + "@types/mdurl": "^2" + } + }, + "node_modules/@types/mdurl": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "25.3.1", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.18.0" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/triple-beam": { + "version": "1.3.5", + "license": "MIT" + }, + "node_modules/@types/yargs": { + "version": "17.0.35", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz", + "integrity": "sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/type-utils": "7.18.0", + "@typescript-eslint/utils": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^7.0.0", + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.18.0.tgz", + "integrity": "sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/typescript-estree": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz", + "integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==", "dev": true, "license": "MIT", "dependencies": { - "@types/istanbul-lib-coverage": "*" + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.4", + "node_modules/@typescript-eslint/type-utils": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.18.0.tgz", + "integrity": "sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==", "dev": true, "license": "MIT", "dependencies": { - "@types/istanbul-lib-report": "*" + "@typescript-eslint/typescript-estree": "7.18.0", + "@typescript-eslint/utils": "7.18.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@types/json-schema": { - "version": "7.0.15", + "node_modules/@typescript-eslint/types": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz", + "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } }, - "node_modules/@types/linkify-it": { - "version": "5.0.0", + "node_modules/@typescript-eslint/typescript-estree": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz", + "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==", "dev": true, - "license": "MIT" + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } }, - "node_modules/@types/markdown-it": { - "version": "14.1.2", + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", + "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", "dev": true, "license": "MIT", "dependencies": { - "@types/linkify-it": "^5", - "@types/mdurl": "^2" + "balanced-match": "^1.0.0" } }, - "node_modules/@types/mdurl": { - "version": "2.0.0", + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, - "license": "MIT" + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, - "node_modules/@types/node": { - "version": "25.3.1", + "node_modules/@typescript-eslint/utils": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.18.0.tgz", + "integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~7.18.0" + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/typescript-estree": "7.18.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" } }, - "node_modules/@types/stack-utils": { - "version": "2.0.3", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/triple-beam": { - "version": "1.3.5", - "license": "MIT" - }, - "node_modules/@types/yargs": { - "version": "17.0.35", + "node_modules/@typescript-eslint/visitor-keys": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz", + "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==", "dev": true, "license": "MIT", "dependencies": { - "@types/yargs-parser": "*" + "@typescript-eslint/types": "7.18.0", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@types/yargs-parser": { - "version": "21.0.3", - "dev": true, - "license": "MIT" - }, "node_modules/@ungap/structured-clone": { "version": "1.3.0", "dev": true, @@ -3332,14 +2121,6 @@ } } }, - "node_modules/ansi-colors": { - "version": "4.1.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/ansi-escapes": { "version": "4.3.2", "dev": true, @@ -3432,6 +2213,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/arraybuffer.prototype.slice": { "version": "1.0.4", "dev": true, @@ -3467,14 +2258,6 @@ "dev": true, "license": "MIT" }, - "node_modules/assertion-error": { - "version": "1.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, "node_modules/async": { "version": "3.2.6", "license": "MIT" @@ -3484,206 +2267,45 @@ "dev": true, "license": "MIT", "engines": { - "node": ">= 0.4" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "license": "MIT" - }, - "node_modules/available-typed-arrays": { - "version": "1.0.7", - "dev": true, - "license": "MIT", - "dependencies": { - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/axios": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.16.1.tgz", - "integrity": "sha512-caYkukvroVPO8KrzuJEb50Hm07KwfBZPEC3VeFHTsqWHvKTsy54hjJz9BS/cdaypROE2rH6xvm9mHX4fgWkr3A==", - "dev": true, - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.16.0", - "form-data": "^4.0.5", - "https-proxy-agent": "^5.0.1", - "proxy-from-env": "^2.1.0" - } - }, - "node_modules/axios/node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/axios/node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/babel-jest": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/transform": "^29.7.0", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.6.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.8.0" - } - }, - "node_modules/babel-loader": { - "version": "9.2.1", - "dev": true, - "license": "MIT", - "dependencies": { - "find-cache-dir": "^4.0.0", - "schema-utils": "^4.0.0" - }, - "engines": { - "node": ">= 14.15.0" - }, - "peerDependencies": { - "@babel/core": "^7.12.0", - "webpack": ">=5" - } - }, - "node_modules/babel-loader/node_modules/find-cache-dir": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "common-path-prefix": "^3.0.0", - "pkg-dir": "^7.0.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/babel-loader/node_modules/find-up": { - "version": "6.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^7.1.0", - "path-exists": "^5.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/babel-loader/node_modules/locate-path": { - "version": "7.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^6.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/babel-loader/node_modules/p-limit": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^1.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/babel-loader/node_modules/p-locate": { - "version": "6.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/babel-loader/node_modules/path-exists": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">= 0.4" } }, - "node_modules/babel-loader/node_modules/pkg-dir": { - "version": "7.0.0", + "node_modules/asynckit": { + "version": "0.4.0", + "license": "MIT" + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", "dev": true, "license": "MIT", "dependencies": { - "find-up": "^6.3.0" + "possible-typed-array-names": "^1.0.0" }, "engines": { - "node": ">=14.16" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/babel-loader/node_modules/yocto-queue": { - "version": "1.2.2", + "node_modules/babel-jest": { + "version": "29.7.0", "dev": true, "license": "MIT", + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, "engines": { - "node": ">=12.20" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "@babel/core": "^7.8.0" } }, "node_modules/babel-plugin-istanbul": { @@ -3738,50 +2360,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.4.15", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.28.6", - "@babel/helper-define-polyfill-provider": "^0.6.6", - "semver": "^6.3.1" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { - "version": "6.3.1", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.10.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.2", - "core-js-compat": "^3.38.0" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.6.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.6" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, "node_modules/babel-preset-current-node-syntax": { "version": "1.2.0", "dev": true, @@ -3865,22 +2443,6 @@ "node": "*" } }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/bluebird": { - "version": "3.7.2", - "dev": true, - "license": "MIT" - }, "node_modules/bn.js": { "version": "5.2.3", "dev": true, @@ -3911,11 +2473,6 @@ "dev": true, "license": "MIT" }, - "node_modules/browser-stdout": { - "version": "1.3.1", - "dev": true, - "license": "ISC" - }, "node_modules/browserify-aes": { "version": "1.2.0", "dev": true, @@ -4022,6 +2579,19 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/bser": { "version": "2.1.1", "dev": true, @@ -4193,58 +2763,6 @@ ], "license": "CC-BY-4.0" }, - "node_modules/catharsis": { - "version": "0.9.0", - "dev": true, - "license": "MIT", - "dependencies": { - "lodash": "^4.17.15" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/chai": { - "version": "4.3.6", - "dev": true, - "license": "MIT", - "dependencies": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.2", - "deep-eql": "^3.0.1", - "get-func-name": "^2.0.0", - "loupe": "^2.3.1", - "pathval": "^1.1.1", - "type-detect": "^4.0.5" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/chai-as-promised": { - "version": "7.1.1", - "dev": true, - "license": "WTFPL", - "dependencies": { - "check-error": "^1.0.2" - }, - "peerDependencies": { - "chai": ">= 2.1.2 < 5" - } - }, - "node_modules/chai-string": { - "version": "1.6.0", - "dev": true, - "license": "MIT", - "peerDependencies": { - "chai": "^4.1.2" - } - }, - "node_modules/chai-things": { - "version": "0.2.0", - "dev": true, - "license": "MIT" - }, "node_modules/chalk": { "version": "4.1.2", "dev": true, @@ -4268,51 +2786,6 @@ "node": ">=10" } }, - "node_modules/check-error": { - "version": "1.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "get-func-name": "^2.0.2" - }, - "engines": { - "node": "*" - } - }, - "node_modules/chokidar": { - "version": "3.6.0", - "dev": true, - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chokidar/node_modules/glob-parent": { - "version": "5.1.2", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/chrome-trace-event": { "version": "1.0.4", "dev": true, @@ -4481,19 +2954,6 @@ "node": ">= 0.8" } }, - "node_modules/commander": { - "version": "6.2.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/common-path-prefix": { - "version": "3.0.0", - "dev": true, - "license": "ISC" - }, "node_modules/commondir": { "version": "1.0.1", "dev": true, @@ -4509,18 +2969,6 @@ "dev": true, "license": "MIT" }, - "node_modules/core-js-compat": { - "version": "3.48.0", - "dev": true, - "license": "MIT", - "dependencies": { - "browserslist": "^4.28.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, "node_modules/core-util-is": { "version": "1.0.3", "license": "MIT" @@ -4740,17 +3188,6 @@ } } }, - "node_modules/deep-eql": { - "version": "3.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "type-detect": "^4.0.0" - }, - "engines": { - "node": ">=0.12" - } - }, "node_modules/deep-is": { "version": "0.1.4", "dev": true, @@ -4834,14 +3271,6 @@ "node": ">=8" } }, - "node_modules/diff": { - "version": "5.2.2", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, "node_modules/diff-sequences": { "version": "29.6.3", "dev": true, @@ -4869,6 +3298,19 @@ "version": "1.0.3", "license": "MIT" }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/doctrine": { "version": "3.0.0", "dev": true, @@ -4900,6 +3342,13 @@ "node": ">= 0.4" } }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, "node_modules/electron-to-chromium": { "version": "1.5.302", "dev": true, @@ -5372,6 +3821,36 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "dev": true, @@ -5447,19 +3926,6 @@ "node": ">=8" } }, - "node_modules/find-cache-dir": { - "version": "2.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/find-up": { "version": "5.0.0", "dev": true, @@ -5505,27 +3971,6 @@ "version": "1.1.0", "license": "MIT" }, - "node_modules/follow-redirects": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", - "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, "node_modules/for-each": { "version": "0.3.5", "dev": true, @@ -5599,11 +4044,6 @@ ], "license": "MIT" }, - "node_modules/fs-readdir-recursive": { - "version": "1.1.0", - "dev": true, - "license": "MIT" - }, "node_modules/fs.realpath": { "version": "1.0.0", "dev": true, @@ -5678,14 +4118,6 @@ "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/get-func-name": { - "version": "2.0.2", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, "node_modules/get-intrinsic": { "version": "1.3.0", "license": "MIT", @@ -5754,19 +4186,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/get-value": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-3.0.1.tgz", - "integrity": "sha512-mKZj9JLQrwMBtj5wxi6MH8Z5eSKaERpAwjg43dPtlGI1ZVEgH/qC7T8/6R2OBSUA+zzHBZgICsVJaEIV2tKTDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=6.0" - } - }, "node_modules/glob": { "version": "7.2.3", "dev": true, @@ -5831,6 +4250,27 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/gopd": { "version": "1.2.0", "license": "MIT", @@ -5992,14 +4432,6 @@ "node": ">= 0.4" } }, - "node_modules/he": { - "version": "1.2.0", - "dev": true, - "license": "MIT", - "bin": { - "he": "bin/he" - } - }, "node_modules/hmac-drbg": { "version": "1.0.1", "dev": true, @@ -6296,17 +4728,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/is-boolean-object": { "version": "1.2.2", "dev": true, @@ -6497,14 +4918,6 @@ "node": ">=8" } }, - "node_modules/is-plain-obj": { - "version": "2.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/is-plain-object": { "version": "2.0.4", "dev": true, @@ -6622,17 +5035,6 @@ "dev": true, "license": "MIT" }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-utf8": { "version": "0.2.1", "dev": true, @@ -6813,6 +5215,22 @@ "node": ">=8" } }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, "node_modules/jest": { "version": "29.7.0", "dev": true, @@ -7365,134 +5783,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/js2xmlparser": { - "version": "4.0.2", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "xmlcreate": "^2.0.4" - } - }, - "node_modules/jsdoc": { - "version": "3.6.11", - "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.11.tgz", - "integrity": "sha512-8UCU0TYeIYD9KeLzEcAu2q8N/mx9O3phAGl32nmHlE0LpaJL71mMkP4d+QE5zWfNt50qheHtOZ0qoxVrsX5TUg==", - "dev": true, - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "@babel/parser": "^7.9.4", - "@types/markdown-it": "^12.2.3", - "bluebird": "^3.7.2", - "catharsis": "^0.9.0", - "escape-string-regexp": "^2.0.0", - "js2xmlparser": "^4.0.2", - "klaw": "^3.0.0", - "markdown-it": "^12.3.2", - "markdown-it-anchor": "^8.4.1", - "marked": "^4.0.10", - "mkdirp": "^1.0.4", - "requizzle": "^0.2.3", - "strip-json-comments": "^3.1.0", - "taffydb": "2.6.2", - "underscore": "~1.13.2" - }, - "bin": { - "jsdoc": "jsdoc.js" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/jsdoc/node_modules/@types/markdown-it": { - "version": "12.2.3", - "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz", - "integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@types/linkify-it": "*", - "@types/mdurl": "*" - } - }, - "node_modules/jsdoc/node_modules/entities": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", - "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", - "dev": true, - "license": "BSD-2-Clause", - "peer": true, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/jsdoc/node_modules/escape-string-regexp": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jsdoc/node_modules/linkify-it": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", - "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "uc.micro": "^1.0.1" - } - }, - "node_modules/jsdoc/node_modules/markdown-it": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", - "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "argparse": "^2.0.1", - "entities": "~2.1.0", - "linkify-it": "^3.0.1", - "mdurl": "^1.0.1", - "uc.micro": "^1.0.5" - }, - "bin": { - "markdown-it": "bin/markdown-it.js" - } - }, - "node_modules/jsdoc/node_modules/mdurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/jsdoc/node_modules/mkdirp": { - "version": "1.0.4", - "dev": true, - "license": "MIT", - "peer": true, - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jsdoc/node_modules/uc.micro": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", - "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", - "dev": true, - "license": "MIT", - "peer": true - }, "node_modules/jsdom": { "version": "25.0.1", "license": "MIT", @@ -7552,16 +5842,6 @@ "dev": true, "license": "MIT" }, - "node_modules/json-schema-migrate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/json-schema-migrate/-/json-schema-migrate-2.0.0.tgz", - "integrity": "sha512-r38SVTtojDRp4eD6WsCqiE0eNDt4v1WalBXb9cyZYw9ai5cGtBwzRNWjHzJl38w6TxFkXAIA7h+fyX3tnrAFhQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^8.0.0" - } - }, "node_modules/json-schema-traverse": { "version": "1.0.0", "dev": true, @@ -7609,14 +5889,6 @@ "node": ">=0.10.0" } }, - "node_modules/klaw": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.1.9" - } - }, "node_modules/kleur": { "version": "3.0.3", "dev": true, @@ -7763,20 +6035,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lodash": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", - "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.debounce": { - "version": "4.0.8", + "node_modules/lodash.flattendeep": { + "version": "4.4.0", "dev": true, "license": "MIT" }, - "node_modules/lodash.flattendeep": { - "version": "4.4.0", + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", "dev": true, "license": "MIT" }, @@ -7785,21 +6052,6 @@ "dev": true, "license": "MIT" }, - "node_modules/log-symbols": { - "version": "4.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/logform": { "version": "2.7.0", "license": "MIT", @@ -7838,14 +6090,6 @@ "node": "^12.20.0 || >=14" } }, - "node_modules/loupe": { - "version": "2.3.7", - "dev": true, - "license": "MIT", - "dependencies": { - "get-func-name": "^2.0.1" - } - }, "node_modules/lru-cache": { "version": "5.1.1", "dev": true, @@ -7854,25 +6098,12 @@ "yallist": "^3.0.2" } }, - "node_modules/make-dir": { - "version": "2.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "pify": "^4.0.1", - "semver": "^5.6.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "5.7.2", + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver" - } + "license": "ISC" }, "node_modules/makeerror": { "version": "1.0.12", @@ -7897,25 +6128,9 @@ "markdown-it": "bin/markdown-it.mjs" } }, - "node_modules/markdown-it-anchor": { - "version": "8.6.7", - "dev": true, - "license": "Unlicense", - "peerDependencies": { - "@types/markdown-it": "*", - "markdown-it": "*" - } - }, - "node_modules/marked": { - "version": "4.3.0", - "dev": true, - "license": "MIT", - "bin": { - "marked": "bin/marked.js" - }, - "engines": { - "node": ">= 12" - } + "node_modules/markdown-transform-e2e": { + "resolved": "e2e", + "link": true }, "node_modules/math-intrinsics": { "version": "1.1.0", @@ -7943,6 +6158,16 @@ "dev": true, "license": "MIT" }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, "node_modules/micromatch": { "version": "4.0.8", "dev": true, @@ -8005,151 +6230,49 @@ "node_modules/minimalistic-crypto-utils": { "version": "1.0.1", "dev": true, - "license": "MIT" - }, - "node_modules/minimatch": { - "version": "3.1.5", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/mkdirp": { - "version": "3.0.1", - "dev": true, - "license": "MIT", - "bin": { - "mkdirp": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/mocha": { - "version": "10.8.2", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-colors": "^4.1.3", - "browser-stdout": "^1.3.1", - "chokidar": "^3.5.3", - "debug": "^4.3.5", - "diff": "^5.2.0", - "escape-string-regexp": "^4.0.0", - "find-up": "^5.0.0", - "glob": "^8.1.0", - "he": "^1.2.0", - "js-yaml": "^4.1.0", - "log-symbols": "^4.1.0", - "minimatch": "^5.1.6", - "ms": "^2.1.3", - "serialize-javascript": "^6.0.2", - "strip-json-comments": "^3.1.1", - "supports-color": "^8.1.1", - "workerpool": "^6.5.1", - "yargs": "^16.2.0", - "yargs-parser": "^20.2.9", - "yargs-unparser": "^2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/mocha/node_modules/brace-expansion": { - "version": "2.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/mocha/node_modules/cliui": { - "version": "7.0.4", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } + "license": "MIT" }, - "node_modules/mocha/node_modules/glob": { - "version": "8.1.0", + "node_modules/minimatch": { + "version": "3.1.5", "dev": true, "license": "ISC", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": "*" } }, - "node_modules/mocha/node_modules/minimatch": { - "version": "5.1.9", + "node_modules/minimist": { + "version": "1.2.8", "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/mocha/node_modules/supports-color": { - "version": "8.1.1", + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, + "license": "BlueOak-1.0.0", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "node": ">=16 || 14 >=14.17" } }, - "node_modules/mocha/node_modules/yargs": { - "version": "16.2.0", + "node_modules/mkdirp": { + "version": "3.0.1", "dev": true, "license": "MIT", - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "bin": { + "mkdirp": "dist/cjs/src/bin.js" }, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/ms": { @@ -8589,6 +6712,13 @@ "node": ">=8" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, "node_modules/pako": { "version": "1.0.11", "license": "(MIT AND Zlib)" @@ -8690,12 +6820,38 @@ "dev": true, "license": "MIT" }, - "node_modules/pathval": { - "version": "1.1.1", + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true, "license": "MIT", "engines": { - "node": "*" + "node": ">=8" } }, "node_modules/pbkdf2": { @@ -8730,14 +6886,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/pify": { - "version": "4.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/pinkie": { "version": "2.0.4", "dev": true, @@ -8802,86 +6950,51 @@ "node": ">=0.10.0" } }, - "node_modules/pkg-dir": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "find-up": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-dir/node_modules/find-up": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-dir/node_modules/locate-path": { - "version": "3.0.0", + "node_modules/playwright": { + "version": "1.60.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.60.0.tgz", + "integrity": "sha512-hheHdokM8cdqCb0lcE3s+zT4t4W+vvjpGxsZlDnikarzx8tSzMebh3UiFtgqwFwnTnjYQcsyMF8ei2mCO/tpeA==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" + "playwright-core": "1.60.0" }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-dir/node_modules/p-limit": { - "version": "2.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" + "bin": { + "playwright": "cli.js" }, "engines": { - "node": ">=6" + "node": ">=18" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "optionalDependencies": { + "fsevents": "2.3.2" } }, - "node_modules/pkg-dir/node_modules/p-locate": { - "version": "3.0.0", + "node_modules/playwright-core": { + "version": "1.60.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.60.0.tgz", + "integrity": "sha512-9bW6zvX/m0lEbgTKJ6YppOKx8H3VOPBMOCFh2irXFOT4BbHgrx5hPjwJYLT40Lu+4qtD36qKc/Hn56StUW57IA==", "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^2.0.0" + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" }, "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-dir/node_modules/path-exists": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" + "node": ">=18" } }, - "node_modules/plantuml-encoder": { - "version": "1.4.0", - "dev": true, - "license": "MIT" - }, - "node_modules/pluralize": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", - "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "dev": true, + "hasInstallScript": true, "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=4" + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, "node_modules/possible-typed-array-names": { @@ -8924,6 +7037,15 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/process-nextick-args": { "version": "2.0.1", "license": "MIT" @@ -8951,14 +7073,6 @@ "node": ">= 6" } }, - "node_modules/proxy-from-env": { - "version": "2.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, "node_modules/public-encrypt": { "version": "4.0.3", "dev": true, @@ -9140,17 +7254,6 @@ "version": "5.1.2", "license": "MIT" }, - "node_modules/readdirp": { - "version": "3.6.0", - "dev": true, - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, "node_modules/rechoir": { "version": "0.8.0", "dev": true, @@ -9183,22 +7286,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/regenerate": { - "version": "1.4.2", - "dev": true, - "license": "MIT" - }, - "node_modules/regenerate-unicode-properties": { - "version": "10.2.2", - "dev": true, - "license": "MIT", - "dependencies": { - "regenerate": "^1.4.2" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/regexp.prototype.flags": { "version": "1.5.4", "dev": true, @@ -9218,38 +7305,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/regexpu-core": { - "version": "6.4.0", - "dev": true, - "license": "MIT", - "dependencies": { - "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.2.2", - "regjsgen": "^0.8.0", - "regjsparser": "^0.13.0", - "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.2.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/regjsgen": { - "version": "0.8.0", - "dev": true, - "license": "MIT" - }, - "node_modules/regjsparser": { - "version": "0.13.0", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "jsesc": "~3.1.0" - }, - "bin": { - "regjsparser": "bin/parser" - } - }, "node_modules/release-zalgo": { "version": "1.0.0", "dev": true, @@ -9281,14 +7336,6 @@ "dev": true, "license": "ISC" }, - "node_modules/requizzle": { - "version": "0.2.4", - "dev": true, - "license": "MIT", - "dependencies": { - "lodash": "^4.17.21" - } - }, "node_modules/resolve": { "version": "1.22.11", "dev": true, @@ -9954,6 +8001,22 @@ "node": ">=8" } }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/string.prototype.trim": { "version": "1.2.10", "dev": true, @@ -10017,6 +8080,20 @@ "node": ">=8" } }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-bom": { "version": "4.0.0", "dev": true, @@ -10075,13 +8152,6 @@ "version": "3.2.4", "license": "MIT" }, - "node_modules/taffydb": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz", - "integrity": "sha512-y3JaeRSplks6NYQuCOj3ZFMO3j60rTwbuKCvZxsAraGYH2epusatvZ0baZYA01WsGqJBq/Dl6vOrMUJqyMj8kA==", - "dev": true, - "peer": true - }, "node_modules/tapable": { "version": "2.3.0", "dev": true, @@ -10256,62 +8326,176 @@ "node": ">=16" } }, - "node_modules/tr46": { - "version": "5.1.1", + "node_modules/tr46": { + "version": "5.1.1", + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/traverse": { + "version": "0.6.11", + "dev": true, + "license": "MIT", + "dependencies": { + "gopd": "^1.2.0", + "typedarray.prototype.slice": "^1.0.5", + "which-typed-array": "^1.1.18" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/triple-beam": { + "version": "1.4.1", + "license": "MIT", + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/ts-api-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", + "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/ts-jest": { + "version": "29.4.11", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.11.tgz", + "integrity": "sha512-IrFl7l9AuB/qrNw5quqvAv/hmKMb8dhWOH4jQOGo0Oq8tCeo1O86/iTFG1FaRimgUkF13l4PcepO8ATFT6Ns4g==", + "dev": true, "license": "MIT", "dependencies": { - "punycode": "^2.3.1" + "bs-logger": "^0.2.6", + "fast-json-stable-stringify": "^2.1.0", + "handlebars": "^4.7.9", + "json5": "^2.2.3", + "lodash.memoize": "^4.1.2", + "make-error": "^1.3.6", + "semver": "^7.8.0", + "type-fest": "^4.41.0", + "yargs-parser": "^21.1.1" + }, + "bin": { + "ts-jest": "cli.js" }, "engines": { - "node": ">=18" + "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/transform": "^29.0.0 || ^30.0.0", + "@jest/types": "^29.0.0 || ^30.0.0", + "babel-jest": "^29.0.0 || ^30.0.0", + "jest": "^29.0.0 || ^30.0.0", + "jest-util": "^29.0.0 || ^30.0.0", + "typescript": ">=4.3 <7" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/transform": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jest-util": { + "optional": true + } } }, - "node_modules/traverse": { - "version": "0.6.11", + "node_modules/ts-jest/node_modules/semver": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz", + "integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==", "dev": true, - "license": "MIT", - "dependencies": { - "gopd": "^1.2.0", - "typedarray.prototype.slice": "^1.0.5", - "which-typed-array": "^1.1.18" + "license": "ISC", + "bin": { + "semver": "bin/semver.js" }, "engines": { - "node": ">= 0.4" + "node": ">=10" + } + }, + "node_modules/ts-jest/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/triple-beam": { - "version": "1.4.1", - "license": "MIT", + "node_modules/ts-jest/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", "engines": { - "node": ">= 14.0.0" + "node": ">=12" } }, - "node_modules/tsd-jsdoc": { - "version": "2.5.0", + "node_modules/ts-loader": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.6.0.tgz", + "integrity": "sha512-dsJO0S+T7grTDWTc4a0nTygXGjKncVUpx8Y+af8EvI/D5WgTJby5UEk5eoMCB9EcLQmnvitqh99MqtjtHgAwFQ==", "dev": true, "license": "MIT", "dependencies": { - "typescript": "^3.2.1" + "chalk": "^4.1.0", + "enhanced-resolve": "^5.0.0", + "micromatch": "^4.0.0", + "semver": "^7.3.4", + "source-map": "^0.7.4" + }, + "engines": { + "node": ">=12.0.0" }, "peerDependencies": { - "jsdoc": "^3.6.3" + "loader-utils": "*", + "typescript": "*", + "webpack": "^4.0.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "loader-utils": { + "optional": true + } } }, - "node_modules/tsd-jsdoc/node_modules/typescript": { - "version": "3.9.10", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz", - "integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==", + "node_modules/ts-loader/node_modules/source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, + "license": "BSD-3-Clause", "engines": { - "node": ">=4.2.0" + "node": ">= 12" } }, "node_modules/type-check": { @@ -10325,14 +8509,6 @@ "node": ">= 0.8.0" } }, - "node_modules/type-detect": { - "version": "4.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/type-fest": { "version": "0.20.2", "dev": true, @@ -10494,52 +8670,11 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/underscore": { - "version": "1.13.8", - "dev": true, - "license": "MIT" - }, "node_modules/undici-types": { "version": "7.18.2", "dev": true, "license": "MIT" }, - "node_modules/unicode-canonical-property-names-ecmascript": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-ecmascript": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "unicode-canonical-property-names-ecmascript": "^2.0.0", - "unicode-property-aliases-ecmascript": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-value-ecmascript": { - "version": "2.2.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-property-aliases-ecmascript": { - "version": "2.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/update-browserslist-db": { "version": "1.2.3", "dev": true, @@ -11000,11 +9135,6 @@ "dev": true, "license": "MIT" }, - "node_modules/workerpool": { - "version": "6.5.1", - "dev": true, - "license": "Apache-2.0" - }, "node_modules/wrap-ansi": { "version": "7.0.0", "license": "MIT", @@ -11020,6 +9150,25 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/wrappy": { "version": "1.0.2", "dev": true, @@ -11067,11 +9216,6 @@ "version": "2.2.0", "license": "MIT" }, - "node_modules/xmlcreate": { - "version": "2.0.4", - "dev": true, - "license": "Apache-2.0" - }, "node_modules/xtend": { "version": "4.0.2", "dev": true, @@ -11122,50 +9266,6 @@ "node": ">=12" } }, - "node_modules/yargs-parser": { - "version": "20.2.9", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-unparser": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-unparser/node_modules/camelcase": { - "version": "6.3.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yargs-unparser/node_modules/decamelize": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/yargs/node_modules/yargs-parser": { "version": "21.1.1", "license": "ISC", @@ -11186,7 +9286,7 @@ }, "packages/markdown-cicero": { "name": "@accordproject/markdown-cicero", - "version": "0.16.25", + "version": "1.0.0", "license": "Apache-2.0", "dependencies": { "@accordproject/markdown-common": "*", @@ -11197,668 +9297,698 @@ }, "devDependencies": { "@accordproject/concerto-core": "^4.1.2", + "@types/jest": "^29.5.12", + "@types/markdown-it": "^14.1.2", + "@types/node": "^20.11.30", + "@typescript-eslint/eslint-plugin": "^7.4.0", + "@typescript-eslint/parser": "^7.4.0", "eslint": "8.57.1", "jest": "^29.7.0", "jest-diff": "^29.7.0", - "jsdoc": "4.0.4", "license-check-and-add": "2.3.6", + "rimraf": "^5.0.5", + "ts-jest": "^29.1.2", "typescript": "^5.9.3" }, "engines": { - "node": ">=18", + "node": ">=22", "npm": ">=9" }, "peerDependencies": { "@accordproject/concerto-core": "^4.1.2" } }, - "packages/markdown-cicero/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "packages/markdown-cicero/node_modules/@types/node": { + "version": "20.19.41", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.41.tgz", + "integrity": "sha512-ECymXOukMnOoVkC2bb1Vc/w/836DXncOg5m8Xj1RH7xSHZJWNYY6Zh7EH477vcnD5egKNNfy2RpNOmuChhFPgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "packages/markdown-cicero/node_modules/brace-expansion": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", + "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", "dev": true, "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "packages/markdown-cicero/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/markdown-cicero/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, "engines": { - "node": ">=8" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "packages/markdown-cicero/node_modules/jsdoc": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.4.tgz", - "integrity": "sha512-zeFezwyXeG4syyYHbvh1A967IAqq/67yXtXvuL5wnqCkFZe8I0vKfm+EO+YEvLguo6w9CDUbrAXVtJSHh2E8rw==", + "packages/markdown-cicero/node_modules/rimraf": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", + "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^10.3.7" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/markdown-cicero/node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "dev": true, + "license": "MIT" + }, + "packages/markdown-cli": { + "name": "@accordproject/markdown-cli", + "version": "1.0.0", "license": "Apache-2.0", "dependencies": { - "@babel/parser": "^7.20.15", - "@jsdoc/salty": "^0.2.1", - "@types/markdown-it": "^14.1.1", - "bluebird": "^3.7.2", - "catharsis": "^0.9.0", - "escape-string-regexp": "^2.0.0", - "js2xmlparser": "^4.0.2", - "klaw": "^3.0.0", - "markdown-it": "^14.1.0", - "markdown-it-anchor": "^8.6.7", - "marked": "^4.0.10", - "mkdirp": "^1.0.4", - "requizzle": "^0.2.3", - "strip-json-comments": "^3.1.0", - "underscore": "~1.13.2" + "@accordproject/concerto-util": "^4.1.2", + "@accordproject/markdown-transform": "*", + "yargs": "17.7.2" }, "bin": { - "jsdoc": "jsdoc.js" + "markus": "lib/cli.js" + }, + "devDependencies": { + "@types/jest": "^29.5.12", + "@types/node": "^20.11.30", + "@types/yargs": "^17.0.32", + "@typescript-eslint/eslint-plugin": "^7.4.0", + "@typescript-eslint/parser": "^7.4.0", + "eslint": "8.57.1", + "jest": "^29.7.0", + "license-check-and-add": "2.3.6", + "rimraf": "^5.0.5", + "ts-jest": "^29.1.2", + "typescript": "^5.9.3" }, "engines": { - "node": ">=12.0.0" + "node": ">=22", + "npm": ">=9" } }, - "packages/markdown-cicero/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "packages/markdown-cli/node_modules/@types/node": { + "version": "20.19.41", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.41.tgz", + "integrity": "sha512-ECymXOukMnOoVkC2bb1Vc/w/836DXncOg5m8Xj1RH7xSHZJWNYY6Zh7EH477vcnD5egKNNfy2RpNOmuChhFPgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "packages/markdown-cli/node_modules/brace-expansion": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", + "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", "dev": true, "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "packages/markdown-cli/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, "bin": { - "mkdirp": "bin/cmd.js" + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/markdown-cli/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" }, "engines": { - "node": ">=10" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "packages/markdown-cli": { - "name": "@accordproject/markdown-cli", - "version": "0.16.25", - "license": "Apache-2.0", + "packages/markdown-cli/node_modules/rimraf": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", + "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", + "dev": true, + "license": "ISC", "dependencies": { - "@accordproject/concerto-util": "^4.1.2", - "@accordproject/markdown-transform": "*", - "yargs": "17.7.2" + "glob": "^10.3.7" }, "bin": { - "markus": "index.js" + "rimraf": "dist/esm/bin.mjs" }, - "devDependencies": { - "chai": "4.3.6", - "chai-as-promised": "7.1.1", - "chai-things": "0.2.0", - "eslint": "8.57.1", - "license-check-and-add": "2.3.6", - "mocha": "10.8.2", - "nyc": "17.1.0", - "typescript": "^5.9.3" - }, - "engines": { - "node": ">=18", - "npm": ">=9" + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, + "packages/markdown-cli/node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, "packages/markdown-common": { "name": "@accordproject/markdown-common", - "version": "0.16.25", + "version": "1.0.0", "license": "Apache-2.0", "dependencies": { "@xmldom/xmldom": "^0.9.10", "markdown-it": "^14.1.0" }, "devDependencies": { - "@accordproject/concerto-codegen": "^4.0.1", "@accordproject/concerto-core": "^4.1.3", + "@types/jest": "^29.5.12", + "@types/markdown-it": "^14.1.2", + "@types/node": "^20.11.30", + "@typescript-eslint/eslint-plugin": "^7.4.0", + "@typescript-eslint/parser": "^7.4.0", "eslint": "8.57.1", "jest": "^29.7.0", "jest-diff": "^29.7.0", - "jsdoc": "^4.0.4", "license-check-and-add": "2.3.6", + "rimraf": "^5.0.5", + "ts-jest": "^29.1.2", "typescript": "^5.9.3" }, "engines": { - "node": ">=15", + "node": ">=22", "npm": ">=9" }, "peerDependencies": { "@accordproject/concerto-core": "^4.1.3" } }, - "packages/markdown-common/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "packages/markdown-common/node_modules/@types/node": { + "version": "20.19.41", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.41.tgz", + "integrity": "sha512-ECymXOukMnOoVkC2bb1Vc/w/836DXncOg5m8Xj1RH7xSHZJWNYY6Zh7EH477vcnD5egKNNfy2RpNOmuChhFPgQ==", "dev": true, "license": "MIT", - "engines": { - "node": ">=8" + "dependencies": { + "undici-types": "~6.21.0" } }, - "packages/markdown-common/node_modules/jsdoc": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.5.tgz", - "integrity": "sha512-P4C6MWP9yIlMiK8nwoZvxN84vb6MsnXcHuy7XzVOvQoCizWX5JFCBsWIIWKXBltpoRZXddUOVQmCTOZt9yDj9g==", + "packages/markdown-common/node_modules/brace-expansion": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", + "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "dependencies": { - "@babel/parser": "^7.20.15", - "@jsdoc/salty": "^0.2.1", - "@types/markdown-it": "^14.1.1", - "bluebird": "^3.7.2", - "catharsis": "^0.9.0", - "escape-string-regexp": "^2.0.0", - "js2xmlparser": "^4.0.2", - "klaw": "^3.0.0", - "markdown-it": "^14.1.0", - "markdown-it-anchor": "^8.6.7", - "marked": "^4.0.10", - "mkdirp": "^1.0.4", - "requizzle": "^0.2.3", - "strip-json-comments": "^3.1.0", - "underscore": "~1.13.2" + "balanced-match": "^1.0.0" + } + }, + "packages/markdown-common/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, "bin": { - "jsdoc": "jsdoc.js" + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/markdown-common/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" }, "engines": { - "node": ">=12.0.0" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "packages/markdown-common/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "packages/markdown-common/node_modules/rimraf": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", + "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", "dev": true, - "license": "MIT", + "license": "ISC", + "dependencies": { + "glob": "^10.3.7" + }, "bin": { - "mkdirp": "bin/cmd.js" + "rimraf": "dist/esm/bin.mjs" }, - "engines": { - "node": ">=10" + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, + "packages/markdown-common/node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, "packages/markdown-html": { "name": "@accordproject/markdown-html", - "version": "0.16.25", + "version": "1.0.0", "license": "Apache-2.0", "dependencies": { "@accordproject/markdown-cicero": "*", "@accordproject/markdown-common": "*", "jsdom": "^25.0.1", + "process": "^0.11.10", "type-of": "^2.0.1" }, "devDependencies": { - "@babel/cli": "7.25.9", - "@babel/core": "7.26.0", - "@babel/preset-env": "7.26.0", - "ajv": "^8.17.1", - "babel-loader": "^8.4.1", - "babel-plugin-istanbul": "7.0.0", + "@types/jest": "^29.5.12", + "@types/jsdom": "^21.1.7", + "@types/node": "^20.11.30", + "@typescript-eslint/eslint-plugin": "^7.4.0", + "@typescript-eslint/parser": "^7.4.0", "browserify-zlib": "0.2.0", "buffer": "^6.0.3", "crypto-browserify": "3.12.1", "eslint": "8.57.1", "https-browserify": "1.0.0", "jest": "^29.7.0", - "jsdoc": "^4.0.4", "license-check-and-add": "2.3.6", "raw-loader": "^4.0.2", + "rimraf": "^5.0.5", "stream-browserify": "3.0.0", "stream-http": "3.2.0", - "tsd-jsdoc": "^2.5.0", + "ts-jest": "^29.1.2", + "ts-loader": "^9.5.1", "typescript": "^5.9.3", "vm-browserify": "^1.1.2", "webpack": "5.104.1", "webpack-cli": "5.1.4" }, "engines": { - "node": ">=18", + "node": ">=22", "npm": ">=9" } }, - "packages/markdown-html/node_modules/@babel/core": { - "version": "7.26.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.26.0", - "@babel/generator": "^7.26.0", - "@babel/helper-compilation-targets": "^7.25.9", - "@babel/helper-module-transforms": "^7.26.0", - "@babel/helpers": "^7.26.0", - "@babel/parser": "^7.26.0", - "@babel/template": "^7.25.9", - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.26.0", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "packages/markdown-html/node_modules/babel-loader": { - "version": "8.4.1", - "dev": true, - "license": "MIT", - "dependencies": { - "find-cache-dir": "^3.3.1", - "loader-utils": "^2.0.4", - "make-dir": "^3.1.0", - "schema-utils": "^2.6.5" - }, - "engines": { - "node": ">= 8.9" - }, - "peerDependencies": { - "@babel/core": "^7.0.0", - "webpack": ">=2" - } - }, - "packages/markdown-html/node_modules/babel-plugin-istanbul": { - "version": "7.0.0", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.3", - "istanbul-lib-instrument": "^6.0.2", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "packages/markdown-html/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "packages/markdown-html/node_modules/find-cache-dir": { - "version": "3.3.2", + "packages/markdown-html/node_modules/@types/node": { + "version": "20.19.41", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.41.tgz", + "integrity": "sha512-ECymXOukMnOoVkC2bb1Vc/w/836DXncOg5m8Xj1RH7xSHZJWNYY6Zh7EH477vcnD5egKNNfy2RpNOmuChhFPgQ==", "dev": true, "license": "MIT", "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + "undici-types": "~6.21.0" } }, - "packages/markdown-html/node_modules/find-up": { - "version": "4.1.0", + "packages/markdown-html/node_modules/brace-expansion": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", + "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", "dev": true, "license": "MIT", "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" + "balanced-match": "^1.0.0" } }, - "packages/markdown-html/node_modules/jsdoc": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.5.tgz", - "integrity": "sha512-P4C6MWP9yIlMiK8nwoZvxN84vb6MsnXcHuy7XzVOvQoCizWX5JFCBsWIIWKXBltpoRZXddUOVQmCTOZt9yDj9g==", + "packages/markdown-html/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dev": true, - "license": "Apache-2.0", + "license": "ISC", "dependencies": { - "@babel/parser": "^7.20.15", - "@jsdoc/salty": "^0.2.1", - "@types/markdown-it": "^14.1.1", - "bluebird": "^3.7.2", - "catharsis": "^0.9.0", - "escape-string-regexp": "^2.0.0", - "js2xmlparser": "^4.0.2", - "klaw": "^3.0.0", - "markdown-it": "^14.1.0", - "markdown-it-anchor": "^8.6.7", - "marked": "^4.0.10", - "mkdirp": "^1.0.4", - "requizzle": "^0.2.3", - "strip-json-comments": "^3.1.0", - "underscore": "~1.13.2" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, "bin": { - "jsdoc": "jsdoc.js" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "packages/markdown-html/node_modules/locate-path": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" + "glob": "dist/esm/bin.mjs" }, - "engines": { - "node": ">=8" + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "packages/markdown-html/node_modules/make-dir": { - "version": "3.1.0", + "packages/markdown-html/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "semver": "^6.0.0" + "brace-expansion": "^2.0.2" }, "engines": { - "node": ">=8" + "node": ">=16 || 14 >=14.17" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "packages/markdown-html/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, - "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" + "url": "https://github.com/sponsors/isaacs" } }, - "packages/markdown-html/node_modules/p-limit": { - "version": "2.3.0", + "packages/markdown-html/node_modules/rimraf": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", + "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "p-try": "^2.0.0" + "glob": "^10.3.7" }, - "engines": { - "node": ">=6" + "bin": { + "rimraf": "dist/esm/bin.mjs" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/isaacs" } }, - "packages/markdown-html/node_modules/p-locate": { - "version": "4.1.0", + "packages/markdown-html/node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } + "license": "MIT" }, - "packages/markdown-html/node_modules/pkg-dir": { - "version": "4.2.0", - "dev": true, - "license": "MIT", + "packages/markdown-it-cicero": { + "name": "@accordproject/markdown-it-cicero", + "version": "1.0.0", + "license": "Apache-2.0", "dependencies": { - "find-up": "^4.0.0" + "markdown-it": "^14.1.0" }, - "engines": { - "node": ">=8" - } - }, - "packages/markdown-html/node_modules/schema-utils": { - "version": "2.7.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.5", - "ajv": "^6.12.4", - "ajv-keywords": "^3.5.2" + "devDependencies": { + "@types/jest": "^29.5.12", + "@types/markdown-it": "^14.1.2", + "@types/node": "^20.11.30", + "@typescript-eslint/eslint-plugin": "^7.4.0", + "@typescript-eslint/parser": "^7.4.0", + "eslint": "8.57.1", + "jest": "^29.7.0", + "license-check-and-add": "2.3.6", + "rimraf": "^5.0.5", + "ts-jest": "^29.1.2", + "typescript": "^5.9.3" }, "engines": { - "node": ">= 8.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "node": ">=22", + "npm": ">=9" } }, - "packages/markdown-html/node_modules/schema-utils/node_modules/ajv": { - "version": "6.14.0", + "packages/markdown-it-cicero/node_modules/@types/node": { + "version": "20.19.41", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.41.tgz", + "integrity": "sha512-ECymXOukMnOoVkC2bb1Vc/w/836DXncOg5m8Xj1RH7xSHZJWNYY6Zh7EH477vcnD5egKNNfy2RpNOmuChhFPgQ==", "dev": true, "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "undici-types": "~6.21.0" } }, - "packages/markdown-html/node_modules/schema-utils/node_modules/ajv-keywords": { - "version": "3.5.2", + "packages/markdown-it-cicero/node_modules/brace-expansion": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", + "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", "dev": true, "license": "MIT", - "peerDependencies": { - "ajv": "^6.9.1" + "dependencies": { + "balanced-match": "^1.0.0" } }, - "packages/markdown-html/node_modules/schema-utils/node_modules/json-schema-traverse": { - "version": "0.4.1", - "dev": true, - "license": "MIT" - }, - "packages/markdown-html/node_modules/semver": { - "version": "6.3.1", + "packages/markdown-it-cicero/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dev": true, "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "packages/markdown-it-cicero": { - "name": "@accordproject/markdown-it-cicero", - "version": "0.16.25", - "license": "Apache-2.0", "dependencies": { - "markdown-it": "^14.1.0" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, - "devDependencies": { - "chai": "4.3.6", - "chai-as-promised": "7.1.1", - "chai-string": "^1.5.0", - "chai-things": "0.2.0", - "eslint": "8.57.1", - "jsdoc": "^4.0.4", - "license-check-and-add": "2.3.6", - "mocha": "10.8.2", - "nyc": "17.1.0", - "typescript": "^5.9.3" + "bin": { + "glob": "dist/esm/bin.mjs" }, - "engines": { - "node": ">=18", - "npm": ">=9" - } - }, - "packages/markdown-it-cicero/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "packages/markdown-it-cicero/node_modules/jsdoc": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.5.tgz", - "integrity": "sha512-P4C6MWP9yIlMiK8nwoZvxN84vb6MsnXcHuy7XzVOvQoCizWX5JFCBsWIIWKXBltpoRZXddUOVQmCTOZt9yDj9g==", + "packages/markdown-it-cicero/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, - "license": "Apache-2.0", + "license": "ISC", "dependencies": { - "@babel/parser": "^7.20.15", - "@jsdoc/salty": "^0.2.1", - "@types/markdown-it": "^14.1.1", - "bluebird": "^3.7.2", - "catharsis": "^0.9.0", - "escape-string-regexp": "^2.0.0", - "js2xmlparser": "^4.0.2", - "klaw": "^3.0.0", - "markdown-it": "^14.1.0", - "markdown-it-anchor": "^8.6.7", - "marked": "^4.0.10", - "mkdirp": "^1.0.4", - "requizzle": "^0.2.3", - "strip-json-comments": "^3.1.0", - "underscore": "~1.13.2" - }, - "bin": { - "jsdoc": "jsdoc.js" + "brace-expansion": "^2.0.2" }, "engines": { - "node": ">=12.0.0" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "packages/markdown-it-cicero/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "packages/markdown-it-cicero/node_modules/rimraf": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", + "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", "dev": true, - "license": "MIT", + "license": "ISC", + "dependencies": { + "glob": "^10.3.7" + }, "bin": { - "mkdirp": "bin/cmd.js" + "rimraf": "dist/esm/bin.mjs" }, - "engines": { - "node": ">=10" + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, + "packages/markdown-it-cicero/node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, "packages/markdown-it-template": { "name": "@accordproject/markdown-it-template", - "version": "0.16.25", + "version": "1.0.0", "license": "Apache-2.0", "dependencies": { "markdown-it": "^14.1.0" }, "devDependencies": { - "chai": "4.3.6", - "chai-as-promised": "7.1.1", - "chai-string": "^1.5.0", - "chai-things": "0.2.0", + "@types/jest": "^29.5.12", + "@types/markdown-it": "^14.1.2", + "@types/node": "^20.11.30", + "@typescript-eslint/eslint-plugin": "^7.4.0", + "@typescript-eslint/parser": "^7.4.0", "eslint": "8.57.1", - "jsdoc": "^4.0.4", + "jest": "^29.7.0", "license-check-and-add": "2.3.6", - "mocha": "10.8.2", - "nyc": "17.1.0", + "rimraf": "^5.0.5", + "ts-jest": "^29.1.2", "typescript": "^5.9.3" }, "engines": { - "node": ">=18", + "node": ">=22", "npm": ">=9" } }, - "packages/markdown-it-template/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "packages/markdown-it-template/node_modules/@types/node": { + "version": "20.19.41", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.41.tgz", + "integrity": "sha512-ECymXOukMnOoVkC2bb1Vc/w/836DXncOg5m8Xj1RH7xSHZJWNYY6Zh7EH477vcnD5egKNNfy2RpNOmuChhFPgQ==", "dev": true, "license": "MIT", - "engines": { - "node": ">=8" + "dependencies": { + "undici-types": "~6.21.0" } }, - "packages/markdown-it-template/node_modules/jsdoc": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.5.tgz", - "integrity": "sha512-P4C6MWP9yIlMiK8nwoZvxN84vb6MsnXcHuy7XzVOvQoCizWX5JFCBsWIIWKXBltpoRZXddUOVQmCTOZt9yDj9g==", + "packages/markdown-it-template/node_modules/brace-expansion": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", + "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "dependencies": { - "@babel/parser": "^7.20.15", - "@jsdoc/salty": "^0.2.1", - "@types/markdown-it": "^14.1.1", - "bluebird": "^3.7.2", - "catharsis": "^0.9.0", - "escape-string-regexp": "^2.0.0", - "js2xmlparser": "^4.0.2", - "klaw": "^3.0.0", - "markdown-it": "^14.1.0", - "markdown-it-anchor": "^8.6.7", - "marked": "^4.0.10", - "mkdirp": "^1.0.4", - "requizzle": "^0.2.3", - "strip-json-comments": "^3.1.0", - "underscore": "~1.13.2" + "balanced-match": "^1.0.0" + } + }, + "packages/markdown-it-template/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, "bin": { - "jsdoc": "jsdoc.js" + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/markdown-it-template/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" }, "engines": { - "node": ">=12.0.0" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "packages/markdown-it-template/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "packages/markdown-it-template/node_modules/rimraf": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", + "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", "dev": true, - "license": "MIT", + "license": "ISC", + "dependencies": { + "glob": "^10.3.7" + }, "bin": { - "mkdirp": "bin/cmd.js" + "rimraf": "dist/esm/bin.mjs" }, - "engines": { - "node": ">=10" + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, + "packages/markdown-it-template/node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, "packages/markdown-template": { "name": "@accordproject/markdown-template", - "version": "0.16.25", + "version": "1.0.0", "license": "Apache-2.0", "dependencies": { "@accordproject/markdown-cicero": "*", "@accordproject/markdown-common": "*", "@accordproject/markdown-it-template": "*", "dayjs": "1.11.13", - "markdown-it": "^14.1.0" + "markdown-it": "^14.1.0", + "process": "^0.11.10" }, "devDependencies": { "@accordproject/concerto-core": "^4.1.3", "@accordproject/concerto-cto": "^4.1.3", - "@babel/cli": "7.25.9", - "@babel/core": "7.26.0", - "@babel/preset-env": "7.26.0", - "babel-loader": "9.2.1", - "babel-plugin-istanbul": "7.0.0", - "chai": "4.3.6", - "chai-as-promised": "7.1.1", - "chai-string": "^1.5.0", - "chai-things": "0.2.0", + "@types/jest": "^29.5.12", + "@types/markdown-it": "^14.1.2", + "@types/node": "^20.11.30", + "@typescript-eslint/eslint-plugin": "^7.4.0", + "@typescript-eslint/parser": "^7.4.0", "crypto-browserify": "3.12.1", "eslint": "8.57.1", - "jsdoc": "^4.0.4", + "jest": "^29.7.0", "license-check-and-add": "2.3.6", - "mocha": "10.8.2", - "nyc": "17.1.0", - "raw-loader": "^4.0.2", + "rimraf": "^5.0.5", "stream-browserify": "3.0.0", - "tsd-jsdoc": "^2.5.0", + "ts-jest": "^29.1.2", + "ts-loader": "^9.5.1", "typescript": "^5.9.3", "webpack": "5.104.1", "webpack-cli": "5.1.4" }, "engines": { - "node": ">=18", + "node": ">=22", "npm": ">=9" }, "peerDependencies": { @@ -11866,114 +9996,90 @@ "@accordproject/concerto-cto": "^4.1.3" } }, - "packages/markdown-template/node_modules/@babel/core": { - "version": "7.26.0", + "packages/markdown-template/node_modules/@types/node": { + "version": "20.19.41", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.41.tgz", + "integrity": "sha512-ECymXOukMnOoVkC2bb1Vc/w/836DXncOg5m8Xj1RH7xSHZJWNYY6Zh7EH477vcnD5egKNNfy2RpNOmuChhFPgQ==", "dev": true, "license": "MIT", "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.26.0", - "@babel/generator": "^7.26.0", - "@babel/helper-compilation-targets": "^7.25.9", - "@babel/helper-module-transforms": "^7.26.0", - "@babel/helpers": "^7.26.0", - "@babel/parser": "^7.26.0", - "@babel/template": "^7.25.9", - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.26.0", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "packages/markdown-template/node_modules/babel-plugin-istanbul": { - "version": "7.0.0", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.3", - "istanbul-lib-instrument": "^6.0.2", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=12" + "undici-types": "~6.21.0" } }, - "packages/markdown-template/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "packages/markdown-template/node_modules/brace-expansion": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", + "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", "dev": true, "license": "MIT", - "engines": { - "node": ">=8" + "dependencies": { + "balanced-match": "^1.0.0" } }, - "packages/markdown-template/node_modules/jsdoc": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.5.tgz", - "integrity": "sha512-P4C6MWP9yIlMiK8nwoZvxN84vb6MsnXcHuy7XzVOvQoCizWX5JFCBsWIIWKXBltpoRZXddUOVQmCTOZt9yDj9g==", + "packages/markdown-template/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dev": true, - "license": "Apache-2.0", + "license": "ISC", "dependencies": { - "@babel/parser": "^7.20.15", - "@jsdoc/salty": "^0.2.1", - "@types/markdown-it": "^14.1.1", - "bluebird": "^3.7.2", - "catharsis": "^0.9.0", - "escape-string-regexp": "^2.0.0", - "js2xmlparser": "^4.0.2", - "klaw": "^3.0.0", - "markdown-it": "^14.1.0", - "markdown-it-anchor": "^8.6.7", - "marked": "^4.0.10", - "mkdirp": "^1.0.4", - "requizzle": "^0.2.3", - "strip-json-comments": "^3.1.0", - "underscore": "~1.13.2" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, "bin": { - "jsdoc": "jsdoc.js" + "glob": "dist/esm/bin.mjs" }, - "engines": { - "node": ">=12.0.0" + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "packages/markdown-template/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "packages/markdown-template/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, - "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" }, "engines": { - "node": ">=10" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "packages/markdown-template/node_modules/semver": { - "version": "6.3.1", + "packages/markdown-template/node_modules/rimraf": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", + "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", "dev": true, "license": "ISC", + "dependencies": { + "glob": "^10.3.7" + }, "bin": { - "semver": "bin/semver.js" + "rimraf": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, + "packages/markdown-template/node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, "packages/markdown-transform": { "name": "@accordproject/markdown-transform", - "version": "0.16.25", + "version": "1.0.0", "license": "Apache-2.0", "dependencies": { "@accordproject/markdown-cicero": "*", @@ -11981,321 +10087,118 @@ "@accordproject/markdown-html": "*", "@accordproject/markdown-template": "*", "dijkstrajs": "^1.0.3", - "jszip": "^3.10.1" + "jszip": "^3.10.1", + "process": "^0.11.10" }, "devDependencies": { "@accordproject/concerto-core": "^4.1.3", - "@babel/cli": "7.25.9", - "@babel/core": "7.26.0", - "@babel/preset-env": "7.16.11", - "@babel/register": "7.25.9", - "axios": "^1.15.2", - "babel-loader": "9.2.1", - "babel-plugin-istanbul": "7.0.0", + "@types/jest": "^29.5.12", + "@types/node": "^20.11.30", + "@typescript-eslint/eslint-plugin": "^7.4.0", + "@typescript-eslint/parser": "^7.4.0", "browserify-zlib": "^0.2.0", - "chai": "4.3.6", - "chai-as-promised": "7.1.1", - "chai-string": "^1.5.0", - "chai-things": "0.2.0", "crypto-browserify": "3.12.1", "eslint": "8.57.1", "https-browserify": "^1.0.0", - "jsdoc": "^4.0.4", + "jest": "^29.7.0", "license-check-and-add": "2.3.6", - "mocha": "10.8.2", - "nyc": "17.1.0", - "plantuml-encoder": "^1.4.0", - "raw-loader": "^4.0.2", + "rimraf": "^5.0.5", "stream-browserify": "3.0.0", "stream-http": "^3.2.0", - "tsd-jsdoc": "^2.5.0", + "ts-jest": "^29.1.2", + "ts-loader": "^9.5.1", "typescript": "^5.9.3", "webpack": "^5.104.1", "webpack-cli": "5.1.4" }, "engines": { - "node": ">=18", + "node": ">=22", "npm": ">=9" }, "peerDependencies": { "@accordproject/concerto-core": "^4.1.3" } }, - "packages/markdown-transform/node_modules/@babel/core": { - "version": "7.26.0", + "packages/markdown-transform/node_modules/@types/node": { + "version": "20.19.41", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.41.tgz", + "integrity": "sha512-ECymXOukMnOoVkC2bb1Vc/w/836DXncOg5m8Xj1RH7xSHZJWNYY6Zh7EH477vcnD5egKNNfy2RpNOmuChhFPgQ==", "dev": true, "license": "MIT", "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.26.0", - "@babel/generator": "^7.26.0", - "@babel/helper-compilation-targets": "^7.25.9", - "@babel/helper-module-transforms": "^7.26.0", - "@babel/helpers": "^7.26.0", - "@babel/parser": "^7.26.0", - "@babel/template": "^7.25.9", - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.26.0", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" + "undici-types": "~6.21.0" } }, - "packages/markdown-transform/node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.3.3", + "packages/markdown-transform/node_modules/brace-expansion": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", + "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-compilation-targets": "^7.17.7", - "@babel/helper-plugin-utils": "^7.16.7", - "debug": "^4.1.1", - "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2", - "semver": "^6.1.2" - }, - "peerDependencies": { - "@babel/core": "^7.4.0-0" + "balanced-match": "^1.0.0" } }, - "packages/markdown-transform/node_modules/@babel/plugin-proposal-private-property-in-object": { - "version": "7.21.11", + "packages/markdown-transform/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-create-class-features-plugin": "^7.21.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "packages/markdown-transform/node_modules/@babel/preset-env": { - "version": "7.16.11", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.16.8", - "@babel/helper-compilation-targets": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-validator-option": "^7.16.7", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.16.7", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.16.7", - "@babel/plugin-proposal-async-generator-functions": "^7.16.8", - "@babel/plugin-proposal-class-properties": "^7.16.7", - "@babel/plugin-proposal-class-static-block": "^7.16.7", - "@babel/plugin-proposal-dynamic-import": "^7.16.7", - "@babel/plugin-proposal-export-namespace-from": "^7.16.7", - "@babel/plugin-proposal-json-strings": "^7.16.7", - "@babel/plugin-proposal-logical-assignment-operators": "^7.16.7", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.7", - "@babel/plugin-proposal-numeric-separator": "^7.16.7", - "@babel/plugin-proposal-object-rest-spread": "^7.16.7", - "@babel/plugin-proposal-optional-catch-binding": "^7.16.7", - "@babel/plugin-proposal-optional-chaining": "^7.16.7", - "@babel/plugin-proposal-private-methods": "^7.16.11", - "@babel/plugin-proposal-private-property-in-object": "^7.16.7", - "@babel/plugin-proposal-unicode-property-regex": "^7.16.7", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5", - "@babel/plugin-transform-arrow-functions": "^7.16.7", - "@babel/plugin-transform-async-to-generator": "^7.16.8", - "@babel/plugin-transform-block-scoped-functions": "^7.16.7", - "@babel/plugin-transform-block-scoping": "^7.16.7", - "@babel/plugin-transform-classes": "^7.16.7", - "@babel/plugin-transform-computed-properties": "^7.16.7", - "@babel/plugin-transform-destructuring": "^7.16.7", - "@babel/plugin-transform-dotall-regex": "^7.16.7", - "@babel/plugin-transform-duplicate-keys": "^7.16.7", - "@babel/plugin-transform-exponentiation-operator": "^7.16.7", - "@babel/plugin-transform-for-of": "^7.16.7", - "@babel/plugin-transform-function-name": "^7.16.7", - "@babel/plugin-transform-literals": "^7.16.7", - "@babel/plugin-transform-member-expression-literals": "^7.16.7", - "@babel/plugin-transform-modules-amd": "^7.16.7", - "@babel/plugin-transform-modules-commonjs": "^7.16.8", - "@babel/plugin-transform-modules-systemjs": "^7.16.7", - "@babel/plugin-transform-modules-umd": "^7.16.7", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.16.8", - "@babel/plugin-transform-new-target": "^7.16.7", - "@babel/plugin-transform-object-super": "^7.16.7", - "@babel/plugin-transform-parameters": "^7.16.7", - "@babel/plugin-transform-property-literals": "^7.16.7", - "@babel/plugin-transform-regenerator": "^7.16.7", - "@babel/plugin-transform-reserved-words": "^7.16.7", - "@babel/plugin-transform-shorthand-properties": "^7.16.7", - "@babel/plugin-transform-spread": "^7.16.7", - "@babel/plugin-transform-sticky-regex": "^7.16.7", - "@babel/plugin-transform-template-literals": "^7.16.7", - "@babel/plugin-transform-typeof-symbol": "^7.16.7", - "@babel/plugin-transform-unicode-escapes": "^7.16.7", - "@babel/plugin-transform-unicode-regex": "^7.16.7", - "@babel/preset-modules": "^0.1.5", - "@babel/types": "^7.16.8", - "babel-plugin-polyfill-corejs2": "^0.3.0", - "babel-plugin-polyfill-corejs3": "^0.5.0", - "babel-plugin-polyfill-regenerator": "^0.3.0", - "core-js-compat": "^3.20.2", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=6.9.0" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "packages/markdown-transform/node_modules/@babel/preset-modules": { - "version": "0.1.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", - "@babel/plugin-transform-dotall-regex": "^7.4.4", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" + "bin": { + "glob": "dist/esm/bin.mjs" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "packages/markdown-transform/node_modules/babel-plugin-istanbul": { - "version": "7.0.0", + "packages/markdown-transform/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, - "license": "BSD-3-Clause", + "license": "ISC", "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.3", - "istanbul-lib-instrument": "^6.0.2", - "test-exclude": "^6.0.0" + "brace-expansion": "^2.0.2" }, "engines": { - "node": ">=12" - } - }, - "packages/markdown-transform/node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.3.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.17.7", - "@babel/helper-define-polyfill-provider": "^0.3.3", - "semver": "^6.1.1" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "packages/markdown-transform/node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.5.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.3.2", - "core-js-compat": "^3.21.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "packages/markdown-transform/node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.3.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.3.1" + "node": ">=16 || 14 >=14.17" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "packages/markdown-transform/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "packages/markdown-transform/node_modules/jsdoc": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.5.tgz", - "integrity": "sha512-P4C6MWP9yIlMiK8nwoZvxN84vb6MsnXcHuy7XzVOvQoCizWX5JFCBsWIIWKXBltpoRZXddUOVQmCTOZt9yDj9g==", + "packages/markdown-transform/node_modules/rimraf": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", + "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", "dev": true, - "license": "Apache-2.0", + "license": "ISC", "dependencies": { - "@babel/parser": "^7.20.15", - "@jsdoc/salty": "^0.2.1", - "@types/markdown-it": "^14.1.1", - "bluebird": "^3.7.2", - "catharsis": "^0.9.0", - "escape-string-regexp": "^2.0.0", - "js2xmlparser": "^4.0.2", - "klaw": "^3.0.0", - "markdown-it": "^14.1.0", - "markdown-it-anchor": "^8.6.7", - "marked": "^4.0.10", - "mkdirp": "^1.0.4", - "requizzle": "^0.2.3", - "strip-json-comments": "^3.1.0", - "underscore": "~1.13.2" - }, - "bin": { - "jsdoc": "jsdoc.js" + "glob": "^10.3.7" }, - "engines": { - "node": ">=12.0.0" - } - }, - "packages/markdown-transform/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, - "license": "MIT", "bin": { - "mkdirp": "bin/cmd.js" + "rimraf": "dist/esm/bin.mjs" }, - "engines": { - "node": ">=10" + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "packages/markdown-transform/node_modules/semver": { - "version": "6.3.1", + "packages/markdown-transform/node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } + "license": "MIT" } } } diff --git a/package.json b/package.json index de9358cf..f6306d78 100644 --- a/package.json +++ b/package.json @@ -17,20 +17,22 @@ "npm": ">=9" }, "workspaces": [ - "./packages/*" + "./packages/*", + "./e2e" ], "name": "markdown-transform", "description": "A framework for transforming markdown", - "version": "0.16.25", + "version": "1.0.0", "private": true, "scripts": { - "build": "npm run build --workspaces --if-present", + "build": "npm run build -w @accordproject/markdown-common && npm run build -w @accordproject/markdown-it-template -w @accordproject/markdown-it-cicero && npm run build -w @accordproject/markdown-cicero && npm run build -w @accordproject/markdown-html -w @accordproject/markdown-template && npm run build -w @accordproject/markdown-transform && npm run build -w @accordproject/markdown-cli", "postinstall": "npm run models:get", "models:get": "node ./scripts/external/getExternalModels.js", "models:clean": "node ./scripts/external/cleanExternalModels.js", "coverage": "node ./scripts/coverage.js \"packages/markdown-*\" && nyc report -t coverage --cwd . --report-dir coverage --reporter=lcov && cat ./coverage/lcov.info", "pretest": "npm run licchk", - "test": "npm run test:cov --workspaces", + "test": "npm run test:cov --workspaces --if-present", + "test:e2e": "npm test -w markdown-transform-e2e", "licchk": "license-check-and-add" }, "repository": { @@ -56,6 +58,7 @@ ".github", "node_modules", "packages", + "e2e", "softhsm", "build.cfg", "README.md", @@ -96,7 +99,7 @@ ], "insert_license": false, "license_formats": { - "js|ergo|cto": { + "ts|tsx|js|cto": { "prepend": "/*", "append": " */", "eachLine": { diff --git a/packages/markdown-cicero/.eslintrc.cjs b/packages/markdown-cicero/.eslintrc.cjs new file mode 100644 index 00000000..4fae87ee --- /dev/null +++ b/packages/markdown-cicero/.eslintrc.cjs @@ -0,0 +1,52 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +module.exports = { + root: true, + env: { + es2022: true, + node: true, + jest: true, + }, + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + ], + parser: '@typescript-eslint/parser', + parserOptions: { + ecmaVersion: 2022, + sourceType: 'module', + }, + plugins: ['@typescript-eslint'], + ignorePatterns: [ + 'node_modules/', + 'lib/', + 'coverage/', + '**/*.snap', + ], + rules: { + 'indent': ['error', 4, { 'SwitchCase': 1 }], + 'quotes': ['error', 'single', { 'avoidEscape': true, 'allowTemplateLiterals': true }], + 'semi': ['error', 'always'], + 'no-console': 'warn', + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-var-requires': 'off', + '@typescript-eslint/no-unused-vars': ['error', { 'args': 'none', 'ignoreRestSiblings': true, 'caughtErrors': 'none' }], + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/no-empty-function': 'off', + '@typescript-eslint/ban-ts-comment': 'off', + '@typescript-eslint/no-this-alias': 'off', + 'no-unused-vars': 'off', + }, +}; diff --git a/packages/markdown-cicero/.eslintrc.yml b/packages/markdown-cicero/.eslintrc.yml deleted file mode 100644 index ec0c5d88..00000000 --- a/packages/markdown-cicero/.eslintrc.yml +++ /dev/null @@ -1,47 +0,0 @@ -env: - es6: true - node: true - mocha: true -extends: 'eslint:recommended' -parserOptions: - ecmaVersion: 12 - sourceType: 'script' -rules: - indent: - - error - - 4 - linebreak-style: - - warn - - unix - quotes: - - error - - single - semi: - - error - - always - no-unused-vars: - - error - - args: none - no-console: warn - curly: error - eqeqeq: error - no-throw-literal: error - strict: error - no-var: error - dot-notation: error - no-tabs: error - no-trailing-spaces: error - # no-use-before-define: error - no-useless-call: error - no-with: error - operator-linebreak: error - require-jsdoc: - - error - - require: - ClassDeclaration: true - MethodDefinition: true - FunctionDeclaration: true - valid-jsdoc: - - error - - requireReturn: false - yoda: error diff --git a/packages/markdown-cicero/.gitignore b/packages/markdown-cicero/.gitignore index a6cf488d..4bd86293 100644 --- a/packages/markdown-cicero/.gitignore +++ b/packages/markdown-cicero/.gitignore @@ -15,6 +15,7 @@ pids /out /dist /umd +/lib # Directory for instrumented libs generated by jscoverage/JSCover lib-cov diff --git a/packages/markdown-cicero/README.md b/packages/markdown-cicero/README.md index 3095a163..735941d5 100644 --- a/packages/markdown-cicero/README.md +++ b/packages/markdown-cicero/README.md @@ -1,32 +1,49 @@ -# CiceroMark Transform +# CiceroMark Transformer -This package extends CommonMark to introduce three new DOM nodes: -1. Clause -2. Variable -3. ComputedVariable +Converts CiceroMark — markdown with embedded contract clauses, variables, conditionals, formulas and lists — to/from CommonMark and to/from markdown text. -These are expressed using markdown code blocks and html inlines to ensure that they are safely persisted within markdown text. +CiceroMark extends CommonMark with these additional nodes: -Use `CiceroMarkTransform` to map from the CommonMark DOM nodes to CiceroMark DOM nodes. +- `Clause` +- `Variable`, `FormattedVariable`, `EnumVariable` +- `Conditional`, `Optional` +- `Formula` +- `ListBlock` + +Schema: [`ciceromark@0.6.0`](https://models.accordproject.org/markdown/ciceromark@0.6.0.html). ## Installation ``` -npm install @accordproject/markdown-cicero --save +npm install @accordproject/markdown-cicero ``` -## Usage +Peer dependency: `@accordproject/concerto-core@^4.1.2`. -``` javascript +## Usage -const CiceroMarkTransformer = require('@accordproject/markdown-cicero').CiceroMarkTransformer; +```ts +import { CiceroMarkTransformer } from '@accordproject/markdown-cicero'; const ciceroMarkTransformer = new CiceroMarkTransformer(); -const dom = ciceroMarkTransformer.fromMarkdown( '# Heading One'); -const newMarkdown = ciceroMarkTransformer.toMarkdown(dom); + +// markdown_cicero string → CiceroMark DOM +const dom = ciceroMarkTransformer.fromMarkdownCicero( + '{{#clause greeting}}Hello {{name}}{{/clause}}' +); + +// CiceroMark DOM → markdown_cicero string +const newMarkdown = ciceroMarkTransformer.toMarkdownCicero(dom); ``` +If you have a plain markdown string (no clauses/variables), use `fromMarkdown` / `toMarkdown` — they round-trip through CommonMark. + +## What this package exports + +- `CiceroMarkTransformer` — the main entry point +- `FromCiceroEditVisitor` — convert legacy CiceroEdit markdown into CiceroMark +- `ToCommonMarkVisitor` — strip CiceroMark-specific nodes back to plain CommonMark +- `Decorators` — read structured decorators (`@decorator(args)`) attached to CiceroMark nodes + ## License Accord Project source code files are made available under the Apache License, Version 2.0 (Apache-2.0), located in the LICENSE file. Accord Project documentation files are made available under the Creative Commons Attribution 4.0 International License (CC-BY-4.0), available at http://creativecommons.org/licenses/by/4.0/. - -© 2017-2019 Clause, Inc. diff --git a/packages/markdown-cicero/jest.config.js b/packages/markdown-cicero/jest.config.js index 6572ff4e..c8c592ed 100644 --- a/packages/markdown-cicero/jest.config.js +++ b/packages/markdown-cicero/jest.config.js @@ -13,185 +13,18 @@ */ 'use strict'; -// For a detailed explanation regarding each configuration property, visit: -// https://jestjs.io/docs/en/configuration.html +/** @type {import('jest').Config} */ module.exports = { - // All imported modules in your tests should be mocked automatically - // automock: false, - - // Stop running tests after `n` failures - // bail: 0, - - // Respect "browser" field in package.json when resolving modules - // browser: false, - - // The directory where Jest should store its cached dependency information - // cacheDirectory: "/private/var/folders/tv/4ljndl3s2jg90nxd8h7f3bgr0000gn/T/jest_dx", - - // Automatically clear mock calls and instances between every test + preset: 'ts-jest', + testEnvironment: 'node', clearMocks: true, - - // Indicates whether the coverage information should be collected while executing the test - // collectCoverage: false, - - // An array of glob patterns indicating a set of files for which coverage information should be collected - collectCoverageFrom: [ 'lib/**/*.js' ], - - // The directory where Jest should output its coverage files + testMatch: ['/src/**/*.test.ts'], + collectCoverageFrom: ['src/**/*.ts', '!src/**/*.test.ts', '!src/**/*.d.ts'], coverageDirectory: 'coverage', - - // An array of regexp pattern strings used to skip coverage collection - coveragePathIgnorePatterns: [ - '/node_modules/' - ], - - // A list of reporter names that Jest uses when writing coverage reports - coverageReporters: [ - 'json', - 'text', - 'lcov', - 'html' - ], - - // An object that configures minimum threshold enforcement for coverage results - // coverageThreshold: null, - - // A path to a custom dependency extractor - // dependencyExtractor: null, - - // Make calling deprecated APIs throw helpful error messages - // errorOnDeprecated: false, - - // Force coverage collection from ignored files using an array of glob patterns - // forceCoverageMatch: [], - - // A path to a module which exports an async function that is triggered once before all test suites - // globalSetup: null, - - // A path to a module which exports an async function that is triggered once after all test suites - // globalTeardown: null, - - // A set of global variables that need to be available in all test environments - // globals: {}, - - // An array of directory names to be searched recursively up from the requiring module's location - // moduleDirectories: [ - // "node_modules" - // ], - - // An array of file extensions your modules use - // moduleFileExtensions: [ - // "js", - // "json", - // "jsx", - // "ts", - // "tsx", - // "node" - // ], - - // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader - // modulePathIgnorePatterns: [], - - // Activates notifications for test results - // notify: false, - - // An enum that specifies notification mode. Requires { notify: true } - // notifyMode: "failure-change", - - // A preset that is used as a base for Jest's configuration - // preset: null, - - // Run tests from one or more projects - // projects: null, - - // Use this configuration option to add custom reporters to Jest - // reporters: undefined, - - // Automatically reset mock state between every test - // resetMocks: false, - - // Reset the module registry before running each individual test - // resetModules: false, - - // A path to a custom resolver - // resolver: null, - - // Automatically restore mock state between every test - // restoreMocks: false, - - // The root directory that Jest should scan for tests and modules within - // rootDir: null, - - // A list of paths to directories that Jest should use to search for files in - // roots: [ - // "" - // ], - - // Allows you to use a custom runner instead of Jest's default test runner - // runner: "jest-runner", - - // The paths to modules that run some code to configure or set up the testing environment before each test - // setupFiles: [], - - // A list of paths to modules that run some code to configure or set up the testing framework before each test - // setupFilesAfterEnv: [], - - // A list of paths to snapshot serializer modules Jest should use for snapshot testing - // snapshotSerializers: [], - - // The test environment that will be used for testing - testEnvironment: 'node', - - // Options that will be passed to the testEnvironment - // testEnvironmentOptions: {}, - - // Adds a location field to test results - // testLocationInResults: false, - - // The glob patterns Jest uses to detect test files - // testMatch: [ - // "**/__tests__/**/*.[jt]s?(x)", - // "**/?(*.)+(spec|test).[tj]s?(x)" - // ], - - // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped - // testPathIgnorePatterns: [ - // "/node_modules/" - // ], - - // The regexp pattern or array of patterns that Jest uses to detect test files - // testRegex: [], - - // This option allows the use of a custom results processor - // testResultsProcessor: null, - - // This option allows use of a custom test runner - // testRunner: "jasmine2", - - // This option sets the URL for the jsdom environment. It is reflected in properties such as location.href - // testURL: "http://localhost", - - // Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout" - // timers: "real", - - // A map from regular expressions to paths to transformers - // transform: {}, - - // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation - // transformIgnorePatterns: [ - // 'node_modules' - // ], - - // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them - // unmockedModulePathPatterns: undefined, - - // Indicates whether each individual test should be reported during the run - verbose: true, - - // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode - // watchPathIgnorePatterns: [], - - // Whether to use watchman for file crawling - // watchman: true, + coveragePathIgnorePatterns: ['/node_modules/'], + coverageReporters: ['json', 'text', 'lcov', 'html'], + transform: { + '^.+\\.tsx?$': ['ts-jest', { tsconfig: '/tsconfig.test.json' }], + }, }; diff --git a/packages/markdown-cicero/jsdoc.json b/packages/markdown-cicero/jsdoc.json deleted file mode 100644 index 0e564706..00000000 --- a/packages/markdown-cicero/jsdoc.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "tags": { - "allowUnknownTags": true, - "dictionaries": ["jsdoc", "closure"] - }, - "source": { - "include": [ - "./lib", - "./index.js" - ], - "includePattern": ".+\\.js(doc|x)?$" - }, - "plugins": ["plugins/markdown"], - "templates": { - "logoFile": "", - "cleverLinks": false, - "monospaceLinks": false, - "dateFormat": "ddd MMM Do YYYY", - "outputSourceFiles": true, - "outputSourcePath": true, - "systemName": "Accord Project Cicero SDK", - "footer": "", - "copyright": "Released under the Apache License v2.0", - "navType": "vertical", - "theme": "spacelab", - "linenums": true, - "collapseSymbols": false, - "inverseNav": true, - "protocol": "html://", - "methodHeadingReturns": false - }, - "markdown": { - "parser": "gfm", - "hardwrap": true - } -} \ No newline at end of file diff --git a/packages/markdown-cicero/lib/CiceroMarkTransformer.js b/packages/markdown-cicero/lib/CiceroMarkTransformer.js deleted file mode 100644 index ef0f8c8a..00000000 --- a/packages/markdown-cicero/lib/CiceroMarkTransformer.js +++ /dev/null @@ -1,253 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -/** @typedef {import('@accordproject/markdown-common/types/model/commonmark').IDocument} IDocument */ -/** @typedef {import('@accordproject/markdown-common/types/model/commonmark').IDocument} ICiceroDocument */ -/** @typedef {import('@accordproject/markdown-common/types/model/ciceromark').IClause} IClause */ - -const { ModelManager, Factory, Serializer } = require('@accordproject/concerto-core'); - -const MarkdownIt = require('markdown-it'); -const MarkdownItCicero = require('@accordproject/markdown-it-cicero'); -const FromMarkdownIt = require('@accordproject/markdown-common').FromMarkdownIt; -const cicerorules = require('./cicerorules'); -const ToMarkdownCiceroVisitor = require('./ToMarkdownCiceroVisitor'); -const ToCiceroMarkUnwrappedVisitor = require('./ToCiceroMarkUnwrappedVisitor'); - -const CommonMarkTransformer = require('@accordproject/markdown-common').CommonMarkTransformer; - -const FromCiceroEditVisitor = require('./FromCiceroEditVisitor'); -const ToCommonMarkVisitor = require('./ToCommonMarkVisitor'); - -const {CommonMarkModel,CiceroMarkModel,ConcertoMetaModel} = require('@accordproject/markdown-common'); - -const unquoteVariables = require('./UnquoteVariables'); - -/** - * Converts a CiceroMark DOM to/from a - * CommonMark DOM. - * - * Converts a CiceroMark DOM to/from a markdown string. - */ -class CiceroMarkTransformer { - /** - * Construct the parser. - */ - constructor() { - // Setup for Nested Parsing - this.commonMark = new CommonMarkTransformer(); - - // Setup for validation - this.modelManager = new ModelManager({strict: true}); - this.modelManager.addCTOModel(ConcertoMetaModel.MODEL, 'metamodel.cto'); - this.modelManager.addCTOModel(CommonMarkModel.MODEL, 'commonmark.cto'); - this.modelManager.addCTOModel(CiceroMarkModel.MODEL, 'ciceromark.cto'); - const factory = new Factory(this.modelManager); - this.serializer = new Serializer(factory, this.modelManager); - } - - /** - * Obtain the Clause text for a Clause node - * @param {IClause} input CiceroMark DOM - * @returns {string} markdown_cicero string - */ - getClauseText(input) { - if (input.$class === `${CiceroMarkModel.NAMESPACE}.Clause`) { - const docInput = { - $class: `${CommonMarkModel.NAMESPACE}.Document`, - xmlns : 'http://commonmark.org/xml/1.0', - nodes: input.nodes, - }; - return this.toMarkdownCicero(docInput); - } else { - throw new Error('Cannot apply getClauseText to non-clause node'); - } - } - - /** - * Retrieve the serializer used by the parser - * - * @returns {Serializer} a serializer capable of dealing with the Concerto - * object returns by parse - */ - getSerializer() { - return this.serializer; - } - - /** - * Converts a CiceroEdit string to a CiceroMark DOM - * @param {string} input - ciceroedit string - * @returns {ICiceroDocument} CiceroMark DOM - */ - fromCiceroEdit(input) { - const commonMark = this.commonMark.fromMarkdown(input); - const dom = this.serializer.fromJSON(commonMark); - - // Add Cicero nodes - const parameters = { - ciceroMark: this, - commonMark: this.commonMark, - modelManager : this.modelManager, - serializer : this.serializer, - }; - const visitor = new FromCiceroEditVisitor(); - dom.accept(visitor, parameters); - return this.serializer.toJSON(dom); - } - - /** - * Converts a CiceroMark DOM to a CiceroMark Unwrapped DOM - * @param {ICiceroDocument} input - CiceroMark DOM (JSON) - * @param {object} [options] configuration options - * @param {boolean} [options.unquoteVariables] if true variable quotations are removed - * @returns {ICiceroDocument} CiceroMark DOM - */ - toCiceroMarkUnwrapped(input,options) { - // remove variables, e.g. {{ variable }}, {{% formula %}} - if(options && Object.prototype.hasOwnProperty.call(options,'unquoteVariables') && options.unquoteVariables) { - input = this.unquote(input); - } - - const dom = this.serializer.fromJSON(input); - - // convert to common mark - const visitor = new ToCiceroMarkUnwrappedVisitor(); - dom.accept(visitor, { - modelManager : this.modelManager, - }); - - return this.serializer.toJSON(dom); - } - - /** - * Converts a CommonMark DOM to a CiceroMark DOM - * @param {IDocument} input - CommonMark DOM (in JSON) - * @returns {ICiceroDocument} CiceroMark DOM - */ - fromCommonMark(input) { - return input; // Now the identity - } - - /** - * Converts a markdown string to a CiceroMark DOM - * @param {string} markdown a markdown string - * @returns {ICiceroDocument} ciceromark object (JSON) - */ - fromMarkdown(markdown) { - const commonMarkDom = this.commonMark.fromMarkdown(markdown); - return this.fromCommonMark(commonMarkDom); - } - - /** - * Converts a CiceroMark DOM to a markdown string - * @param {ICiceroDocument} input CiceroMark DOM - * @param {object} [options] configuration options - * @returns {string} markdown string - */ - toMarkdown(input, options) { - const commonMarkDom = this.toCommonMark(input, options); - return this.commonMark.toMarkdown(commonMarkDom); - } - - /** - * Converts a cicero markdown string to a CiceroMark DOM - * @param {string} markdown a cicero markdown string - * @param {object} [options] configuration options - * @returns {ICiceroDocument} ciceromark object (JSON) - */ - fromMarkdownCicero(markdown, options) { - const tokens = this.toTokens(markdown); - return this.fromTokens(tokens); - } - - /** - * Converts a CiceroMark DOM to a cicero markdown string - * @param {ICiceroDocument} input CiceroMark DOM - * @returns {string} cicero markdown string - */ - toMarkdownCicero(input) { - const visitor = new ToMarkdownCiceroVisitor(); - return visitor.toMarkdownCicero(this.serializer.fromJSON(input)); - } - - /** - * Converts a CiceroMark DOM to a CommonMark DOM - * @param {ICiceroDocument} input CiceroMark DOM - * @param {object} [options] configuration options - * @param {boolean} [options.removeFormatting] if true the formatting nodes are removed - * @param {boolean} [options.unquoteVariables] if true variable quotations are removed - * @returns {IDocument} CommonMark DOM - */ - toCommonMark(input, options) { - let json = this.toCiceroMarkUnwrapped(input,options); - const dom = this.serializer.fromJSON(json); - - // convert to common mark - const visitor = new ToCommonMarkVisitor(options); - dom.accept( visitor, { - commonMark: this.commonMark, - modelManager : this.modelManager, - serializer : this.serializer - } ); - - let result = this.serializer.toJSON(dom); - // remove formatting - if(options && options.removeFormatting) { - const cmt = new CommonMarkTransformer(options); - result = cmt.removeFormatting(result); - } - - return result; - } - - /** - * Unquotes a CiceroMark DOM - * @param {ICiceroDocument} input CiceroMark DOM - * @returns {ICiceroDocument} unquoted CiceroMark DOM - */ - unquote(input) { - return unquoteVariables(input); - } - - /** - * Converts a ciceromark string into a token stream - * - * @param {string} input the string to parse - * @returns {object[]} a markdown-it token stream - */ - toTokens(input) { - const parser = new MarkdownIt({html:true}).use(MarkdownItCicero); // XXX HTML inlines and code blocks true - const tokenStream = parser.parse(input,{}); - return tokenStream; - } - - /** - * Converts a token stream into a CiceroMark DOM object. - * - * @param {object[]} tokenStream the token stream - * @returns {ICiceroDocument} the CiceroMark DOM (JSON) - */ - fromTokens(tokenStream) { - const fromMarkdownIt = new FromMarkdownIt(cicerorules); - const json = fromMarkdownIt.toCommonMark(tokenStream); - - // validate the object using the model - const validJson = this.serializer.fromJSON(json); - return this.serializer.toJSON(validJson); - } -} - -module.exports = CiceroMarkTransformer; \ No newline at end of file diff --git a/packages/markdown-cicero/lib/Decorators.test.js b/packages/markdown-cicero/lib/Decorators.test.js deleted file mode 100644 index c5bc5b65..00000000 --- a/packages/markdown-cicero/lib/Decorators.test.js +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// @ts-nocheck -/* eslint-disable no-undef */ -'use strict'; - -const fs = require('fs'); -const Decorators = require('./Decorators'); - -/** - * Load a test node from disk - * @param {string} name the name of the file to load - * @returns {object} the node - */ -function loadNode(name) { - return JSON.parse(fs.readFileSync( __dirname + `/../test/data/decorators/${name}`, 'utf-8')); -} - -describe('decorators', () => { - it('handles string decorator', () => { - const decorators = new Decorators( loadNode('string.json')); - expect(decorators.getDecoratorValue( 'Test', 'type')).toBe('value'); - }); - - it('handles boolean decorator', () => { - const decorators = new Decorators( loadNode('boolean.json')); - expect(decorators.getDecoratorValue( 'Test', 'type')).toBe(true); - }); - - it('handles number decorator', () => { - const decorators = new Decorators( loadNode('number.json')); - expect(decorators.getDecoratorValue( 'Test', 'type')).toBe(3.14); - }); - - it('handles identifier decorator', () => { - const decorators = new Decorators( loadNode('identifier.json')); - expect(decorators.getDecoratorValue( 'Test', 'type')).toBe('typeIdentifier'); - }); - - it('handles getting a value for a missing decorator', () => { - const decorators = new Decorators( loadNode('string.json')); - expect(decorators.getDecoratorValue( 'Missing', 'type')).toBe(null); - }); - - it('handles getting a missing value for a decorator', () => { - const decorators = new Decorators( loadNode('string.json')); - expect(decorators.getDecoratorValue( 'Test', 'missing')).toBe(undefined); - }); - - it('handles mutiple args decorator', () => { - const decorators = new Decorators( loadNode('multi.json')); - expect(decorators.getDecoratorValue( 'Test', 'type')).toBe('value'); - expect(decorators.getDecoratorValue( 'Test', 'second')).toBe('value2'); - }); - - it('hasDecorator', () => { - const decorators = new Decorators( loadNode('multi.json')); - expect(decorators.hasDecorator( 'Test')).toBe(true); - }); - - it('getArguments', () => { - const decorators = new Decorators( loadNode('multi.json')); - expect(decorators.getArguments( 'Test')).toStrictEqual({second: 'value2', type: 'value'}); - }); - - it('fails with odd number of arguments', () => { - expect(() => new Decorators( loadNode('invalid-odd.json'))).toThrow('Arguments must be [name, value] pairs'); - }); - - it('fails with argument names that are not strings', () => { - expect(() => new Decorators( loadNode('invalid-arg-name.json'))).toThrow('Argument names must be strings'); - }); - -}); diff --git a/packages/markdown-cicero/lib/FromCiceroEditVisitor.js b/packages/markdown-cicero/lib/FromCiceroEditVisitor.js deleted file mode 100644 index 2f0baad7..00000000 --- a/packages/markdown-cicero/lib/FromCiceroEditVisitor.js +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -const { CiceroMarkModel, CommonMarkModel } = require('@accordproject/markdown-common'); - -/** - * Converts a CommonMark DOM to a CiceroMark DOM - */ -class FromCiceroEditVisitor { - /** - * Visits a sub-tree and return CiceroMark DOM - * @param {*} visitor the visitor to use - * @param {*} thing the node to visit - * @param {*} [parameters] optional parameters - */ - static visitChildren(visitor, thing, parameters) { - if(thing.nodes) { - FromCiceroEditVisitor.visitNodes(visitor, thing.nodes, parameters); - } - } - - /** - * Visits a list of nodes and return the CiceroMark DOM - * @param {*} visitor the visitor to use - * @param {*} things the list node to visit - * @param {*} [parameters] optional parameters - */ - static visitNodes(visitor, things, parameters) { - things.forEach(node => { - node.accept(visitor, parameters); - }); - } - - /** - * Visit a node - * @param {*} thing the object being visited - * @param {*} parameters the parameters - */ - visit(thing, parameters) { - switch(thing.getType()) { - case 'CodeBlock': { - const tag = thing.tag; - if (tag && tag.tagName === 'clause' && tag.attributes.length === 2) { - const ciceroMarkTag = `${CiceroMarkModel.NAMESPACE}.Clause`; - // Remove last new line, needed by CommonMark parser to identify ending code block (\n```) - const clauseText = thing.text; - - //console.log('CONTENT! : ' + tag.content); - if (FromCiceroEditVisitor.getAttribute(tag.attributes, 'src') && - FromCiceroEditVisitor.getAttribute(tag.attributes, 'clauseid')) { - thing.$classDeclaration = parameters.modelManager.getType(ciceroMarkTag); - thing.src = FromCiceroEditVisitor.getAttribute(tag.attributes, 'src').value; - thing.name = FromCiceroEditVisitor.getAttribute(tag.attributes, 'clauseid').value; - - const commonMark = parameters.commonMark.fromMarkdown(clauseText); - thing.nodes = parameters.serializer.fromJSON(commonMark).nodes; - FromCiceroEditVisitor.visitNodes(this, thing.nodes, parameters); - - thing.text = null; // Remove text - delete thing.tag; - delete thing.info; - } - } else if (tag && tag.tagName === 'list' && tag.attributes.length === 0) { - const ciceroMarkTag = `${CiceroMarkModel.NAMESPACE}.ListBlock`; - // Remove last new line, needed by CommonMark parser to identify ending code block (\n```) - const listText = thing.text; - - const commonMark = parameters.commonMark.fromMarkdown(listText); - const newNodes = parameters.serializer.fromJSON(commonMark).nodes; - if (newNodes.length === 1 && newNodes[0].getType() === 'List') { - const listNode = newNodes[0]; - thing.$classDeclaration = parameters.modelManager.getType(ciceroMarkTag); - thing.name = ''; // XXX Hack -- since there is no name in CiceroEdit -- will be filled in later - thing.type = listNode.type; - thing.start = listNode.start; - thing.tight = listNode.tight; - thing.delimiter = listNode.delimiter; - thing.nodes = listNode.nodes; - FromCiceroEditVisitor.visitNodes(this, thing.nodes, parameters); - - thing.text = null; // Remove text - delete thing.tag; - delete thing.info; - } - } - } - break; - //case 'HtmlBlock': - case 'HtmlInline': { - if (thing.tag && - thing.tag.tagName === 'variable' && - (thing.tag.attributes.length === 2 || thing.tag.attributes.length === 3)) { - const tag = thing.tag; - if (FromCiceroEditVisitor.getAttribute(tag.attributes, 'id') && - FromCiceroEditVisitor.getAttribute(tag.attributes, 'value')) { - const format = FromCiceroEditVisitor.getAttribute(tag.attributes, 'format'); - const ciceroMarkTag = format ? `${CiceroMarkModel.NAMESPACE}.FormattedVariable` : `${CiceroMarkModel.NAMESPACE}.Variable`; - thing.$classDeclaration = parameters.modelManager.getType(ciceroMarkTag); - thing.name = FromCiceroEditVisitor.getAttribute(tag.attributes, 'id').value; - thing.value = decodeURIComponent(FromCiceroEditVisitor.getAttribute(tag.attributes, 'value').value); - if (format) { // For FormattedVariables - thing.format = decodeURIComponent(format.value); - } - delete thing.tag; - delete thing.text; - } - } - if (thing.tag && - thing.tag.tagName === 'if' && - thing.tag.attributes.length === 4) { - const tag = thing.tag; - if (FromCiceroEditVisitor.getAttribute(tag.attributes, 'id') && - FromCiceroEditVisitor.getAttribute(tag.attributes, 'value') && - FromCiceroEditVisitor.getAttribute(tag.attributes, 'whenTrue') && - FromCiceroEditVisitor.getAttribute(tag.attributes, 'whenFalse')) { - const ciceroMarkTag = `${CiceroMarkModel.NAMESPACE}.Conditional`; - thing.$classDeclaration = parameters.modelManager.getType(ciceroMarkTag); - thing.name = FromCiceroEditVisitor.getAttribute(tag.attributes, 'id').value; - const valueText = decodeURIComponent(FromCiceroEditVisitor.getAttribute(tag.attributes, 'value').value); - const valueNode = parameters.serializer.fromJSON({ - $class: `${CommonMarkModel.NAMESPACE}.Text`, - text: valueText, - }); - thing.nodes = [valueNode]; - const whenTrueText = decodeURIComponent(FromCiceroEditVisitor.getAttribute(tag.attributes, 'whenTrue').value); - const whenTrueNodes = whenTrueText ? [parameters.serializer.fromJSON({ - $class: `${CommonMarkModel.NAMESPACE}.Text`, - text: whenTrueText, - })] : []; - thing.isTrue = valueText === whenTrueText; - thing.whenTrue = whenTrueNodes; - const whenFalseText = decodeURIComponent(FromCiceroEditVisitor.getAttribute(tag.attributes, 'whenFalse').value); - const whenFalseNodes = whenFalseText ? [parameters.serializer.fromJSON({ - $class: `${CommonMarkModel.NAMESPACE}.Text`, - text: whenFalseText, - })] : []; - thing.whenFalse = whenFalseNodes; - delete thing.tag; - delete thing.text; - } - } - if (thing.tag && thing.tag.tagName === 'computed' && thing.tag.attributes.length === 1) { - const tag = thing.tag; - const ciceroMarkTag = `${CiceroMarkModel.NAMESPACE}.Formula`; - if (FromCiceroEditVisitor.getAttribute(tag.attributes, 'value')) { - thing.$classDeclaration = parameters.modelManager.getType(ciceroMarkTag); - thing.name = ''; // XXX Hack -- since there is no name in CiceroEdit -- will be filled in later - thing.value = decodeURIComponent(FromCiceroEditVisitor.getAttribute(tag.attributes, 'value').value); - delete thing.tag; - delete thing.text; - } - } - } - break; - default: - FromCiceroEditVisitor.visitChildren(this, thing, parameters); - } - } - - /** - * Find an attribute from its name - * @param {*} attributes - the array of attributes - * @param {string} name - the name of the attributes - * @return {*} the attribute or undefined - */ - static getAttribute(attributes, name) { - const atts = attributes.filter(x => x.name === name); - return atts.length === 0 ? null : atts[0]; - } - -} - -module.exports = FromCiceroEditVisitor; \ No newline at end of file diff --git a/packages/markdown-cicero/lib/ToCiceroMarkUnwrappedVisitor.js b/packages/markdown-cicero/lib/ToCiceroMarkUnwrappedVisitor.js deleted file mode 100644 index 118d7ac5..00000000 --- a/packages/markdown-cicero/lib/ToCiceroMarkUnwrappedVisitor.js +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -const { CommonMarkModel } = require('@accordproject/markdown-common'); - -/** - * Utility: flattening array of arrays - * @param {*[]} arr - input array of arrays - * @return {*[]} flattened array - */ -function flatten(arr) { - return arr.reduce((acc, val) => acc.concat(val), []); -} - -/** - * Converts a CiceroMark DOM to a CiceroMark unwrapped DOM - */ -class ToCiceroMarkUnwrappedVisitor { - /** - * Construct the visitor - */ - constructor() { - } - - /** - * Visits a sub-tree and return the CommonMark DOM - * @param {*} visitor the visitor to use - * @param {*} thing the node to visit - * @param {*} [parameters] optional parameters - */ - static visitChildren(visitor, thing, parameters) { - if(thing.nodes) { - const result = - thing.nodes.map(node => { - return node.accept(visitor, parameters); - }); - thing.nodes = flatten(result); - } - } - - /** - * Visit a node - * @param {*} thing the object being visited - * @param {*} parameters the parameters - * @return {*[]} result nodes - */ - visit(thing, parameters) { - const thingType = thing.getType(); - switch(thingType) { - case 'ListBlock': { - ToCiceroMarkUnwrappedVisitor.visitChildren(this, thing, parameters); - - const ciceroMarkTag = `${CommonMarkModel.NAMESPACE}.List`; - thing.$classDeclaration = parameters.modelManager.getType(ciceroMarkTag); - - delete thing.name; - delete thing.elementType; - delete thing.decorators; - } - break; - case 'Variable': - case 'EnumVariable': - case 'FormattedVariable': { - // Revert to HtmlInline - thing.$classDeclaration = parameters.modelManager.getType(`${CommonMarkModel.NAMESPACE}.Text`); - thing.text = decodeURIComponent(thing.value); - - delete thing.elementType; - delete thing.decorators; - delete thing.name; - delete thing.value; - delete thing.format; - delete thing.enumValues; - delete thing.identifiedBy; - } - break; - case 'Conditional': - case 'Optional': { - // Revert to HtmlInline - thing.$classDeclaration = parameters.modelManager.getType(`${CommonMarkModel.NAMESPACE}.Text`); - ToCiceroMarkUnwrappedVisitor.visitChildren(this, thing, parameters); - return thing.nodes; - } - default: - ToCiceroMarkUnwrappedVisitor.visitChildren(this, thing, parameters); - } - return [thing]; - } -} - -module.exports = ToCiceroMarkUnwrappedVisitor; \ No newline at end of file diff --git a/packages/markdown-cicero/lib/ToCommonMarkVisitor.js b/packages/markdown-cicero/lib/ToCommonMarkVisitor.js deleted file mode 100644 index be92d0a9..00000000 --- a/packages/markdown-cicero/lib/ToCommonMarkVisitor.js +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -const { CommonMarkModel } = require('@accordproject/markdown-common'); - -/** - * Utility: flattening array of arrays - * @param {*[]} arr - input array of arrays - * @return {*[]} flattened array - */ -function flatten(arr) { - return arr.reduce((acc, val) => acc.concat(val), []); -} - -/** - * Converts a CiceroMark DOM to a CommonMark DOM - */ -class ToCommonMarkVisitor { - /** - * Construct the visitor - */ - constructor() { - } - - /** - * Visits a sub-tree and return the CommonMark DOM - * @param {*} visitor the visitor to use - * @param {*} thing the node to visit - * @param {*} [parameters] optional parameters - */ - static visitChildren(visitor, thing, parameters) { - if(thing.nodes) { - const result = - thing.nodes.map(node => { - return node.accept(visitor, parameters); - }); - thing.nodes = flatten(result); - } - } - - /** - * Visit a node - * @param {*} thing the object being visited - * @param {*} parameters the parameters - * @return {*[]} result nodes - */ - visit(thing, parameters) { - const thingType = thing.getType(); - switch(thingType) { - case 'Clause': { - ToCommonMarkVisitor.visitChildren(this, thing, parameters); - return thing.nodes; - } - case 'Formula': { - // Revert to HtmlInline - thing.$classDeclaration = parameters.modelManager.getType(`${CommonMarkModel.NAMESPACE}.Text`); - thing.text = decodeURIComponent(thing.value); - - delete thing.elementType; - delete thing.name; - delete thing.value; - delete thing.code; - delete thing.dependencies; - } - break; - default: - ToCommonMarkVisitor.visitChildren(this, thing, parameters); - } - return [thing]; - } -} - -module.exports = ToCommonMarkVisitor; \ No newline at end of file diff --git a/packages/markdown-cicero/lib/ToMarkdownCiceroVisitor.js b/packages/markdown-cicero/lib/ToMarkdownCiceroVisitor.js deleted file mode 100644 index a2d77f9a..00000000 --- a/packages/markdown-cicero/lib/ToMarkdownCiceroVisitor.js +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -const CommonMarkUtils = require('@accordproject/markdown-common').CommonMarkUtils; -const FromCommonMarkVisitor = require('@accordproject/markdown-common').FromCommonMarkVisitor; -const fromcommonmarkrules = require('@accordproject/markdown-common').fromcommonmarkrules; -const fromciceromarkrules = require('./fromciceromarkrules'); - -/** - * Converts a CiceroMark DOM to a cicero markdown string. - */ -class ToMarkdownCiceroVisitor extends FromCommonMarkVisitor { - /** - * Construct the visitor. - * @param {object} [options] configuration options - * @param {*} resultSeq how to sequentially combine results - * @param {object} rules how to process each node type - */ - constructor(options) { - const resultString = (result) => { - return result; - }; - const resultSeq = (parameters,result) => { - result.forEach((next) => { - parameters.result += next; - }); - }; - const setFirst = (thingType) => { - return thingType === 'Item' || thingType === 'Clause' ? true : false; - }; - const rules = fromcommonmarkrules; - Object.assign(rules,fromciceromarkrules); - super(options,resultString,resultSeq,rules,setFirst); - } - - /** - * Converts a CiceroMark DOM to a cicero markdown string. - * @param {*} input - CiceroMark DOM (JSON) - * @returns {string} the cicero markdown string - */ - toMarkdownCicero(input) { - const parameters = {}; - parameters.result = this.resultString(''); - parameters.stack = CommonMarkUtils.blocksInit(); - input.accept(this, parameters); - return parameters.result.trim(); - } -} - -module.exports = ToMarkdownCiceroVisitor; \ No newline at end of file diff --git a/packages/markdown-cicero/lib/UnquoteVariables.js b/packages/markdown-cicero/lib/UnquoteVariables.js deleted file mode 100644 index 8ee37ec7..00000000 --- a/packages/markdown-cicero/lib/UnquoteVariables.js +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -const { Stack, CiceroMarkModel, CommonMarkModel } = require('@accordproject/markdown-common'); - -/** -* Maps the keys in an object -* @param {*} obj input object -* @param {*} stack stack object -* @param {*} options stack object -*/ -function mapObject(obj, stack) { - switch (obj.$class) { - // Strip quotes - case `${CiceroMarkModel.NAMESPACE}.Formula`: - case `${CiceroMarkModel.NAMESPACE}.Variable`: - case `${CiceroMarkModel.NAMESPACE}.FormattedVariable`: - stack.append({ - $class: `${CommonMarkModel.NAMESPACE}.Text`, - text: obj.value.replace(/^"/, '').replace(/"$/, '') - }); - break; - - // remove these, visit children - case `${CommonMarkModel.NAMESPACE}.Document`: - obj.nodes.forEach(element => { - mapObject(element, stack); - }); - break; - // otherwise visit all nodes - default: - if(obj.nodes){ - let resObj = Object.assign({}, obj); - resObj.nodes = []; - stack.push(resObj); - obj.nodes.forEach(element => { - mapObject(element, stack); - }); - stack.pop(); - } else { - stack.append(obj); - } - break; - } -} - -/** -* Replaces variable and formulas with text nodes -* @param {*} input input object -* @param {*} options options object -* @returns {*} the modified object -*/ -function unquoteVariables(input) { - const root = { - $class : `${CommonMarkModel.NAMESPACE}.Document`, - xmlns : input.xmlns, - nodes: [] - }; - const stack = new Stack(); - stack.push(root, false); - mapObject(input, stack); - return root; -} - -module.exports = unquoteVariables; \ No newline at end of file diff --git a/packages/markdown-cicero/lib/fromciceromarkrules.js b/packages/markdown-cicero/lib/fromciceromarkrules.js deleted file mode 100644 index 34bda3c0..00000000 --- a/packages/markdown-cicero/lib/fromciceromarkrules.js +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -const CommonMarkUtils = require('@accordproject/markdown-common').CommonMarkUtils; - -const rules = {}; -// Inlines -rules.Formula = (visitor,thing,children,parameters,resultString,resultSeq) => { - const result = [resultString('{{%'),resultString(thing.value),resultString('%}}')]; - resultSeq(parameters,result); -}; -// Container blocks -rules.Clause = (visitor,thing,children,parameters,resultString,resultSeq) => { - const next1 = CommonMarkUtils.mkPrefix(parameters,2); - const srcAttr = thing.src ? ' src="' + thing.src + '"' : ''; - const next2 = `{{#clause ${thing.name}${srcAttr}}}\n`; - const closeParameters = Object.assign({},parameters); - closeParameters.stack = Object.assign({},parameters.stack); - closeParameters.stack.first = false; - const next3 = CommonMarkUtils.mkPrefix(closeParameters,1); - const next4 = '{{/clause}}'; - const result = [resultString(next1),resultString(next2),children,resultString(next3),resultString(next4)]; - resultSeq(parameters,result); -}; - -module.exports = rules; \ No newline at end of file diff --git a/packages/markdown-cicero/package.json b/packages/markdown-cicero/package.json index adf623ff..eface54f 100644 --- a/packages/markdown-cicero/package.json +++ b/packages/markdown-cicero/package.json @@ -1,6 +1,6 @@ { "name": "@accordproject/markdown-cicero", - "version": "0.16.25", + "version": "1.0.0", "description": "A framework for transforming markdown", "engines": { "node": ">=22", @@ -10,23 +10,23 @@ "access": "public" }, "files": [ - "lib", - "types" + "lib" ], - "main": "index.js", + "main": "lib/index.js", + "types": "lib/index.d.ts", + "typings": "lib/index.d.ts", "scripts": { - "pretest": "npm run lint", - "lint": "eslint .", + "pretest": "npm run lint && npm run build", + "lint": "eslint . --ext .ts", "postlint": "npm run licchk", "licchk": "license-check-and-add", - "test": "jest --timeOut=10000 --silent", + "test": "jest --silent", + "test:noisy": "jest", "test:updateSnapshot": "jest --updateSnapshot --silent", - "test:cov": "npm run lint && jest --timeOut=10000 --coverage --silent", - "jsdoc": "jsdoc -c jsdoc.json package.json", - "build": "npm run build:types", - "build:types": "tsc" + "test:cov": "npm run lint && npm run build && jest --coverage --silent", + "build": "tsc -p tsconfig.json", + "clean": "rimraf lib" }, - "typings": "types/index.d.ts", "repository": { "type": "git", "url": "git+https://github.com/accordproject/markdown-transform.git", @@ -46,11 +46,17 @@ "homepage": "https://github.com/accordproject/markdown-transform", "devDependencies": { "@accordproject/concerto-core": "^4.1.2", + "@types/jest": "^29.5.12", + "@types/markdown-it": "^14.1.2", + "@types/node": "^20.11.30", + "@typescript-eslint/eslint-plugin": "^7.4.0", + "@typescript-eslint/parser": "^7.4.0", "eslint": "8.57.1", "jest": "^29.7.0", "jest-diff": "^29.7.0", - "jsdoc": "4.0.4", "license-check-and-add": "2.3.6", + "rimraf": "^5.0.5", + "ts-jest": "^29.1.2", "typescript": "^5.9.3" }, "peerDependencies": { @@ -64,14 +70,12 @@ "winston": "3.17.0" }, "license-check-and-add-config": { - "folder": "./lib", + "folder": "./src", "license": "header.txt", "exact_paths_method": "EXCLUDE", "exact_paths": [ - "externalModels/.npmignore", - "externalModels/.gitignore", "coverage", - "index.d.ts", + "__snapshots__", "./system", "LICENSE", "node_modules", @@ -89,7 +93,7 @@ ], "insert_license": false, "license_formats": { - "js|njk|pegjs|cto|acl|qry": { + "ts|tsx|js|njk|pegjs|cto|acl|qry": { "prepend": "/*", "append": " */", "eachLine": { @@ -105,28 +109,5 @@ "file": "header.md" } } - }, - "nyc": { - "produce-source-map": "true", - "sourceMap": "inline", - "reporter": [ - "lcov", - "text", - "text-summary", - "html", - "json" - ], - "include": [ - "lib/**/*.js" - ], - "exclude": [ - "scripts/**/*.js" - ], - "all": true, - "check-coverage": true, - "statements": 90, - "branches": 71, - "functions": 88, - "lines": 90 } } diff --git a/packages/markdown-cicero/lib/CiceroEditTransformer.test.js b/packages/markdown-cicero/src/CiceroEditTransformer.test.ts similarity index 67% rename from packages/markdown-cicero/lib/CiceroEditTransformer.test.js rename to packages/markdown-cicero/src/CiceroEditTransformer.test.ts index 5cf16b5e..aa091110 100644 --- a/packages/markdown-cicero/lib/CiceroEditTransformer.test.js +++ b/packages/markdown-cicero/src/CiceroEditTransformer.test.ts @@ -12,50 +12,41 @@ * limitations under the License. */ -// @ts-nocheck -/* eslint-disable no-undef */ -'use strict'; +import * as fs from 'fs'; +import { diff } from 'jest-diff'; +import { CiceroMarkTransformer } from './CiceroMarkTransformer'; -const fs = require('fs'); -const diff = require('jest-diff'); - -const CiceroMarkTransformer = require('./CiceroMarkTransformer'); - -// eslint-disable-next-line no-unused-vars -let ciceroMarkTransformer = null; +let ciceroMarkTransformer: CiceroMarkTransformer; expect.extend({ - toMarkdownRoundtrip(ciceroEditText,markdownText,testName) { + toMarkdownRoundtrip(ciceroEditText: string, markdownText: string, _testName?: string) { const jsonEdit = ciceroMarkTransformer.fromCiceroEdit(ciceroEditText); const jsonEditUnwrapped = ciceroMarkTransformer.toCiceroMarkUnwrapped(jsonEdit); const newMarkdownEdit = ciceroMarkTransformer.toMarkdownCicero(jsonEditUnwrapped); const jsonMark = ciceroMarkTransformer.fromMarkdownCicero(markdownText); - const newMarkdownMark = ciceroMarkTransformer.toMarkdownCicero(jsonMark); const json1 = ciceroMarkTransformer.fromMarkdownCicero(newMarkdownEdit); const json2 = jsonMark; const pass = JSON.stringify(json1) === JSON.stringify(json2); const message = pass ? () => - this.utils.matcherHint(`toMarkdownRoundtrip - ${newMarkdownEdit} <-> ${newMarkdownMark}`, undefined, undefined, undefined) + - '\n\n' + - `Expected: ${this.utils.printExpected(json1)}\n` + - `Received: ${this.utils.printReceived(json2)}` + this.utils.matcherHint(`toMarkdownRoundtrip - ${newMarkdownEdit} <-> ${markdownText}`, undefined, undefined, undefined) + + '\n\n' + + `Expected: ${this.utils.printExpected(json1)}\n` + + `Received: ${this.utils.printReceived(json2)}` : () => { - const diffString = diff(json1, json2, { - expand: true, - }); + const diffString = diff(json1, json2, { expand: true }); return ( - this.utils.matcherHint(`toMarkdownRoundtrip - ${JSON.stringify(newMarkdownEdit)} -> ${JSON.stringify(newMarkdownMark)}`, undefined, undefined, undefined) + - '\n\n' + - (diffString && diffString.includes('- Expect') - ? `Difference:\n\n${diffString}` - : `Expected: ${this.utils.printExpected(json1)}\n` + - `Received: ${this.utils.printReceived(json2)}`) + this.utils.matcherHint(`toMarkdownRoundtrip - ${JSON.stringify(newMarkdownEdit)}`, undefined, undefined, undefined) + + '\n\n' + + (diffString && diffString.includes('- Expect') + ? `Difference:\n\n${diffString}` + : `Expected: ${this.utils.printExpected(json1)}\n` + + `Received: ${this.utils.printReceived(json2)}`) ); }; - return {actual: ciceroEditText, message, pass}; + return { actual: ciceroEditText, message, pass }; }, }); @@ -63,18 +54,14 @@ beforeAll(() => { ciceroMarkTransformer = new CiceroMarkTransformer(); }); -/** - * Get the name and contents of all markdown test files - * @returns {*} an array of name/contents tuples - */ -function getMarkdownFiles() { - const result = []; +function getMarkdownFiles(): [string, string, string][] { + const result: [string, string, string][] = []; const files = fs.readdirSync(__dirname + '/../test/data/ciceroedit'); - files.forEach(function(file) { - if(file.endsWith('.md')) { - let contentsEdit = fs.readFileSync(__dirname + '/../test/data/ciceroedit/' + file, 'utf8'); - let contentsMark = fs.readFileSync(__dirname + '/../test/data/ciceromark/' + file, 'utf8'); + files.forEach(function (file) { + if (file.endsWith('.md')) { + const contentsEdit = fs.readFileSync(__dirname + '/../test/data/ciceroedit/' + file, 'utf8'); + const contentsMark = fs.readFileSync(__dirname + '/../test/data/ciceromark/' + file, 'utf8'); result.push([file, contentsEdit, contentsMark]); } }); @@ -83,14 +70,14 @@ function getMarkdownFiles() { } describe('markdown', () => { - getMarkdownFiles().forEach( ([file, ciceroEditText, markdownText]) => { + getMarkdownFiles().forEach(([file, ciceroEditText, markdownText]) => { it(`converts ${file} to ciceromark`, () => { const json = ciceroMarkTransformer.fromCiceroEdit(ciceroEditText); expect(json).toMatchSnapshot(); }); it(`roundtrips ${file}`, () => { - expect(ciceroEditText).toMarkdownRoundtrip(markdownText,file); + expect(ciceroEditText).toMarkdownRoundtrip(markdownText, file); }); }); }); @@ -99,7 +86,6 @@ describe('acceptance', () => { it('converts acceptance to CommonMark DOM', () => { const markdownText = fs.readFileSync(__dirname + '/../test/data/ciceroedit/acceptance.md', 'utf8'); const json = ciceroMarkTransformer.fromCiceroEdit(markdownText); - // console.log(JSON.stringify(json, null, 4)); expect(json).toMatchSnapshot(); const jsonUnwrapped = ciceroMarkTransformer.toCiceroMarkUnwrapped(json); const newMarkdown = ciceroMarkTransformer.toMarkdownCicero(jsonUnwrapped); @@ -109,16 +95,14 @@ describe('acceptance', () => { it('converts acceptance to markdown string (unquoted)', () => { const markdownText = fs.readFileSync(__dirname + '/../test/data/ciceroedit/acceptance.md', 'utf8'); const json = ciceroMarkTransformer.fromCiceroEdit(markdownText); - // console.log(JSON.stringify(json, null, 4)); expect(json).toMatchSnapshot(); - const newMarkdown = ciceroMarkTransformer.toMarkdown(json,{unquoteVariables:true}); + const newMarkdown = ciceroMarkTransformer.toMarkdown(json, { unquoteVariables: true }); expect(newMarkdown).toMatchSnapshot(); }); it('converts acceptance-formula to markdown string', () => { const markdownText = fs.readFileSync(__dirname + '/../test/data/ciceroedit/acceptance-formula.md', 'utf8'); const json = ciceroMarkTransformer.fromCiceroEdit(markdownText); - // console.log(JSON.stringify(json, null, 4)); expect(json).toMatchSnapshot(); const newMarkdown = ciceroMarkTransformer.toMarkdown(json); expect(newMarkdown).toMatchSnapshot(); @@ -127,19 +111,16 @@ describe('acceptance', () => { it('converts acceptance-formula to markdown string (unquoted)', () => { const markdownText = fs.readFileSync(__dirname + '/../test/data/ciceroedit/acceptance-formula.md', 'utf8'); const json = ciceroMarkTransformer.fromCiceroEdit(markdownText); - // console.log(JSON.stringify(json, null, 4)); expect(json).toMatchSnapshot(); - const newMarkdown = ciceroMarkTransformer.toMarkdown(json,{unquoteVariables:true}); + const newMarkdown = ciceroMarkTransformer.toMarkdown(json, { unquoteVariables: true }); expect(newMarkdown).toMatchSnapshot(); }); it('converts acceptance-notclause2 to markdown string', () => { const markdownText = fs.readFileSync(__dirname + '/../test/data/ciceroedit/acceptance-notclause2.md', 'utf8'); const json = ciceroMarkTransformer.fromCiceroEdit(markdownText); - // console.log(JSON.stringify(json, null, 4)); expect(json).toMatchSnapshot(); - const newMarkdown = ciceroMarkTransformer.toMarkdown(json,{unquoteVariables:true}); + const newMarkdown = ciceroMarkTransformer.toMarkdown(json, { unquoteVariables: true }); expect(newMarkdown).toMatchSnapshot(); }); - }); diff --git a/packages/markdown-cicero/lib/CiceroMarkTransformer.test.js b/packages/markdown-cicero/src/CiceroMarkTransformer.test.ts similarity index 73% rename from packages/markdown-cicero/lib/CiceroMarkTransformer.test.js rename to packages/markdown-cicero/src/CiceroMarkTransformer.test.ts index 673bc8e4..46d5c8a2 100644 --- a/packages/markdown-cicero/lib/CiceroMarkTransformer.test.js +++ b/packages/markdown-cicero/src/CiceroMarkTransformer.test.ts @@ -12,20 +12,14 @@ * limitations under the License. */ -// @ts-nocheck -/* eslint-disable no-undef */ -'use strict'; +import * as fs from 'fs'; +import { diff } from 'jest-diff'; +import { CiceroMarkTransformer } from './CiceroMarkTransformer'; -const fs = require('fs'); -const diff = require('jest-diff'); - -const CiceroMarkTransformer = require('./CiceroMarkTransformer'); - -// eslint-disable-next-line no-unused-vars -let ciceroMarkTransformer = null; +let ciceroMarkTransformer: CiceroMarkTransformer; expect.extend({ - toMarkdownRoundtrip(markdownText) { + toMarkdownRoundtrip(markdownText: string) { const json1 = ciceroMarkTransformer.fromMarkdownCicero(markdownText); const newMarkdown = ciceroMarkTransformer.toMarkdownCicero(json1); const json2 = ciceroMarkTransformer.fromMarkdownCicero(newMarkdown); @@ -34,24 +28,22 @@ expect.extend({ const message = pass ? () => this.utils.matcherHint(`toMarkdownRoundtrip - ${markdownText} -> ${newMarkdown}`, undefined, undefined, undefined) + - '\n\n' + - `Expected: ${this.utils.printExpected(json1)}\n` + - `Received: ${this.utils.printReceived(json2)}` + '\n\n' + + `Expected: ${this.utils.printExpected(json1)}\n` + + `Received: ${this.utils.printReceived(json2)}` : () => { - const diffString = diff(json1, json2, { - expand: true, - }); + const diffString = diff(json1, json2, { expand: true }); return ( this.utils.matcherHint(`toMarkdownRoundtrip - ${JSON.stringify(markdownText)} -> ${JSON.stringify(newMarkdown)}`, undefined, undefined, undefined) + - '\n\n' + - (diffString && diffString.includes('- Expect') - ? `Difference:\n\n${diffString}` - : `Expected: ${this.utils.printExpected(json1)}\n` + - `Received: ${this.utils.printReceived(json2)}`) + '\n\n' + + (diffString && diffString.includes('- Expect') + ? `Difference:\n\n${diffString}` + : `Expected: ${this.utils.printExpected(json1)}\n` + + `Received: ${this.utils.printReceived(json2)}`) ); }; - return {actual: markdownText, message, pass}; + return { actual: markdownText, message, pass }; }, }); @@ -61,15 +53,14 @@ beforeAll(() => { /** * Get the name and contents of all markdown test files - * @returns {*} an array of name/contents tuples */ -function getMarkdownFiles() { - const result = []; +function getMarkdownFiles(): [string, string][] { + const result: [string, string][] = []; const files = fs.readdirSync(__dirname + '/../test/data/ciceromark'); - files.forEach(function(file) { - if(file.endsWith('.md')) { - let contents = fs.readFileSync(__dirname + '/../test/data/ciceromark/' + file, 'utf8'); + files.forEach(function (file) { + if (file.endsWith('.md')) { + const contents = fs.readFileSync(__dirname + '/../test/data/ciceromark/' + file, 'utf8'); result.push([file, contents]); } }); @@ -82,9 +73,9 @@ describe('markdown', () => { expect(ciceroMarkTransformer.getSerializer()).toBeTruthy(); }); - getMarkdownFiles().forEach( ([file, markdownText]) => { + getMarkdownFiles().forEach(([file, markdownText]) => { it(`converts ${file} to concerto`, () => { - const json = ciceroMarkTransformer.fromMarkdownCicero(markdownText, 'json'); + const json = ciceroMarkTransformer.fromMarkdownCicero(markdownText); expect(json).toMatchSnapshot(); }); @@ -98,7 +89,6 @@ describe('acceptance', () => { it('converts acceptance to markdown string', () => { const markdownText = fs.readFileSync(__dirname + '/../test/data/ciceromark/acceptance.md', 'utf8'); const json = ciceroMarkTransformer.fromMarkdownCicero(markdownText); - // console.log(JSON.stringify(json, null, 4)); expect(json).toMatchSnapshot(); const newMarkdown = ciceroMarkTransformer.toMarkdown(json); expect(newMarkdown).toMatchSnapshot(); @@ -107,25 +97,22 @@ describe('acceptance', () => { it('converts acceptance to markdown string (unquoted)', () => { const markdownText = fs.readFileSync(__dirname + '/../test/data/ciceromark/acceptance.md', 'utf8'); const json = ciceroMarkTransformer.fromMarkdownCicero(markdownText); - // console.log(JSON.stringify(json, null, 4)); expect(json).toMatchSnapshot(); - const newMarkdown = ciceroMarkTransformer.toMarkdown(json,{unquoteVariables:true}); + const newMarkdown = ciceroMarkTransformer.toMarkdown(json, { unquoteVariables: true }); expect(newMarkdown).toMatchSnapshot(); }); it('converts acceptance to markdown string (plaintext)', () => { const markdownText = fs.readFileSync(__dirname + '/../test/data/ciceromark/acceptance.md', 'utf8'); const json = ciceroMarkTransformer.fromMarkdownCicero(markdownText); - // console.log(JSON.stringify(json, null, 4)); expect(json).toMatchSnapshot(); - const newMarkdown = ciceroMarkTransformer.toMarkdown(json,{removeFormatting:true}); + const newMarkdown = ciceroMarkTransformer.toMarkdown(json, { removeFormatting: true }); expect(newMarkdown).toMatchSnapshot(); }); it('converts acceptance to cicero markdown string', () => { const markdownText = fs.readFileSync(__dirname + '/../test/data/ciceromark/acceptance.md', 'utf8'); const json = ciceroMarkTransformer.fromMarkdownCicero(markdownText); - // console.log(JSON.stringify(json, null, 4)); expect(json).toMatchSnapshot(); const newMarkdown = ciceroMarkTransformer.toMarkdownCicero(json); expect(newMarkdown).toMatchSnapshot(); @@ -134,11 +121,9 @@ describe('acceptance', () => { it('converts acceptance clause content to markdown string (getClauseText)', () => { const markdownText = fs.readFileSync(__dirname + '/../test/data/ciceromark/acceptance.md', 'utf8'); const json = ciceroMarkTransformer.fromMarkdownCicero(markdownText); - // console.log(JSON.stringify(json, null, 4)); expect(json).toMatchSnapshot(); const clauseText = ciceroMarkTransformer.getClauseText(json.nodes[2]); expect(clauseText).toMatchSnapshot(); expect((() => ciceroMarkTransformer.getClauseText(json.nodes[1]))).toThrow('Cannot apply getClauseText to non-clause node'); }); - }); diff --git a/packages/markdown-cicero/src/CiceroMarkTransformer.ts b/packages/markdown-cicero/src/CiceroMarkTransformer.ts new file mode 100644 index 00000000..40b3beeb --- /dev/null +++ b/packages/markdown-cicero/src/CiceroMarkTransformer.ts @@ -0,0 +1,200 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ModelManager, Factory, Serializer } from '@accordproject/concerto-core'; +import MarkdownIt from 'markdown-it'; +import MarkdownItCicero = require('@accordproject/markdown-it-cicero'); +import { + FromMarkdownIt, + CommonMarkTransformer, + CommonMarkModel, + CiceroMarkModel, + ConcertoMetaModel, +} from '@accordproject/markdown-common'; + +import cicerorules from './cicerorules'; +import { ToMarkdownCiceroVisitor } from './ToMarkdownCiceroVisitor'; +import { ToCiceroMarkUnwrappedVisitor } from './ToCiceroMarkUnwrappedVisitor'; +import { FromCiceroEditVisitor } from './FromCiceroEditVisitor'; +import { ToCommonMarkVisitor } from './ToCommonMarkVisitor'; +import { unquoteVariables } from './UnquoteVariables'; + +/** + * Converts a CiceroMark DOM to/from a CommonMark DOM, or a markdown string. + */ +export class CiceroMarkTransformer { + commonMark: CommonMarkTransformer; + modelManager: ModelManager; + serializer: Serializer; + + constructor() { + this.commonMark = new CommonMarkTransformer(); + + this.modelManager = new ModelManager(); + this.modelManager.addCTOModel(ConcertoMetaModel.MODEL, 'metamodel.cto'); + this.modelManager.addCTOModel(CommonMarkModel.MODEL, 'commonmark.cto'); + this.modelManager.addCTOModel(CiceroMarkModel.MODEL, 'ciceromark.cto'); + const factory = new Factory(this.modelManager); + this.serializer = new Serializer(factory, this.modelManager); + } + + /** + * Obtain the Clause text for a Clause node + */ + getClauseText(input: any): string { + if (input.$class === `${CiceroMarkModel.NAMESPACE}.Clause`) { + const docInput = { + $class: `${CommonMarkModel.NAMESPACE}.Document`, + xmlns: 'http://commonmark.org/xml/1.0', + nodes: input.nodes, + }; + return this.toMarkdownCicero(docInput); + } else { + throw new Error('Cannot apply getClauseText to non-clause node'); + } + } + + /** + * Retrieve the serializer used by the parser + */ + getSerializer(): Serializer { + return this.serializer; + } + + /** + * Converts a CiceroEdit string to a CiceroMark DOM + */ + fromCiceroEdit(input: string): any { + const commonMark = this.commonMark.fromMarkdown(input); + const dom = this.serializer.fromJSON(commonMark); + + const parameters = { + ciceroMark: this, + commonMark: this.commonMark, + modelManager: this.modelManager, + serializer: this.serializer, + }; + const visitor = new FromCiceroEditVisitor(); + dom.accept(visitor, parameters); + return this.serializer.toJSON(dom); + } + + /** + * Converts a CiceroMark DOM to a CiceroMark Unwrapped DOM + */ + toCiceroMarkUnwrapped(input: any, options?: { unquoteVariables?: boolean }): any { + if (options && Object.prototype.hasOwnProperty.call(options, 'unquoteVariables') && options.unquoteVariables) { + input = this.unquote(input); + } + + const dom = this.serializer.fromJSON(input); + + const visitor = new ToCiceroMarkUnwrappedVisitor(); + dom.accept(visitor, { + modelManager: this.modelManager, + }); + + return this.serializer.toJSON(dom); + } + + /** + * Converts a CommonMark DOM to a CiceroMark DOM + */ + fromCommonMark(input: any): any { + return input; + } + + /** + * Converts a markdown string to a CiceroMark DOM + */ + fromMarkdown(markdown: string): any { + const commonMarkDom = this.commonMark.fromMarkdown(markdown); + return this.fromCommonMark(commonMarkDom); + } + + /** + * Converts a CiceroMark DOM to a markdown string + */ + toMarkdown(input: any, options?: any): string { + const commonMarkDom = this.toCommonMark(input, options); + return this.commonMark.toMarkdown(commonMarkDom); + } + + /** + * Converts a cicero markdown string to a CiceroMark DOM + */ + fromMarkdownCicero(markdown: string, _options?: any): any { + const tokens = this.toTokens(markdown); + return this.fromTokens(tokens); + } + + /** + * Converts a CiceroMark DOM to a cicero markdown string + */ + toMarkdownCicero(input: any): string { + const visitor = new ToMarkdownCiceroVisitor(); + return visitor.toMarkdownCicero(this.serializer.fromJSON(input)); + } + + /** + * Converts a CiceroMark DOM to a CommonMark DOM + */ + toCommonMark(input: any, options?: { removeFormatting?: boolean; unquoteVariables?: boolean }): any { + const json = this.toCiceroMarkUnwrapped(input, options); + const dom = this.serializer.fromJSON(json); + + const visitor = new ToCommonMarkVisitor(options); + dom.accept(visitor, { + commonMark: this.commonMark, + modelManager: this.modelManager, + serializer: this.serializer, + }); + + let result = this.serializer.toJSON(dom); + if (options && options.removeFormatting) { + const cmt = new CommonMarkTransformer(); + result = cmt.removeFormatting(result); + } + + return result; + } + + /** + * Unquotes a CiceroMark DOM + */ + unquote(input: any): any { + return unquoteVariables(input); + } + + /** + * Converts a ciceromark string into a token stream + */ + toTokens(input: string): any[] { + const parser = new MarkdownIt({ html: true }).use(MarkdownItCicero); + return parser.parse(input, {}); + } + + /** + * Converts a token stream into a CiceroMark DOM object. + */ + fromTokens(tokenStream: any[]): any { + const fromMarkdownIt = new FromMarkdownIt(cicerorules); + const json = fromMarkdownIt.toCommonMark(tokenStream); + + const validJson = this.serializer.fromJSON(json); + return this.serializer.toJSON(validJson); + } +} + +export default CiceroMarkTransformer; diff --git a/packages/markdown-cicero/lib/CommonMarkSpec.test.js b/packages/markdown-cicero/src/CommonMarkSpec.test.ts similarity index 53% rename from packages/markdown-cicero/lib/CommonMarkSpec.test.js rename to packages/markdown-cicero/src/CommonMarkSpec.test.ts index 5dcb3ba2..e5828aa6 100644 --- a/packages/markdown-cicero/lib/CommonMarkSpec.test.js +++ b/packages/markdown-cicero/src/CommonMarkSpec.test.ts @@ -12,45 +12,38 @@ * limitations under the License. */ -// @ts-nocheck -/* eslint-disable no-undef */ -'use strict'; +import * as fs from 'fs'; +import { diff } from 'jest-diff'; +import { CiceroMarkTransformer } from './CiceroMarkTransformer'; -const fs = require('fs'); - -const CiceroMarkTransformer = require('./CiceroMarkTransformer'); - -// eslint-disable-next-line no-unused-vars -let ciceroMarkTransformer = null; +let ciceroMarkTransformer: CiceroMarkTransformer; expect.extend({ - toMarkdownRoundtrip(markdownText) { - const json1 = ciceroMarkTransformer.fromMarkdown(markdownText, 'json', {ciceroEdit:true}); + toMarkdownRoundtrip(markdownText: string) { + const json1 = ciceroMarkTransformer.fromMarkdown(markdownText); const newMarkdown = ciceroMarkTransformer.toMarkdown(json1); - const json2 = ciceroMarkTransformer.fromMarkdown(newMarkdown, 'json', {ciceroEdit:true}); + const json2 = ciceroMarkTransformer.fromMarkdown(newMarkdown); const pass = JSON.stringify(json1) === JSON.stringify(json2); const message = pass ? () => this.utils.matcherHint(`toMarkdownRoundtrip - ${markdownText} -> ${newMarkdown}`, undefined, undefined, undefined) + - '\n\n' + - `Expected: ${this.utils.printExpected(json1)}\n` + - `Received: ${this.utils.printReceived(json2)}` + '\n\n' + + `Expected: ${this.utils.printExpected(json1)}\n` + + `Received: ${this.utils.printReceived(json2)}` : () => { - const diffString = diff(json1, json2, { - expand: true, - }); + const diffString = diff(json1, json2, { expand: true }); return ( this.utils.matcherHint(`toMarkdownRoundtrip - ${JSON.stringify(markdownText)} -> ${JSON.stringify(newMarkdown)}`, undefined, undefined, undefined) + - '\n\n' + - (diffString && diffString.includes('- Expect') - ? `Difference:\n\n${diffString}` - : `Expected: ${this.utils.printExpected(json1)}\n` + - `Received: ${this.utils.printReceived(json2)}`) + '\n\n' + + (diffString && diffString.includes('- Expect') + ? `Difference:\n\n${diffString}` + : `Expected: ${this.utils.printExpected(json1)}\n` + + `Received: ${this.utils.printReceived(json2)}`) ); }; - return {actual: markdownText, message, pass}; + return { actual: markdownText, message, pass }; }, }); @@ -58,44 +51,39 @@ beforeAll(() => { ciceroMarkTransformer = new CiceroMarkTransformer(); }); -/** - * Extracts all the test md snippets from a commonmark spec file - * @param {string} testfile the file to use - * @return {*} the examples - */ -function extractSpecTests(testfile) { - let data = fs.readFileSync(testfile, 'utf8'); - let examples = []; +interface SpecExample { + markdown: string; + html: string; + section: string; + number: number; +} + +function extractSpecTests(testfile: string): SpecExample[] { + const data = fs.readFileSync(testfile, 'utf8'); + const examples: SpecExample[] = []; let current_section = ''; let example_number = 0; - let tests = data - .replace(/\r\n?/g, '\n') // Normalize newlines for platform independence + const tests = data + .replace(/\r\n?/g, '\n') .replace(/^(.|[\n])*/m, ''); tests.replace(/^`{32} example\n([\s\S]*?)^\.\n([\s\S]*?)^`{32}$|^#{1,6} *(.*)$/gm, - function(_, markdownSubmatch, htmlSubmatch, sectionSubmatch){ + function (_, markdownSubmatch, htmlSubmatch, sectionSubmatch) { if (sectionSubmatch) { current_section = sectionSubmatch; } else { example_number++; - examples.push({markdown: markdownSubmatch, - html: htmlSubmatch, - section: current_section, - number: example_number}); + examples.push({ markdown: markdownSubmatch, html: htmlSubmatch, section: current_section, number: example_number }); } + return ''; }); return examples; } -/** - * Get the name and contents of all markdown snippets - * used in a commonmark spec file - * @returns {*} an array of name/contents tuples - */ -function getMarkdownSpecFiles() { - const result = []; +function getMarkdownSpecFiles(): [string, string][] { + const result: [string, string][] = []; const specExamples = extractSpecTests(__dirname + '/../test/data/spec.txt'); - specExamples.forEach(function(example) { + specExamples.forEach(function (example) { result.push([`${example.section}-${example.number}`, example.markdown]); }); @@ -103,14 +91,12 @@ function getMarkdownSpecFiles() { } describe('markdown-spec', () => { - getMarkdownSpecFiles().forEach( ([file, markdownText]) => { + getMarkdownSpecFiles().forEach(([file, markdownText]) => { it(`converts ${file} to concerto`, () => { - const json = ciceroMarkTransformer.fromMarkdown(markdownText, 'json', {ciceroEdit:true}); + const json = ciceroMarkTransformer.fromMarkdown(markdownText); expect(json).toMatchSnapshot(); }); - // currently skipped because not all examples roundtrip - // needs more investigation!! it.skip(`roundtrips ${file}`, () => { expect(markdownText).toMarkdownRoundtrip(); }); diff --git a/packages/markdown-cicero/src/Decorators.test.ts b/packages/markdown-cicero/src/Decorators.test.ts new file mode 100644 index 00000000..e798b6f7 --- /dev/null +++ b/packages/markdown-cicero/src/Decorators.test.ts @@ -0,0 +1,76 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as fs from 'fs'; +import { Decorators } from './Decorators'; + +function loadNode(name: string): any { + return JSON.parse(fs.readFileSync(__dirname + `/../test/data/decorators/${name}`, 'utf-8')); +} + +describe('decorators', () => { + it('handles string decorator', () => { + const decorators = new Decorators(loadNode('string.json')); + expect(decorators.getDecoratorValue('Test', 'type')).toBe('value'); + }); + + it('handles boolean decorator', () => { + const decorators = new Decorators(loadNode('boolean.json')); + expect(decorators.getDecoratorValue('Test', 'type')).toBe(true); + }); + + it('handles number decorator', () => { + const decorators = new Decorators(loadNode('number.json')); + expect(decorators.getDecoratorValue('Test', 'type')).toBe(3.14); + }); + + it('handles identifier decorator', () => { + const decorators = new Decorators(loadNode('identifier.json')); + expect(decorators.getDecoratorValue('Test', 'type')).toBe('typeIdentifier'); + }); + + it('handles getting a value for a missing decorator', () => { + const decorators = new Decorators(loadNode('string.json')); + expect(decorators.getDecoratorValue('Missing', 'type')).toBe(null); + }); + + it('handles getting a missing value for a decorator', () => { + const decorators = new Decorators(loadNode('string.json')); + expect(decorators.getDecoratorValue('Test', 'missing')).toBe(undefined); + }); + + it('handles mutiple args decorator', () => { + const decorators = new Decorators(loadNode('multi.json')); + expect(decorators.getDecoratorValue('Test', 'type')).toBe('value'); + expect(decorators.getDecoratorValue('Test', 'second')).toBe('value2'); + }); + + it('hasDecorator', () => { + const decorators = new Decorators(loadNode('multi.json')); + expect(decorators.hasDecorator('Test')).toBe(true); + }); + + it('getArguments', () => { + const decorators = new Decorators(loadNode('multi.json')); + expect(decorators.getArguments('Test')).toStrictEqual({ second: 'value2', type: 'value' }); + }); + + it('fails with odd number of arguments', () => { + expect(() => new Decorators(loadNode('invalid-odd.json'))).toThrow('Arguments must be [name, value] pairs'); + }); + + it('fails with argument names that are not strings', () => { + expect(() => new Decorators(loadNode('invalid-arg-name.json'))).toThrow('Argument names must be strings'); + }); +}); diff --git a/packages/markdown-cicero/lib/Decorators.js b/packages/markdown-cicero/src/Decorators.ts similarity index 59% rename from packages/markdown-cicero/lib/Decorators.js rename to packages/markdown-cicero/src/Decorators.ts index 6d64b45e..5d003102 100644 --- a/packages/markdown-cicero/lib/Decorators.js +++ b/packages/markdown-cicero/src/Decorators.ts @@ -12,35 +12,34 @@ * limitations under the License. */ -'use strict'; - -const { ConcertoMetaModel } = require('@accordproject/markdown-common'); +import { ConcertoMetaModel } from '@accordproject/markdown-common'; /** * A class to retrieve decorators on CiceroMark nodes */ -class Decorators { +export class Decorators { + data: Record>; + /** * Construct an instance, based on a CiceroMark node * Note that decorator arguments must be specified as an * array of [name (string),value] pairs, even though this is * not enforced by the Concerto grammar. - * @param {object} node the CiceroMark node */ - constructor(node) { + constructor(node: any) { this.data = {}; - if(node.decorators) { - node.decorators.forEach( (d) => { - if(d.arguments.length % 2 !==0) { + if (node.decorators) { + node.decorators.forEach((d: any) => { + if (d.arguments.length % 2 !== 0) { throw new Error('Arguments must be [name, value] pairs'); } - const args = {}; - for( let n=0; n < d.arguments.length-1; n=n+2) { + const args: Record = {}; + for (let n = 0; n < d.arguments.length - 1; n = n + 2) { const arg = d.arguments[n]; - if(arg.$class && arg.$class !== `${ConcertoMetaModel.NAMESPACE}.DecoratorString`) { + if (arg.$class && arg.$class !== `${ConcertoMetaModel.NAMESPACE}.DecoratorString`) { throw new Error(`Argument names must be strings. Found ${arg.$class}`); } - const argValue = d.arguments[n+1]; + const argValue = d.arguments[n + 1]; args[arg.value] = argValue.$class === `${ConcertoMetaModel.NAMESPACE}.DecoratorIdentifier` ? argValue.identifier : argValue.value; } this.data[d.name] = args; @@ -50,36 +49,28 @@ class Decorators { /** * Returns true is the decorator is present - * @param {string} decoratorName the name of the decorator - * @returns {boolean} true is the decorator is present */ - hasDecorator(decoratorName) { + hasDecorator(decoratorName: string): boolean { return !!this.data[decoratorName]; } /** * Get the arguments for a named decorator - * @param {string} decoratorName the name of the decorator - * @returns {array} an array of arguments, or null */ - getArguments(decoratorName) { + getArguments(decoratorName: string): Record | undefined { return this.data[decoratorName]; } /** * Get the arguments for a named decorator - * @param {string} decoratorName the name of the decorator - * @param {string} argumentName the name of the decorator argument - * @returns {object} the value of the argument or null if the decorator - * is missing or undefined if the argument is missing */ - getDecoratorValue(decoratorName, argumentName) { + getDecoratorValue(decoratorName: string, argumentName: string): any { const args = this.getArguments(decoratorName); - if(args) { + if (args) { return args[argumentName]; } return null; } } -module.exports = Decorators; \ No newline at end of file +export default Decorators; diff --git a/packages/markdown-cicero/src/FromCiceroEditVisitor.ts b/packages/markdown-cicero/src/FromCiceroEditVisitor.ts new file mode 100644 index 00000000..46d2ccc7 --- /dev/null +++ b/packages/markdown-cicero/src/FromCiceroEditVisitor.ts @@ -0,0 +1,168 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { CiceroMarkModel, CommonMarkModel } from '@accordproject/markdown-common'; + +/** + * Converts a CommonMark DOM to a CiceroMark DOM + */ +export class FromCiceroEditVisitor { + /** + * Visits a sub-tree and return CiceroMark DOM + */ + static visitChildren(visitor: FromCiceroEditVisitor, thing: any, parameters: any): void { + if (thing.nodes) { + FromCiceroEditVisitor.visitNodes(visitor, thing.nodes, parameters); + } + } + + /** + * Visits a list of nodes and return the CiceroMark DOM + */ + static visitNodes(visitor: FromCiceroEditVisitor, things: any[], parameters: any): void { + things.forEach((node) => { + node.accept(visitor, parameters); + }); + } + + /** + * Visit a node + */ + visit(thing: any, parameters: any): void { + switch (thing.getType()) { + case 'CodeBlock': { + const tag = thing.tag; + if (tag && tag.tagName === 'clause' && tag.attributes.length === 2) { + const ciceroMarkTag = `${CiceroMarkModel.NAMESPACE}.Clause`; + const clauseText = thing.text; + + if (FromCiceroEditVisitor.getAttribute(tag.attributes, 'src') && + FromCiceroEditVisitor.getAttribute(tag.attributes, 'clauseid')) { + thing.$classDeclaration = parameters.modelManager.getType(ciceroMarkTag); + thing.src = FromCiceroEditVisitor.getAttribute(tag.attributes, 'src').value; + thing.name = FromCiceroEditVisitor.getAttribute(tag.attributes, 'clauseid').value; + + const commonMark = parameters.commonMark.fromMarkdown(clauseText); + thing.nodes = parameters.serializer.fromJSON(commonMark).nodes; + FromCiceroEditVisitor.visitNodes(this, thing.nodes, parameters); + + thing.text = null; + delete thing.tag; + delete thing.info; + } + } else if (tag && tag.tagName === 'list' && tag.attributes.length === 0) { + const ciceroMarkTag = `${CiceroMarkModel.NAMESPACE}.ListBlock`; + const listText = thing.text; + + const commonMark = parameters.commonMark.fromMarkdown(listText); + const newNodes = parameters.serializer.fromJSON(commonMark).nodes; + if (newNodes.length === 1 && newNodes[0].getType() === 'List') { + const listNode = newNodes[0]; + thing.$classDeclaration = parameters.modelManager.getType(ciceroMarkTag); + thing.name = ''; + thing.type = listNode.type; + thing.start = listNode.start; + thing.tight = listNode.tight; + thing.delimiter = listNode.delimiter; + thing.nodes = listNode.nodes; + FromCiceroEditVisitor.visitNodes(this, thing.nodes, parameters); + + thing.text = null; + delete thing.tag; + delete thing.info; + } + } + break; + } + case 'HtmlInline': { + if (thing.tag && + thing.tag.tagName === 'variable' && + (thing.tag.attributes.length === 2 || thing.tag.attributes.length === 3)) { + const tag = thing.tag; + if (FromCiceroEditVisitor.getAttribute(tag.attributes, 'id') && + FromCiceroEditVisitor.getAttribute(tag.attributes, 'value')) { + const format = FromCiceroEditVisitor.getAttribute(tag.attributes, 'format'); + const ciceroMarkTag = format ? `${CiceroMarkModel.NAMESPACE}.FormattedVariable` : `${CiceroMarkModel.NAMESPACE}.Variable`; + thing.$classDeclaration = parameters.modelManager.getType(ciceroMarkTag); + thing.name = FromCiceroEditVisitor.getAttribute(tag.attributes, 'id').value; + thing.value = decodeURIComponent(FromCiceroEditVisitor.getAttribute(tag.attributes, 'value').value); + if (format) { + thing.format = decodeURIComponent(format.value); + } + delete thing.tag; + delete thing.text; + } + } + if (thing.tag && + thing.tag.tagName === 'if' && + thing.tag.attributes.length === 4) { + const tag = thing.tag; + if (FromCiceroEditVisitor.getAttribute(tag.attributes, 'id') && + FromCiceroEditVisitor.getAttribute(tag.attributes, 'value') && + FromCiceroEditVisitor.getAttribute(tag.attributes, 'whenTrue') && + FromCiceroEditVisitor.getAttribute(tag.attributes, 'whenFalse')) { + const ciceroMarkTag = `${CiceroMarkModel.NAMESPACE}.Conditional`; + thing.$classDeclaration = parameters.modelManager.getType(ciceroMarkTag); + thing.name = FromCiceroEditVisitor.getAttribute(tag.attributes, 'id').value; + const valueText = decodeURIComponent(FromCiceroEditVisitor.getAttribute(tag.attributes, 'value').value); + const valueNode = parameters.serializer.fromJSON({ + $class: `${CommonMarkModel.NAMESPACE}.Text`, + text: valueText, + }); + thing.nodes = [valueNode]; + const whenTrueText = decodeURIComponent(FromCiceroEditVisitor.getAttribute(tag.attributes, 'whenTrue').value); + const whenTrueNodes = whenTrueText ? [parameters.serializer.fromJSON({ + $class: `${CommonMarkModel.NAMESPACE}.Text`, + text: whenTrueText, + })] : []; + thing.isTrue = valueText === whenTrueText; + thing.whenTrue = whenTrueNodes; + const whenFalseText = decodeURIComponent(FromCiceroEditVisitor.getAttribute(tag.attributes, 'whenFalse').value); + const whenFalseNodes = whenFalseText ? [parameters.serializer.fromJSON({ + $class: `${CommonMarkModel.NAMESPACE}.Text`, + text: whenFalseText, + })] : []; + thing.whenFalse = whenFalseNodes; + delete thing.tag; + delete thing.text; + } + } + if (thing.tag && thing.tag.tagName === 'computed' && thing.tag.attributes.length === 1) { + const tag = thing.tag; + const ciceroMarkTag = `${CiceroMarkModel.NAMESPACE}.Formula`; + if (FromCiceroEditVisitor.getAttribute(tag.attributes, 'value')) { + thing.$classDeclaration = parameters.modelManager.getType(ciceroMarkTag); + thing.name = ''; + thing.value = decodeURIComponent(FromCiceroEditVisitor.getAttribute(tag.attributes, 'value').value); + delete thing.tag; + delete thing.text; + } + } + break; + } + default: + FromCiceroEditVisitor.visitChildren(this, thing, parameters); + } + } + + /** + * Find an attribute from its name + */ + static getAttribute(attributes: any[], name: string): any { + const atts = attributes.filter((x) => x.name === name); + return atts.length === 0 ? null : atts[0]; + } +} + +export default FromCiceroEditVisitor; diff --git a/packages/markdown-cicero/src/ToCiceroMarkUnwrappedVisitor.ts b/packages/markdown-cicero/src/ToCiceroMarkUnwrappedVisitor.ts new file mode 100644 index 00000000..d5ebca56 --- /dev/null +++ b/packages/markdown-cicero/src/ToCiceroMarkUnwrappedVisitor.ts @@ -0,0 +1,83 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { CommonMarkModel } from '@accordproject/markdown-common'; + +/** + * Utility: flattening array of arrays + */ +function flatten(arr: T[][]): T[] { + return arr.reduce((acc, val) => acc.concat(val), []); +} + +/** + * Converts a CiceroMark DOM to a CiceroMark unwrapped DOM + */ +export class ToCiceroMarkUnwrappedVisitor { + /** + * Visits a sub-tree and return the CommonMark DOM + */ + static visitChildren(visitor: ToCiceroMarkUnwrappedVisitor, thing: any, parameters: any): void { + if (thing.nodes) { + const result = thing.nodes.map((node: any) => node.accept(visitor, parameters)); + thing.nodes = flatten(result); + } + } + + /** + * Visit a node + */ + visit(thing: any, parameters: any): any[] { + const thingType = thing.getType(); + switch (thingType) { + case 'ListBlock': { + ToCiceroMarkUnwrappedVisitor.visitChildren(this, thing, parameters); + + const ciceroMarkTag = `${CommonMarkModel.NAMESPACE}.List`; + thing.$classDeclaration = parameters.modelManager.getType(ciceroMarkTag); + + delete thing.name; + delete thing.elementType; + delete thing.decorators; + break; + } + case 'Variable': + case 'EnumVariable': + case 'FormattedVariable': { + thing.$classDeclaration = parameters.modelManager.getType(`${CommonMarkModel.NAMESPACE}.Text`); + thing.text = decodeURIComponent(thing.value); + + delete thing.elementType; + delete thing.decorators; + delete thing.name; + delete thing.value; + delete thing.format; + delete thing.enumValues; + delete thing.identifiedBy; + break; + } + case 'Conditional': + case 'Optional': { + thing.$classDeclaration = parameters.modelManager.getType(`${CommonMarkModel.NAMESPACE}.Text`); + ToCiceroMarkUnwrappedVisitor.visitChildren(this, thing, parameters); + return thing.nodes; + } + default: + ToCiceroMarkUnwrappedVisitor.visitChildren(this, thing, parameters); + } + return [thing]; + } +} + +export default ToCiceroMarkUnwrappedVisitor; diff --git a/packages/markdown-cicero/src/ToCommonMarkVisitor.ts b/packages/markdown-cicero/src/ToCommonMarkVisitor.ts new file mode 100644 index 00000000..0c78936d --- /dev/null +++ b/packages/markdown-cicero/src/ToCommonMarkVisitor.ts @@ -0,0 +1,72 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { CommonMarkModel } from '@accordproject/markdown-common'; + +/** + * Utility: flattening array of arrays + */ +function flatten(arr: T[][]): T[] { + return arr.reduce((acc, val) => acc.concat(val), []); +} + +/** + * Converts a CiceroMark DOM to a CommonMark DOM + */ +export class ToCommonMarkVisitor { + options: any; + + constructor(options?: any) { + this.options = options; + } + + /** + * Visits a sub-tree and return the CommonMark DOM + */ + static visitChildren(visitor: ToCommonMarkVisitor, thing: any, parameters: any): void { + if (thing.nodes) { + const result = thing.nodes.map((node: any) => node.accept(visitor, parameters)); + thing.nodes = flatten(result); + } + } + + /** + * Visit a node + */ + visit(thing: any, parameters: any): any[] { + const thingType = thing.getType(); + switch (thingType) { + case 'Clause': { + ToCommonMarkVisitor.visitChildren(this, thing, parameters); + return thing.nodes; + } + case 'Formula': { + thing.$classDeclaration = parameters.modelManager.getType(`${CommonMarkModel.NAMESPACE}.Text`); + thing.text = decodeURIComponent(thing.value); + + delete thing.elementType; + delete thing.name; + delete thing.value; + delete thing.code; + delete thing.dependencies; + break; + } + default: + ToCommonMarkVisitor.visitChildren(this, thing, parameters); + } + return [thing]; + } +} + +export default ToCommonMarkVisitor; diff --git a/packages/markdown-cicero/src/ToMarkdownCiceroVisitor.ts b/packages/markdown-cicero/src/ToMarkdownCiceroVisitor.ts new file mode 100644 index 00000000..468b9796 --- /dev/null +++ b/packages/markdown-cicero/src/ToMarkdownCiceroVisitor.ts @@ -0,0 +1,47 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { CommonMarkUtils, FromCommonMarkVisitor, fromcommonmarkrules } from '@accordproject/markdown-common'; +import fromciceromarkrules from './fromciceromarkrules'; + +/** + * Converts a CiceroMark DOM to a cicero markdown string. + */ +export class ToMarkdownCiceroVisitor extends FromCommonMarkVisitor { + constructor(options?: any) { + const resultString = (result: string) => result; + const resultSeq = (parameters: any, result: any[]) => { + result.forEach((next) => { + parameters.result += next; + }); + }; + const setFirst = (thingType: string) => thingType === 'Item' || thingType === 'Clause'; + const rules = fromcommonmarkrules; + Object.assign(rules, fromciceromarkrules); + super(options, resultString, resultSeq, rules, setFirst); + } + + /** + * Converts a CiceroMark DOM to a cicero markdown string. + */ + toMarkdownCicero(input: any): string { + const parameters: any = {}; + parameters.result = this.resultString(''); + parameters.stack = CommonMarkUtils.blocksInit(); + input.accept(this, parameters); + return parameters.result.trim(); + } +} + +export default ToMarkdownCiceroVisitor; diff --git a/packages/markdown-cicero/src/UnquoteVariables.ts b/packages/markdown-cicero/src/UnquoteVariables.ts new file mode 100644 index 00000000..54182a16 --- /dev/null +++ b/packages/markdown-cicero/src/UnquoteVariables.ts @@ -0,0 +1,68 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Stack, CiceroMarkModel, CommonMarkModel } from '@accordproject/markdown-common'; + +/** + * Maps the keys in an object + */ +function mapObject(obj: any, stack: Stack): void { + switch (obj.$class) { + case `${CiceroMarkModel.NAMESPACE}.Formula`: + case `${CiceroMarkModel.NAMESPACE}.Variable`: + case `${CiceroMarkModel.NAMESPACE}.FormattedVariable`: + stack.append({ + $class: `${CommonMarkModel.NAMESPACE}.Text`, + text: obj.value.replace(/^"/, '').replace(/"$/, ''), + }); + break; + + case `${CommonMarkModel.NAMESPACE}.Document`: + obj.nodes.forEach((element: any) => { + mapObject(element, stack); + }); + break; + + default: + if (obj.nodes) { + const resObj = Object.assign({}, obj); + resObj.nodes = []; + stack.push(resObj); + obj.nodes.forEach((element: any) => { + mapObject(element, stack); + }); + stack.pop(); + } else { + stack.append(obj); + } + break; + } +} + +/** + * Replaces variable and formulas with text nodes + */ +export function unquoteVariables(input: any): any { + const root = { + $class: `${CommonMarkModel.NAMESPACE}.Document`, + xmlns: input.xmlns, + nodes: [], + }; + const stack = new Stack(); + stack.push(root, false); + mapObject(input, stack); + return root; +} + +export default unquoteVariables; diff --git a/packages/markdown-cicero/lib/__snapshots__/CiceroEditTransformer.test.js.snap b/packages/markdown-cicero/src/__snapshots__/CiceroEditTransformer.test.ts.snap similarity index 100% rename from packages/markdown-cicero/lib/__snapshots__/CiceroEditTransformer.test.js.snap rename to packages/markdown-cicero/src/__snapshots__/CiceroEditTransformer.test.ts.snap diff --git a/packages/markdown-cicero/lib/__snapshots__/CiceroMarkTransformer.test.js.snap b/packages/markdown-cicero/src/__snapshots__/CiceroMarkTransformer.test.ts.snap similarity index 100% rename from packages/markdown-cicero/lib/__snapshots__/CiceroMarkTransformer.test.js.snap rename to packages/markdown-cicero/src/__snapshots__/CiceroMarkTransformer.test.ts.snap diff --git a/packages/markdown-cicero/lib/__snapshots__/CommonMarkSpec.test.js.snap b/packages/markdown-cicero/src/__snapshots__/CommonMarkSpec.test.ts.snap similarity index 100% rename from packages/markdown-cicero/lib/__snapshots__/CommonMarkSpec.test.js.snap rename to packages/markdown-cicero/src/__snapshots__/CommonMarkSpec.test.ts.snap diff --git a/packages/markdown-cicero/lib/cicerorules.js b/packages/markdown-cicero/src/cicerorules.ts similarity index 72% rename from packages/markdown-cicero/lib/cicerorules.js rename to packages/markdown-cicero/src/cicerorules.ts index 8da76e80..c95500fa 100644 --- a/packages/markdown-cicero/lib/cicerorules.js +++ b/packages/markdown-cicero/src/cicerorules.ts @@ -12,10 +12,9 @@ * limitations under the License. */ -'use strict'; +import { CiceroMarkModel, CommonMarkUtils } from '@accordproject/markdown-common'; -const { CiceroMarkModel } = require('@accordproject/markdown-common'); -const { getAttr } = require('@accordproject/markdown-common').CommonMarkUtils; +const { getAttr } = CommonMarkUtils; // Inline rules const formulaRule = { @@ -23,8 +22,8 @@ const formulaRule = { leaf: true, open: false, close: false, - enter: (node,token,callback) => { - node.name = getAttr(token.attrs,'name',null); + enter: (node: any, token: any) => { + node.name = getAttr(token.attrs, 'name', null); node.value = token.content; node.dependencies = []; }, @@ -37,9 +36,9 @@ const clauseOpenRule = { leaf: false, open: true, close: false, - enter: (node,token,callback) => { - node.name = getAttr(token.attrs,'name',null); - node.src = getAttr(token.attrs,'src',null); + enter: (node: any, token: any) => { + node.name = getAttr(token.attrs, 'name', null); + node.src = getAttr(token.attrs, 'src', null); }, }; const clauseCloseRule = { @@ -49,10 +48,10 @@ const clauseCloseRule = { close: true, }; -const rules = { inlines: {}, blocks: {} }; +const rules: any = { inlines: {}, blocks: {} }; rules.inlines.formula = formulaRule; rules.blocks.block_clause_open = clauseOpenRule; rules.blocks.block_clause_close = clauseCloseRule; -module.exports = rules; +export default rules; diff --git a/packages/markdown-cicero/src/fromciceromarkrules.ts b/packages/markdown-cicero/src/fromciceromarkrules.ts new file mode 100644 index 00000000..4b854355 --- /dev/null +++ b/packages/markdown-cicero/src/fromciceromarkrules.ts @@ -0,0 +1,39 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { CommonMarkUtils } from '@accordproject/markdown-common'; + +const rules: Record = {}; + +// Inlines +rules.Formula = (visitor: any, thing: any, children: any, parameters: any, resultString: any, resultSeq: any) => { + const result = [resultString('{{%'), resultString(thing.value), resultString('%}}')]; + resultSeq(parameters, result); +}; + +// Container blocks +rules.Clause = (visitor: any, thing: any, children: any, parameters: any, resultString: any, resultSeq: any) => { + const next1 = CommonMarkUtils.mkPrefix(parameters, 2); + const srcAttr = thing.src ? ' src="' + thing.src + '"' : ''; + const next2 = `{{#clause ${thing.name}${srcAttr}}}\n`; + const closeParameters = Object.assign({}, parameters); + closeParameters.stack = Object.assign({}, parameters.stack); + closeParameters.stack.first = false; + const next3 = CommonMarkUtils.mkPrefix(closeParameters, 1); + const next4 = '{{/clause}}'; + const result = [resultString(next1), resultString(next2), children, resultString(next3), resultString(next4)]; + resultSeq(parameters, result); +}; + +export default rules; diff --git a/packages/markdown-cicero/src/index.ts b/packages/markdown-cicero/src/index.ts new file mode 100644 index 00000000..38f32b51 --- /dev/null +++ b/packages/markdown-cicero/src/index.ts @@ -0,0 +1,32 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { CiceroMarkTransformer } from './CiceroMarkTransformer'; +import { FromCiceroEditVisitor } from './FromCiceroEditVisitor'; +import { ToCommonMarkVisitor } from './ToCommonMarkVisitor'; +import { Decorators } from './Decorators'; + +export { + CiceroMarkTransformer, + FromCiceroEditVisitor, + ToCommonMarkVisitor, + Decorators, +}; + +export default { + CiceroMarkTransformer, + FromCiceroEditVisitor, + ToCommonMarkVisitor, + Decorators, +}; diff --git a/packages/markdown-cicero/src/jest.d.ts b/packages/markdown-cicero/src/jest.d.ts new file mode 100644 index 00000000..5da28a66 --- /dev/null +++ b/packages/markdown-cicero/src/jest.d.ts @@ -0,0 +1,19 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +declare namespace jest { + interface Matchers { + toMarkdownRoundtrip(markdownText?: string, testName?: string): R; + } +} diff --git a/packages/markdown-cicero/tsconfig.json b/packages/markdown-cicero/tsconfig.json index f398dc0a..fb3e9f82 100644 --- a/packages/markdown-cicero/tsconfig.json +++ b/packages/markdown-cicero/tsconfig.json @@ -1,11 +1,11 @@ { + "extends": "../../tsconfig.base.json", "compilerOptions": { - "allowJs": true, + "rootDir": "src", + "outDir": "lib", "declaration": true, - "emitDeclarationOnly": true, - "outDir": "types", - "strict": false + "sourceMap": true }, - "include": ["index.js", "lib/**/*.js"], - "exclude": ["lib/**/*.test.js", "lib/**/__tests__/**/*"] + "include": ["src/**/*.ts"], + "exclude": ["src/**/*.test.ts", "lib", "node_modules"] } diff --git a/packages/markdown-cicero/tsconfig.test.json b/packages/markdown-cicero/tsconfig.test.json new file mode 100644 index 00000000..58810225 --- /dev/null +++ b/packages/markdown-cicero/tsconfig.test.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "rootDir": ".", + "noEmit": true + }, + "include": ["src/**/*.ts"], + "exclude": ["lib", "node_modules"] +} diff --git a/packages/markdown-cicero/types/index.d.ts b/packages/markdown-cicero/types/index.d.ts deleted file mode 100644 index c9528b41..00000000 --- a/packages/markdown-cicero/types/index.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const CiceroMarkTransformer: typeof import("./lib/CiceroMarkTransformer"); -export const FromCiceroEditVisitor: typeof import("./lib/FromCiceroEditVisitor"); -export const ToCommonMarkVisitor: typeof import("./lib/ToCommonMarkVisitor"); -export const Decorators: typeof import("./lib/Decorators"); diff --git a/packages/markdown-cicero/types/lib/CiceroMarkTransformer.d.ts b/packages/markdown-cicero/types/lib/CiceroMarkTransformer.d.ts deleted file mode 100644 index 8dd08d0d..00000000 --- a/packages/markdown-cicero/types/lib/CiceroMarkTransformer.d.ts +++ /dev/null @@ -1,113 +0,0 @@ -export = CiceroMarkTransformer; -/** - * Converts a CiceroMark DOM to/from a - * CommonMark DOM. - * - * Converts a CiceroMark DOM to/from a markdown string. - */ -declare class CiceroMarkTransformer { - commonMark: import("@accordproject/markdown-common/types/lib/CommonMarkTransformer"); - modelManager: ModelManager; - serializer: Serializer; - /** - * Obtain the Clause text for a Clause node - * @param {IClause} input CiceroMark DOM - * @returns {string} markdown_cicero string - */ - getClauseText(input: IClause): string; - /** - * Retrieve the serializer used by the parser - * - * @returns {Serializer} a serializer capable of dealing with the Concerto - * object returns by parse - */ - getSerializer(): Serializer; - /** - * Converts a CiceroEdit string to a CiceroMark DOM - * @param {string} input - ciceroedit string - * @returns {ICiceroDocument} CiceroMark DOM - */ - fromCiceroEdit(input: string): ICiceroDocument; - /** - * Converts a CiceroMark DOM to a CiceroMark Unwrapped DOM - * @param {ICiceroDocument} input - CiceroMark DOM (JSON) - * @param {object} [options] configuration options - * @param {boolean} [options.unquoteVariables] if true variable quotations are removed - * @returns {ICiceroDocument} CiceroMark DOM - */ - toCiceroMarkUnwrapped(input: ICiceroDocument, options?: { - unquoteVariables?: boolean; - }): ICiceroDocument; - /** - * Converts a CommonMark DOM to a CiceroMark DOM - * @param {IDocument} input - CommonMark DOM (in JSON) - * @returns {ICiceroDocument} CiceroMark DOM - */ - fromCommonMark(input: IDocument): ICiceroDocument; - /** - * Converts a markdown string to a CiceroMark DOM - * @param {string} markdown a markdown string - * @returns {ICiceroDocument} ciceromark object (JSON) - */ - fromMarkdown(markdown: string): ICiceroDocument; - /** - * Converts a CiceroMark DOM to a markdown string - * @param {ICiceroDocument} input CiceroMark DOM - * @param {object} [options] configuration options - * @returns {string} markdown string - */ - toMarkdown(input: ICiceroDocument, options?: object): string; - /** - * Converts a cicero markdown string to a CiceroMark DOM - * @param {string} markdown a cicero markdown string - * @param {object} [options] configuration options - * @returns {ICiceroDocument} ciceromark object (JSON) - */ - fromMarkdownCicero(markdown: string, options?: object): ICiceroDocument; - /** - * Converts a CiceroMark DOM to a cicero markdown string - * @param {ICiceroDocument} input CiceroMark DOM - * @returns {string} cicero markdown string - */ - toMarkdownCicero(input: ICiceroDocument): string; - /** - * Converts a CiceroMark DOM to a CommonMark DOM - * @param {ICiceroDocument} input CiceroMark DOM - * @param {object} [options] configuration options - * @param {boolean} [options.removeFormatting] if true the formatting nodes are removed - * @param {boolean} [options.unquoteVariables] if true variable quotations are removed - * @returns {IDocument} CommonMark DOM - */ - toCommonMark(input: ICiceroDocument, options?: { - removeFormatting?: boolean; - unquoteVariables?: boolean; - }): IDocument; - /** - * Unquotes a CiceroMark DOM - * @param {ICiceroDocument} input CiceroMark DOM - * @returns {ICiceroDocument} unquoted CiceroMark DOM - */ - unquote(input: ICiceroDocument): ICiceroDocument; - /** - * Converts a ciceromark string into a token stream - * - * @param {string} input the string to parse - * @returns {object[]} a markdown-it token stream - */ - toTokens(input: string): object[]; - /** - * Converts a token stream into a CiceroMark DOM object. - * - * @param {object[]} tokenStream the token stream - * @returns {ICiceroDocument} the CiceroMark DOM (JSON) - */ - fromTokens(tokenStream: object[]): ICiceroDocument; -} -declare namespace CiceroMarkTransformer { - export { IDocument, ICiceroDocument, IClause }; -} -import { ModelManager } from "@accordproject/concerto-core"; -import { Serializer } from "@accordproject/concerto-core"; -type IDocument = import("@accordproject/markdown-common/types/model/commonmark").IDocument; -type ICiceroDocument = import("@accordproject/markdown-common/types/model/commonmark").IDocument; -type IClause = import("@accordproject/markdown-common/types/model/ciceromark").IClause; diff --git a/packages/markdown-cicero/types/lib/Decorators.d.ts b/packages/markdown-cicero/types/lib/Decorators.d.ts deleted file mode 100644 index 260e8380..00000000 --- a/packages/markdown-cicero/types/lib/Decorators.d.ts +++ /dev/null @@ -1,35 +0,0 @@ -export = Decorators; -/** - * A class to retrieve decorators on CiceroMark nodes - */ -declare class Decorators { - /** - * Construct an instance, based on a CiceroMark node - * Note that decorator arguments must be specified as an - * array of [name (string),value] pairs, even though this is - * not enforced by the Concerto grammar. - * @param {object} node the CiceroMark node - */ - constructor(node: object); - data: {}; - /** - * Returns true is the decorator is present - * @param {string} decoratorName the name of the decorator - * @returns {boolean} true is the decorator is present - */ - hasDecorator(decoratorName: string): boolean; - /** - * Get the arguments for a named decorator - * @param {string} decoratorName the name of the decorator - * @returns {array} an array of arguments, or null - */ - getArguments(decoratorName: string): any[]; - /** - * Get the arguments for a named decorator - * @param {string} decoratorName the name of the decorator - * @param {string} argumentName the name of the decorator argument - * @returns {object} the value of the argument or null if the decorator - * is missing or undefined if the argument is missing - */ - getDecoratorValue(decoratorName: string, argumentName: string): object; -} diff --git a/packages/markdown-cicero/types/lib/FromCiceroEditVisitor.d.ts b/packages/markdown-cicero/types/lib/FromCiceroEditVisitor.d.ts deleted file mode 100644 index 749f03bf..00000000 --- a/packages/markdown-cicero/types/lib/FromCiceroEditVisitor.d.ts +++ /dev/null @@ -1,33 +0,0 @@ -export = FromCiceroEditVisitor; -/** - * Converts a CommonMark DOM to a CiceroMark DOM - */ -declare class FromCiceroEditVisitor { - /** - * Visits a sub-tree and return CiceroMark DOM - * @param {*} visitor the visitor to use - * @param {*} thing the node to visit - * @param {*} [parameters] optional parameters - */ - static visitChildren(visitor: any, thing: any, parameters?: any): void; - /** - * Visits a list of nodes and return the CiceroMark DOM - * @param {*} visitor the visitor to use - * @param {*} things the list node to visit - * @param {*} [parameters] optional parameters - */ - static visitNodes(visitor: any, things: any, parameters?: any): void; - /** - * Find an attribute from its name - * @param {*} attributes - the array of attributes - * @param {string} name - the name of the attributes - * @return {*} the attribute or undefined - */ - static getAttribute(attributes: any, name: string): any; - /** - * Visit a node - * @param {*} thing the object being visited - * @param {*} parameters the parameters - */ - visit(thing: any, parameters: any): void; -} diff --git a/packages/markdown-cicero/types/lib/ToCiceroMarkUnwrappedVisitor.d.ts b/packages/markdown-cicero/types/lib/ToCiceroMarkUnwrappedVisitor.d.ts deleted file mode 100644 index 075b9086..00000000 --- a/packages/markdown-cicero/types/lib/ToCiceroMarkUnwrappedVisitor.d.ts +++ /dev/null @@ -1,20 +0,0 @@ -export = ToCiceroMarkUnwrappedVisitor; -/** - * Converts a CiceroMark DOM to a CiceroMark unwrapped DOM - */ -declare class ToCiceroMarkUnwrappedVisitor { - /** - * Visits a sub-tree and return the CommonMark DOM - * @param {*} visitor the visitor to use - * @param {*} thing the node to visit - * @param {*} [parameters] optional parameters - */ - static visitChildren(visitor: any, thing: any, parameters?: any): void; - /** - * Visit a node - * @param {*} thing the object being visited - * @param {*} parameters the parameters - * @return {*[]} result nodes - */ - visit(thing: any, parameters: any): any[]; -} diff --git a/packages/markdown-cicero/types/lib/ToCommonMarkVisitor.d.ts b/packages/markdown-cicero/types/lib/ToCommonMarkVisitor.d.ts deleted file mode 100644 index c54c7a79..00000000 --- a/packages/markdown-cicero/types/lib/ToCommonMarkVisitor.d.ts +++ /dev/null @@ -1,20 +0,0 @@ -export = ToCommonMarkVisitor; -/** - * Converts a CiceroMark DOM to a CommonMark DOM - */ -declare class ToCommonMarkVisitor { - /** - * Visits a sub-tree and return the CommonMark DOM - * @param {*} visitor the visitor to use - * @param {*} thing the node to visit - * @param {*} [parameters] optional parameters - */ - static visitChildren(visitor: any, thing: any, parameters?: any): void; - /** - * Visit a node - * @param {*} thing the object being visited - * @param {*} parameters the parameters - * @return {*[]} result nodes - */ - visit(thing: any, parameters: any): any[]; -} diff --git a/packages/markdown-cicero/types/lib/ToMarkdownCiceroVisitor.d.ts b/packages/markdown-cicero/types/lib/ToMarkdownCiceroVisitor.d.ts deleted file mode 100644 index 16231561..00000000 --- a/packages/markdown-cicero/types/lib/ToMarkdownCiceroVisitor.d.ts +++ /dev/null @@ -1,20 +0,0 @@ -export = ToMarkdownCiceroVisitor; -declare const ToMarkdownCiceroVisitor_base: typeof import("@accordproject/markdown-common/types/lib/FromCommonMarkVisitor"); -/** - * Converts a CiceroMark DOM to a cicero markdown string. - */ -declare class ToMarkdownCiceroVisitor extends ToMarkdownCiceroVisitor_base { - /** - * Construct the visitor. - * @param {object} [options] configuration options - * @param {*} resultSeq how to sequentially combine results - * @param {object} rules how to process each node type - */ - constructor(options?: object); - /** - * Converts a CiceroMark DOM to a cicero markdown string. - * @param {*} input - CiceroMark DOM (JSON) - * @returns {string} the cicero markdown string - */ - toMarkdownCicero(input: any): string; -} diff --git a/packages/markdown-cicero/types/lib/UnquoteVariables.d.ts b/packages/markdown-cicero/types/lib/UnquoteVariables.d.ts deleted file mode 100644 index 0cfc4880..00000000 --- a/packages/markdown-cicero/types/lib/UnquoteVariables.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -export = unquoteVariables; -/** -* Replaces variable and formulas with text nodes -* @param {*} input input object -* @param {*} options options object -* @returns {*} the modified object -*/ -declare function unquoteVariables(input: any): any; diff --git a/packages/markdown-cicero/types/lib/cicerorules.d.ts b/packages/markdown-cicero/types/lib/cicerorules.d.ts deleted file mode 100644 index d24072be..00000000 --- a/packages/markdown-cicero/types/lib/cicerorules.d.ts +++ /dev/null @@ -1,38 +0,0 @@ -declare namespace formulaRule { - let tag: string; - let leaf: boolean; - let open: boolean; - let close: boolean; - function enter(node: any, token: any, callback: any): void; - let skipEmpty: boolean; -} -declare namespace clauseOpenRule { - let tag_1: string; - export { tag_1 as tag }; - let leaf_1: boolean; - export { leaf_1 as leaf }; - let open_1: boolean; - export { open_1 as open }; - let close_1: boolean; - export { close_1 as close }; - export function enter_1(node: any, token: any, callback: any): void; - export { enter_1 as enter }; -} -declare namespace clauseCloseRule { - let tag_2: string; - export { tag_2 as tag }; - let leaf_2: boolean; - export { leaf_2 as leaf }; - let open_2: boolean; - export { open_2 as open }; - let close_2: boolean; - export { close_2 as close }; -} -export namespace inlines { - export { formulaRule as formula }; -} -export namespace blocks { - export { clauseOpenRule as block_clause_open }; - export { clauseCloseRule as block_clause_close }; -} -export {}; diff --git a/packages/markdown-cicero/types/lib/fromciceromarkrules.d.ts b/packages/markdown-cicero/types/lib/fromciceromarkrules.d.ts deleted file mode 100644 index d37e1105..00000000 --- a/packages/markdown-cicero/types/lib/fromciceromarkrules.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export function Formula(visitor: any, thing: any, children: any, parameters: any, resultString: any, resultSeq: any): void; -export function Clause(visitor: any, thing: any, children: any, parameters: any, resultString: any, resultSeq: any): void; diff --git a/packages/markdown-cli/.eslintrc.cjs b/packages/markdown-cli/.eslintrc.cjs new file mode 100644 index 00000000..27817c11 --- /dev/null +++ b/packages/markdown-cli/.eslintrc.cjs @@ -0,0 +1,37 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +module.exports = { + root: true, + env: { es2022: true, node: true, jest: true }, + extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'], + parser: '@typescript-eslint/parser', + parserOptions: { ecmaVersion: 2022, sourceType: 'module' }, + plugins: ['@typescript-eslint'], + ignorePatterns: ['node_modules/', 'lib/', 'coverage/'], + rules: { + 'indent': ['error', 4, { 'SwitchCase': 1 }], + 'quotes': ['error', 'single', { 'avoidEscape': true, 'allowTemplateLiterals': true }], + 'semi': ['error', 'always'], + 'no-console': 'warn', + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-var-requires': 'off', + '@typescript-eslint/no-unused-vars': ['error', { 'args': 'none', 'ignoreRestSiblings': true, 'caughtErrors': 'none' }], + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/no-empty-function': 'off', + '@typescript-eslint/ban-ts-comment': 'off', + '@typescript-eslint/no-this-alias': 'off', + 'no-unused-vars': 'off', + }, +}; diff --git a/packages/markdown-cli/.eslintrc.yml b/packages/markdown-cli/.eslintrc.yml deleted file mode 100644 index ec0c5d88..00000000 --- a/packages/markdown-cli/.eslintrc.yml +++ /dev/null @@ -1,47 +0,0 @@ -env: - es6: true - node: true - mocha: true -extends: 'eslint:recommended' -parserOptions: - ecmaVersion: 12 - sourceType: 'script' -rules: - indent: - - error - - 4 - linebreak-style: - - warn - - unix - quotes: - - error - - single - semi: - - error - - always - no-unused-vars: - - error - - args: none - no-console: warn - curly: error - eqeqeq: error - no-throw-literal: error - strict: error - no-var: error - dot-notation: error - no-tabs: error - no-trailing-spaces: error - # no-use-before-define: error - no-useless-call: error - no-with: error - operator-linebreak: error - require-jsdoc: - - error - - require: - ClassDeclaration: true - MethodDefinition: true - FunctionDeclaration: true - valid-jsdoc: - - error - - requireReturn: false - yoda: error diff --git a/packages/markdown-cli/.gitignore b/packages/markdown-cli/.gitignore new file mode 100644 index 00000000..e8b2d1ae --- /dev/null +++ b/packages/markdown-cli/.gitignore @@ -0,0 +1,5 @@ +node_modules/ +/lib +/umd +coverage +.nyc_output diff --git a/packages/markdown-cli/README.md b/packages/markdown-cli/README.md index 56dbe26b..584c08da 100644 --- a/packages/markdown-cli/README.md +++ b/packages/markdown-cli/README.md @@ -1,9 +1,8 @@ - # Command Line -Install the `@accordproject/markdown-cli` npm package to access the Markdown Transform command line interface (CLI). After installation you can use the `markus` command and its sub-commands as described below. +Install the `@accordproject/markdown-cli` npm package to get the `markus` command line interface (CLI). After installation you can use the `markus` command and its sub-commands as described below. -To install the Markdown CLI: +To install: ```bash npm install -g @accordproject/markdown-cli @@ -27,7 +26,7 @@ Options: ## `markus transform` -The `markus transform` command lets you transform between any two of the supported formats +The `markus transform` command lets you transform between any two of the supported formats. ```md markus transform @@ -49,13 +48,18 @@ Options: --contract contract template [boolean] [default: false] --currentTime set current time [string] [default: null] --plugin path to a parser plugin [string] + --extension path to a transform extension [array] --sourcePos enable source position [boolean] [default: false] --offline do not resolve external models [boolean] [default: false] ``` +Supported formats for `--from` / `--to` / `--via`: + +`markdown`, `markdown_cicero`, `markdown_template`, `commonmark_tokens`, `ciceromark_tokens`, `templatemark_tokens`, `commonmark`, `ciceromark`, `ciceromark_parsed`, `ciceromark_unquoted`, `templatemark`, `ciceroedit`, `html`, `plaintext`. + ### Example -For example, you can use the `transform` command on the `README.md` file from the [Hello World](https://github.com/accordproject/cicero-template-library/blob/main/src/helloworld) template: +Run `transform` on a markdown file: ```bash markus transform --input README.md @@ -77,18 +81,18 @@ returns: "text": "Hello World" } ] - }, + }, { "$class": "org.accordproject.commonmark@0.5.0.Paragraph", "nodes": [ { "$class": "org.accordproject.commonmark@0.5.0.Text", "text": "This is the Hello World of Accord Project Templates. Executing the clause will simply echo back the text that occurs after the string " - }, + }, { "$class": "org.accordproject.commonmark@0.5.0.Code", "text": "Hello" - }, + }, { "$class": "org.accordproject.commonmark@0.5.0.Text", "text": " prepended to text that is passed in the request." @@ -101,15 +105,15 @@ returns: ### `--from` and `--to` options -You can indicate the source and target formats using the `--from` and `--to` options. For instance, the following transforms from `markdown` to `html`: +Set the source and target formats. The following converts markdown to HTML: ```bash -markus transform --from markdown --to html +markus transform --from markdown --to html --input README.md ``` returns: -```md +```html
@@ -122,30 +126,17 @@ returns: ### `--via` option -When there are several paths between two formats, you can indicate an intermediate format using the `--via` option. The following transforms from `markdown` to `html` *via* `ciceromark`: +When there are several paths between two formats, you can route through an intermediate format. The following transforms from `markdown` to `html` *via* `ciceromark`: ```bash -markus transform --from markdown --via ciceromark --to html -``` - -returns: - -```md - - -
-

Hello World

-

This is the Hello World of Accord Project Templates. Executing the clause will simply echo back the text that occurs after the string Hello prepended to text that is passed in the request.

-
- - +markus transform --from markdown --via ciceromark --to html --input README.md ``` ### `--roundtrip` option -When the transforms allow, you can roundtrip between two formats, i.e., transform from a source to a target format and back to the source target. For instance, the following transform from `markdown` to `ciceromark` and back to markdown: +You can roundtrip between two formats — transform from source to target then back to source. For example, `markdown → ciceromark → markdown`: -```md +```bash markus transform --from markdown --to ciceromark --input README.md --roundtrip ``` @@ -158,20 +149,19 @@ Hello World This is the Hello World of Accord Project Templates. Executing the clause will simply echo back the text that occurs after the string `Hello` prepended to text that is passed in the request. ``` +Roundtripping might result in small textual differences in the source markdown but should always be semantically equivalent. In the example above the ATX heading `# Hello World` has been transformed into the equivalent Setext heading. +### `--model` and `--contract` options -Roundtripping might result in small changes in the source markdown, but should always be semantically equivalent. In the above example the source ATX heading `# Hello World` has been transformed into a Setext heading equivalent. - - +When handling [TemplateMark](https://docs.accordproject.org/docs/markdown-templatemark), provide a Concerto model with `--model` and add `--contract` if the template is a contract (otherwise it is treated as a clause). -### `--model` `--contract` options - -When handling [TemplateMark](https://docs.accordproject.org/docs/markdown-templatemark), one has to provide a model using the `--model` option and whether the template is a clause (default) or a contract (using the `--contract` option). - -For instance the following converts markdown with the template extension to a TemplateMark document object model: +For instance, the following converts a TemplateMark file to its DOM: ```bash -markus transform --from markdown_template --to templatemark --model model/model.cto --input text/grammar.tem.md +markus transform \ + --from markdown_template --to templatemark \ + --model model/model.cto \ + --input text/grammar.tem.md ``` returns: @@ -192,19 +182,19 @@ returns: { "$class": "org.accordproject.commonmark@0.5.0.Text", "text": "Name of the person to greet: " - }, + }, { "$class": "org.accordproject.templatemark@0.5.0.VariableDefinition", "name": "name", "elementType": "String" - }, + }, { "$class": "org.accordproject.commonmark@0.5.0.Text", "text": "." - }, + }, { "$class": "org.accordproject.commonmark@0.5.0.Softbreak" - }, + }, { "$class": "org.accordproject.commonmark@0.5.0.Text", "text": "Thank you!" @@ -217,22 +207,9 @@ returns: } ``` -### `--template` option - -Parsing or drafting contract text using a template can be done using the `--template` option, usually with the corresponding `--model` option to indicate the template model. - -For instance, the following parses a markdown with CiceroMark extension to get the correspond contract data: +### `--extension` option -```bash -markus transform --from markdown_cicero --to data --template text/grammar.tem.md --model model/model.cto --input text/sample.md -``` - -returns: +Pass `--extension path/to/ext.js` (repeatable) to register a custom transform extension at runtime. An extension is a module that exports `{ format?, transforms? }` where `format` declares a new format and `transforms` declares edges to/from it in the transformation graph. See the integration tests in this package for examples. -```json -{ - "$class": "org.accordproject.helloworld.HelloWorldClause", - "name": "Fred Blogs", - "clauseId": "fc345528-2604-420c-9e02-8d85e03cb65b" -} -``` +## License +Accord Project source code files are made available under the Apache License, Version 2.0 (Apache-2.0), located in the LICENSE file. Accord Project documentation files are made available under the Creative Commons Attribution 4.0 International License (CC-BY-4.0), available at http://creativecommons.org/licenses/by/4.0/. diff --git a/packages/markdown-cli/index.js b/packages/markdown-cli/index.js deleted file mode 100755 index 80526e83..00000000 --- a/packages/markdown-cli/index.js +++ /dev/null @@ -1,160 +0,0 @@ -#!/usr/bin/env node -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -const path = require('path'); -const logger = require('@accordproject/concerto-util').Logger; -const commands = require('./lib/commands'); - -require('yargs') - .scriptName('markus') - .usage('$0 [args]') - .demandCommand(1, '# Please specify a command') - .recommendCommands() - .strict() - .command('transform', 'transform between two formats', (yargs) => { - yargs.option('input', { - describe: 'path to the input', - type: 'string' - }); - yargs.option('from', { - describe: 'source format', - type: 'string', - default: 'markdown' - }); - yargs.option('to', { - describe: 'target format', - type: 'string', - default: 'commonmark' - }); - yargs.option('via', { - describe: 'intermediate formats', - type: 'string', - array: true, - default: [] - }); - yargs.option('roundtrip', { - describe: 'roundtrip transform', - type: 'boolean', - default: false - }); - yargs.option('output', { - describe: 'path to the output file', - type: 'string' - }); - yargs.option('model', { - describe: 'array of concerto model files', - type: 'string', - array: true - }); - yargs.option('template', { - describe: 'template grammar', - type: 'string' - }); - yargs.option('contract', { - describe: 'contract template', - type: 'boolean', - default: false - }); - yargs.option('currentTime', { - describe: 'set current time', - type: 'string', - default: null - }); - yargs.option('plugin', { - describe: 'path to a parser plugin', - type: 'string' - }); - yargs.option('extension', { - describe: 'path to a transform extension', - type: 'string', - array: true, - }); - yargs.option('sourcePos', { - describe: 'enable source position', - type: 'boolean', - default: false - }); - yargs.option('verbose', { - describe: 'verbose output', - type: 'boolean', - default: false - }); - yargs.option('offline', { - describe: 'do not resolve external models', - type: 'boolean', - default: false - }); - }, (argv) => { - if (argv.verbose) { - logger.info(`transform input ${argv.input} file`); - } - - try { - argv = commands.validateTransformArgs(argv); - const parameters = {}; - parameters.inputFileName = argv.input; - parameters.model = argv.model; - // Load a parser plugin if given - let plugin = {}; - if (argv.plugin) { - plugin = require(path.resolve(process.cwd(),argv.plugin)); - } - // Load a transform extension if given - const extensions = []; - if (argv.extension) { - argv.extension.forEach((thisExtension) => { - let modExtension = require(path.resolve(process.cwd(),thisExtension)); - if (modExtension.default) { - modExtension = modExtension.default; - } - extensions.push(modExtension); - }); - } - parameters.plugin = plugin; - parameters.template = argv.template; - parameters.templateKind = argv.contract ? 'contract' : 'clause'; - parameters.currentTime = argv.currentTime; - const options = {}; - options.verbose = argv.verbose; - options.sourcePos = argv.sourcePos; - options.roundtrip = argv.roundtrip; - options.offline = argv.offline; - options.extensions = extensions; - return commands.transform(argv.input, argv.from, argv.via, argv.to, argv.output, parameters, options) - .then(({ result, targetFormat }) => { - if(result) { - if(targetFormat.fileFormat !== 'binary') { - logger.info('\n'+result); - } else { - logger.info(`\n`); - } - } - }) - .catch((err) => { - logger.error(err.message); - }); - } catch (err){ - logger.error(err.message); - return; - } - }) - .option('verbose', { - alias: 'v', - default: false - }) - .help() - .parse(); diff --git a/packages/markdown-cli/test/extension/wordcount.js b/packages/markdown-cli/jest.config.js similarity index 53% rename from packages/markdown-cli/test/extension/wordcount.js rename to packages/markdown-cli/jest.config.js index 33d4cd04..b92d058c 100644 --- a/packages/markdown-cli/test/extension/wordcount.js +++ b/packages/markdown-cli/jest.config.js @@ -14,23 +14,18 @@ 'use strict'; +/** @type {import('jest').Config} */ module.exports = { - format: { - name: 'wordcount', - docs: 'A number of words', - fileFormat: 'utf8' + preset: 'ts-jest', + testEnvironment: 'node', + clearMocks: true, + testTimeout: 30000, + testMatch: ['/src/**/*.test.ts'], + collectCoverageFrom: ['src/**/*.ts', '!src/**/*.test.ts', '!src/**/*.d.ts', '!src/cli.ts'], + coverageDirectory: 'coverage', + coveragePathIgnorePatterns: ['/node_modules/'], + coverageReporters: ['json', 'text', 'lcov', 'html'], + transform: { + '^.+\\.tsx?$': ['ts-jest', { tsconfig: '/tsconfig.test.json' }], }, - transforms: { - // Transform a plain text into a word count - plaintext: { - wordcount: ((input, parameters, options) => { - const count = input.split(' ').length; - return '' + count; - }), - }, - // Transform a word count back into plain text - wordcount: { - plaintext: ((input, parameteres, options) => input), - }, - } }; diff --git a/packages/markdown-cli/jsdoc.json b/packages/markdown-cli/jsdoc.json deleted file mode 100644 index 03879d0b..00000000 --- a/packages/markdown-cli/jsdoc.json +++ /dev/null @@ -1 +0,0 @@ -{"tags":{"allowUnknownTags":true,"dictionaries":["jsdoc","closure"]},"source":{"include":["./src","./index.js"],"includePattern":".+\\.js(doc|x)?$"},"plugins":["plugins/markdown"],"templates":{"logoFile":"","cleverLinks":false,"monospaceLinks":false,"dateFormat":"ddd MMM Do YYYY","outputSourceFiles":true,"outputSourcePath":true,"systemName":"Accord Project Cicero SDK","footer":"","copyright":"Released under the Apache License v2.0","navType":"vertical","theme":"spacelab","linenums":true,"collapseSymbols":false,"inverseNav":true,"protocol":"html://","methodHeadingReturns":false},"markdown":{"parser":"gfm","hardwrap":true}} \ No newline at end of file diff --git a/packages/markdown-cli/package.json b/packages/markdown-cli/package.json index 0fc4f311..25a7bed4 100644 --- a/packages/markdown-cli/package.json +++ b/packages/markdown-cli/package.json @@ -1,6 +1,6 @@ { "name": "@accordproject/markdown-cli", - "version": "0.16.25", + "version": "1.0.0", "description": "A framework for transforming markdown", "engines": { "node": ">=22", @@ -10,26 +10,25 @@ "access": "public" }, "bin": { - "markus": "index.js" + "markus": "lib/cli.js" }, "files": [ - "bin", - "lib", - "types", - "umd" + "lib" ], - "main": "index.js", + "main": "lib/index.js", + "types": "lib/index.d.ts", + "typings": "lib/index.d.ts", "scripts": { - "pretest": "npm run lint", - "lint": "eslint .", + "pretest": "npm run lint && npm run build", + "lint": "eslint . --ext .ts", "postlint": "npm run licchk", "licchk": "license-check-and-add", - "test": "mocha --exclude test/extension --timeout 30000", - "test:cov": "npm run lint && nyc mocha --timeout 30000", - "build": "npm run build:types", - "build:types": "tsc" + "test": "jest --silent", + "test:noisy": "jest", + "test:cov": "npm run lint && npm run build && jest --coverage --silent", + "build": "tsc -p tsconfig.json", + "clean": "rimraf lib" }, - "typings": "types/index.d.ts", "repository": { "type": "git", "url": "git+https://github.com/accordproject/markdown-transform.git", @@ -47,13 +46,16 @@ "url": "https://github.com/accordproject/markdown-transform/issues" }, "devDependencies": { - "chai": "4.3.6", - "chai-as-promised": "7.1.1", - "chai-things": "0.2.0", + "@types/jest": "^29.5.12", + "@types/node": "^20.11.30", + "@types/yargs": "^17.0.32", + "@typescript-eslint/eslint-plugin": "^7.4.0", + "@typescript-eslint/parser": "^7.4.0", "eslint": "8.57.1", + "jest": "^29.7.0", "license-check-and-add": "2.3.6", - "mocha": "10.8.2", - "nyc": "17.1.0", + "rimraf": "^5.0.5", + "ts-jest": "^29.1.2", "typescript": "^5.9.3" }, "dependencies": { @@ -62,14 +64,11 @@ "yargs": "17.7.2" }, "license-check-and-add-config": { - "folder": "./lib", + "folder": "./src", "license": "header.txt", "exact_paths_method": "EXCLUDE", "exact_paths": [ - "externalModels/.npmignore", - "externalModels/.gitignore", "coverage", - "index.d.ts", "./system", "LICENSE", "node_modules", @@ -87,7 +86,7 @@ ], "insert_license": false, "license_formats": { - "js|njk|pegjs|cto|acl|qry": { + "ts|tsx|js|njk|pegjs|cto|acl|qry": { "prepend": "/*", "append": " */", "eachLine": { @@ -103,27 +102,5 @@ "file": "header.md" } } - }, - "nyc": { - "produce-source-map": "true", - "sourceMap": "inline", - "reporter": [ - "lcov", - "text-summary", - "html", - "json" - ], - "include": [ - "lib/**/*.js" - ], - "exclude": [ - "scripts/**/*.js" - ], - "all": true, - "check-coverage": true, - "statements": 78, - "branches": 61, - "functions": 77, - "lines": 77 } } diff --git a/packages/markdown-cli/src/cli.test.ts b/packages/markdown-cli/src/cli.test.ts new file mode 100644 index 00000000..bd516e88 --- /dev/null +++ b/packages/markdown-cli/src/cli.test.ts @@ -0,0 +1,109 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as fs from 'fs'; +import * as path from 'path'; +import { Commands } from './commands'; + +function normalizeNLs(input: string): string { + return input.replace(/\r/gm, ''); +} + +function loadModels(dir: string): string[] { + const files = fs.readdirSync(dir); + const ctoFiles = files.filter((file) => path.extname(file) === '.cto'); + return ctoFiles.map((file) => path.join(dir, file)); +} + +const dataDir = path.resolve(__dirname, '..', 'test', 'data'); +const acceptanceModelDir = path.resolve(dataDir, 'acceptance'); +const acceptanceMarkdownFile = path.resolve(dataDir, 'acceptance', 'sample.md'); +const acceptanceMarkdown = normalizeNLs(fs.readFileSync(acceptanceMarkdownFile, 'utf8')); +const acceptanceMarkdownCiceroFile = path.resolve(dataDir, 'acceptance', 'sample_cicero.md'); +const acceptanceMarkdownCicero = normalizeNLs(fs.readFileSync(acceptanceMarkdownCiceroFile, 'utf8')); +const acceptanceCommonMarkFile = path.resolve(dataDir, 'acceptance', 'commonmark.json'); +const acceptanceCiceroMarkFile = path.resolve(dataDir, 'acceptance', 'ciceromark.json'); +const acceptanceCiceroMark = JSON.parse(fs.readFileSync(acceptanceCiceroMarkFile, 'utf8')); +const acceptanceCiceroMarkParsedFile = path.resolve(dataDir, 'acceptance', 'ciceromark_parsed.json'); + +describe('#validateTransformArgs', () => { + it('no args specified', () => { + process.chdir(path.resolve(dataDir)); + const args = Commands.validateTransformArgs({ + _: ['transform'], + }); + expect(args.input).toMatch(/input.md$/); + }); + it('no args specified (verbose)', () => { + process.chdir(path.resolve(dataDir)); + const args = Commands.validateTransformArgs({ + _: ['transform'], + verbose: true, + }); + expect(args.input).toMatch(/input.md$/); + }); + it('all args specified', () => { + process.chdir(path.resolve(dataDir)); + const args = Commands.validateTransformArgs({ + _: ['transform'], + input: 'input.md', + }); + expect(args.input).toMatch(/input.md$/); + }); + it('bad input.md', () => { + process.chdir(path.resolve(dataDir)); + expect(() => Commands.validateTransformArgs({ + _: ['transform'], + input: 'input_en.md', + })).toThrow('A input.md file is required. Try the --input flag or create a input.md.'); + }); +}); + +describe('markdown-cli (acceptance)', () => { + beforeEach(() => { + // Touch the models directory so any missing-file failures are loud. + loadModels(acceptanceModelDir); + }); + + describe('#markdown_parse', () => { + it('should parse a markdown cicero file to CiceroMark', async () => { + const { result } = await Commands.transform(acceptanceMarkdownCiceroFile, 'markdown_cicero', [], 'ciceromark', null, {}, {}); + expect(result).toBe(JSON.stringify(acceptanceCiceroMark)); + }); + + it('should parse a markdown cicero file to CiceroMark (verbose)', async () => { + const { result } = await Commands.transform(acceptanceMarkdownCiceroFile, 'markdown_cicero', [], 'ciceromark', null, {}, { verbose: true }); + expect(result).toBe(JSON.stringify(acceptanceCiceroMark)); + }); + }); + + describe('#draft', () => { + it('should generate a markdown file from CommonMark', async () => { + const { result } = await Commands.transform(acceptanceCommonMarkFile, 'commonmark', [], 'markdown', null, {}, {}); + expect(result).toEqual(acceptanceMarkdown); + }); + + it('should generate a markdown cicero file from CiceroMark', async () => { + const { result } = await Commands.transform(acceptanceCiceroMarkParsedFile, 'ciceromark', [], 'markdown_cicero', null, {}, {}); + expect(result).toEqual(acceptanceMarkdownCicero); + }); + }); + + describe('#normalize', () => { + it('should roundtrip commonmark <-> markdown', async () => { + const { result } = await Commands.transform(acceptanceMarkdownFile, 'markdown', [], 'commonmark', null, {}, { roundtrip: true }); + expect(result).toEqual(acceptanceMarkdown); + }); + }); +}); diff --git a/packages/markdown-cli/src/cli.ts b/packages/markdown-cli/src/cli.ts new file mode 100644 index 00000000..118c77d4 --- /dev/null +++ b/packages/markdown-cli/src/cli.ts @@ -0,0 +1,99 @@ +#!/usr/bin/env node +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +// eslint-disable-next-line @typescript-eslint/no-var-requires +const { Logger: logger } = require('@accordproject/concerto-util'); +import { Commands } from './commands'; +// eslint-disable-next-line @typescript-eslint/no-var-requires +const yargs = require('yargs'); + +yargs + .scriptName('markus') + .usage('$0 [args]') + .demandCommand(1, '# Please specify a command') + .recommendCommands() + .strict() + .command('transform', 'transform between two formats', (yargs: any) => { + yargs.option('input', { describe: 'path to the input', type: 'string' }); + yargs.option('from', { describe: 'source format', type: 'string', default: 'markdown' }); + yargs.option('to', { describe: 'target format', type: 'string', default: 'commonmark' }); + yargs.option('via', { describe: 'intermediate formats', type: 'string', array: true, default: [] }); + yargs.option('roundtrip', { describe: 'roundtrip transform', type: 'boolean', default: false }); + yargs.option('output', { describe: 'path to the output file', type: 'string' }); + yargs.option('model', { describe: 'array of concerto model files', type: 'string', array: true }); + yargs.option('template', { describe: 'template grammar', type: 'string' }); + yargs.option('contract', { describe: 'contract template', type: 'boolean', default: false }); + yargs.option('currentTime', { describe: 'set current time', type: 'string', default: null }); + yargs.option('plugin', { describe: 'path to a parser plugin', type: 'string' }); + yargs.option('extension', { describe: 'path to a transform extension', type: 'string', array: true }); + yargs.option('sourcePos', { describe: 'enable source position', type: 'boolean', default: false }); + yargs.option('verbose', { describe: 'verbose output', type: 'boolean', default: false }); + yargs.option('offline', { describe: 'do not resolve external models', type: 'boolean', default: false }); + }, (argv: any) => { + if (argv.verbose) { + logger.info(`transform input ${argv.input} file`); + } + + try { + argv = Commands.validateTransformArgs(argv); + const parameters: any = {}; + parameters.inputFileName = argv.input; + parameters.model = argv.model; + let plugin: any = {}; + if (argv.plugin) { + plugin = require(path.resolve(process.cwd(), argv.plugin)); + } + const extensions: any[] = []; + if (argv.extension) { + argv.extension.forEach((thisExtension: string) => { + let modExtension = require(path.resolve(process.cwd(), thisExtension)); + if (modExtension.default) { + modExtension = modExtension.default; + } + extensions.push(modExtension); + }); + } + parameters.plugin = plugin; + parameters.template = argv.template; + parameters.templateKind = argv.contract ? 'contract' : 'clause'; + parameters.currentTime = argv.currentTime; + const options: any = {}; + options.verbose = argv.verbose; + options.sourcePos = argv.sourcePos; + options.roundtrip = argv.roundtrip; + options.offline = argv.offline; + options.extensions = extensions; + return Commands.transform(argv.input, argv.from, argv.via, argv.to, argv.output, parameters, options) + .then(({ result, targetFormat }) => { + if (result) { + if (targetFormat && targetFormat.fileFormat !== 'binary') { + logger.info('\n' + result); + } else { + logger.info(`\n`); + } + } + }) + .catch((err: any) => { + logger.error(err.message); + }); + } catch (err: any) { + logger.error(err.message); + return; + } + }) + .option('verbose', { alias: 'v', default: false }) + .help() + .parse(); diff --git a/packages/markdown-cli/lib/commands.js b/packages/markdown-cli/src/commands.ts similarity index 51% rename from packages/markdown-cli/lib/commands.js rename to packages/markdown-cli/src/commands.ts index 692c89af..cbf17aba 100644 --- a/packages/markdown-cli/lib/commands.js +++ b/packages/markdown-cli/src/commands.ts @@ -12,25 +12,19 @@ * limitations under the License. */ -'use strict'; - -const fs = require('fs'); -const logger = require('@accordproject/concerto-util').Logger; -const { TransformEngine, builtinTransformationGraph } = require('@accordproject/markdown-transform'); +import * as fs from 'fs'; +// eslint-disable-next-line @typescript-eslint/no-var-requires +const { Logger: logger } = require('@accordproject/concerto-util'); +import { TransformEngine, builtinTransformationGraph } from '@accordproject/markdown-transform'; /** * Utility class that implements the commands exposed by the CLI. - * @class */ -class Commands { +export class Commands { /** * Load an input file - * @param {*} engine - the transformation engine - * @param {string} filePath - the file name - * @param {string} format - the format - * @returns {*} the content - of the file */ - static loadFormatFromFile(engine,filePath,format) { + static loadFormatFromFile(engine: TransformEngine, filePath: string, format: string): any { const fileFormat = engine.formatDescriptor(format).fileFormat; if (fileFormat === 'json') { return JSON.parse(fs.readFileSync(filePath, 'utf8')); @@ -43,12 +37,8 @@ class Commands { /** * Prints a format to string - * @param {*} engine - the transformation engine - * @param {*} input - the input - * @param {string} format - the format - * @returns {string} the string representation */ - static printFormatToString(engine,input,format) { + static printFormatToString(engine: TransformEngine, input: any, format: string): string { const fileFormat = engine.formatDescriptor(format).fileFormat; if (fileFormat === 'json') { return JSON.stringify(input); @@ -59,35 +49,29 @@ class Commands { /** * Prints a format to file - * @param {*} engine - the transformation engine - * @param {*} input the input - * @param {string} format the format - * @param {string} filePath the file name */ - static printFormatToFile(engine,input,format,filePath) { + static printFormatToFile(engine: TransformEngine, input: any, format: string, filePath: string): void { logger.info('Creating file: ' + filePath); - fs.writeFileSync(filePath, Commands.printFormatToString(engine,input,format)); + fs.writeFileSync(filePath, Commands.printFormatToString(engine, input, format)); } /** * Set a default for a file argument - * - * @param {object} argv the inbound argument values object - * @param {string} argName the argument name - * @param {string} argDefaultName the argument default name - * @param {Function} argDefaultFun how to compute the argument default - * @returns {object} a modified argument object */ - static setDefaultFileArg(argv, argName, argDefaultName, argDefaultFun) { - if(!argv[argName]){ + static setDefaultFileArg( + argv: any, + argName: string, + argDefaultName: string, + argDefaultFun: (argv: any, name: string) => string, + ): any { + if (!argv[argName]) { logger.info(`Loading a default ${argDefaultName} file.`); argv[argName] = argDefaultFun(argv, argDefaultName); } - let argExists = true; - argExists = fs.existsSync(argv[argName]); + const argExists = fs.existsSync(argv[argName]); - if (!argExists){ + if (!argExists) { throw new Error(`A ${argDefaultName} file is required. Try the --${argName} flag or create a ${argDefaultName}.`); } else { return argv; @@ -96,14 +80,11 @@ class Commands { /** * Set default params before we transform - * - * @param {object} argv the inbound argument values object - * @returns {object} a modfied argument object */ - static validateTransformArgs(argv) { - argv = Commands.setDefaultFileArg(argv, 'input', 'input.md', ((argv, argDefaultName) => { return argDefaultName; })); + static validateTransformArgs(argv: any): any { + argv = Commands.setDefaultFileArg(argv, 'input', 'input.md', (_argv, name) => name); - if(argv.verbose) { + if (argv.verbose) { logger.info(`transform input ${argv.input} printing intermediate transformations.`); } @@ -112,25 +93,20 @@ class Commands { /** * Transform between formats - * - * @param {string} inputPath to the input file - * @param {string} from the source format - * @param {string[]} via intermediate formats - * @param {string} to the target format - * @param {string} outputPath to an output file - * @param {object} parameters the transform parameters - * @param {object} [options] configuration options - * @param {boolean} [options.verbose] verbose output - * @param {boolean} [options.roundtrip] roundtrip transform back to source format - * @returns {object} Promise to the result of parsing */ - static async transform(inputPath, from, via, to, outputPath, parameters, options) { - // Initialize the transform engine + static async transform( + inputPath: string, + from: string, + via: string[], + to: string, + outputPath: string | undefined, + parameters: any, + options: any, + ): Promise<{ result?: string; targetFormat?: any }> { const engine = new TransformEngine(builtinTransformationGraph); - // Get extensions - const { extensions, ...otherOptions } = options; + const { extensions, ...otherOptions } = options || {}; if (extensions) { - extensions.forEach((thisExtension) => { + extensions.forEach((thisExtension: any) => { engine.registerExtension(thisExtension); }); } @@ -150,15 +126,13 @@ class Commands { } if (outputPath) { - Commands.printFormatToFile(engine,result,finalFormat,outputPath); - return Promise.resolve({}); + Commands.printFormatToFile(engine, result, finalFormat, outputPath); + return {}; } - return Promise.resolve(Commands.printFormatToString(engine,result,finalFormat)) - .then((result) => { - const targetFormat = engine.formatDescriptor(finalFormat); - return { result, targetFormat }; - }); + const resultString = Commands.printFormatToString(engine, result, finalFormat); + const targetFormat = engine.formatDescriptor(finalFormat); + return { result: resultString, targetFormat }; } } -module.exports = Commands; +export default Commands; diff --git a/packages/markdown-it-template/index.js b/packages/markdown-cli/src/index.ts old mode 100755 new mode 100644 similarity index 83% rename from packages/markdown-it-template/index.js rename to packages/markdown-cli/src/index.ts index 664ae2f2..bc1d2cc2 --- a/packages/markdown-it-template/index.js +++ b/packages/markdown-cli/src/index.ts @@ -12,11 +12,7 @@ * limitations under the License. */ -'use strict'; +import { Commands } from './commands'; -/** - * Export the plugins - * @module markdown-it-template - */ - -module.exports = require('./lib'); +export { Commands }; +export default { Commands }; diff --git a/packages/markdown-cli/test/cli.js b/packages/markdown-cli/test/cli.js deleted file mode 100644 index 9cc3219e..00000000 --- a/packages/markdown-cli/test/cli.js +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -const chai = require('chai'); -const fs = require('fs'); -const path = require('path'); - -chai.should(); -chai.use(require('chai-things')); -chai.use(require('chai-as-promised')); - -const Commands = require('../lib/commands'); - -/** - * Prepare the text for parsing (normalizes new lines, etc) - * @param {string} input - the text for the clause - * @return {string} - the normalized text for the clause - */ -function normalizeNLs(input) { - // we replace all \r and \n with \n - let text = input.replace(/\r/gm,''); - return text; -} - -/** - * Load models - * @param {string} dir - a directory - * @return {*} the list of model files - */ -function loadModels(dir) { - const files = fs.readdirSync(dir); - const ctoFiles = files.filter((file) => path.extname(file) === '.cto'); - const ctoPaths = ctoFiles.map((file) => path.join(dir, file)); - return ctoPaths; -} - -// Acceptance test -const acceptanceGrammarFile = path.resolve(__dirname, 'data/acceptance', 'grammar.tem.md'); -const acceptanceModelDir = path.resolve(__dirname, 'data/acceptance'); -const acceptanceMarkdownFile = path.resolve(__dirname, 'data/acceptance', 'sample.md'); -const acceptanceMarkdown = normalizeNLs(fs.readFileSync(acceptanceMarkdownFile, 'utf8')); -const acceptanceMarkdownCiceroFile = path.resolve(__dirname, 'data/acceptance', 'sample_cicero.md'); -const acceptanceMarkdownCicero = normalizeNLs(fs.readFileSync(acceptanceMarkdownCiceroFile, 'utf8')); -const acceptanceCommonMarkFile = path.resolve(__dirname, 'data/acceptance', 'commonmark.json'); -const acceptanceCiceroMarkFile = path.resolve(__dirname, 'data/acceptance', 'ciceromark.json'); -const acceptanceCiceroMark = JSON.parse(fs.readFileSync(acceptanceCiceroMarkFile, 'utf8')); -const acceptanceCiceroMarkParsedFile = path.resolve(__dirname, 'data/acceptance', 'ciceromark_parsed.json'); - -describe('#validateTransformArgs', () => { - it('no args specified', () => { - process.chdir(path.resolve(__dirname, 'data/')); - const args = Commands.validateTransformArgs({ - _: ['transform'], - }); - args.input.should.match(/input.md$/); - }); - it('no args specified (verbose)', () => { - process.chdir(path.resolve(__dirname, 'data/')); - const args = Commands.validateTransformArgs({ - _: ['transform'], - verbose: true - }); - args.input.should.match(/input.md$/); - }); - it('all args specified', () => { - process.chdir(path.resolve(__dirname, 'data/')); - const args = Commands.validateTransformArgs({ - _: ['transform'], - input: 'input.md' - }); - args.input.should.match(/input.md$/); - }); - it('bad input.md', () => { - process.chdir(path.resolve(__dirname, 'data/')); - (() => Commands.validateTransformArgs({ - _: ['transform'], - input: 'input_en.md' - })).should.throw('A input.md file is required. Try the --input flag or create a input.md.'); - }); -}); - -describe('markdown-cli (acceptance)', () => { - // eslint-disable-next-line no-unused-vars - let parameters; - beforeEach(async () => { - const models = loadModels(acceptanceModelDir); - parameters = { template: acceptanceGrammarFile, model: models, templateKind: 'contract' }; - }); - - describe('#markdown_parse', () => { - it('should parse a markdown cicero file to CiceroMark', async () => { - const { result } = await Commands.transform(acceptanceMarkdownCiceroFile, 'markdown_cicero', [], 'ciceromark', null, {}, {}); - result.should.equal(JSON.stringify(acceptanceCiceroMark)); - }); - - it('should parse a markdown cicero file to CiceroMark (verbose)', async () => { - const { result } = await Commands.transform(acceptanceMarkdownCiceroFile, 'markdown_cicero', [], 'ciceromark', null, {}, {verbose:true}); - result.should.equal(JSON.stringify(acceptanceCiceroMark)); - }); - }); - - describe('#draft', () => { - it('should generate a markdown file from CommonMark', async () => { - const { result } = await Commands.transform(acceptanceCommonMarkFile, 'commonmark', [], 'markdown', null, {}, {}); - result.should.eql(acceptanceMarkdown); - }); - - it('should generate a markdown cicero file from CiceroMark', async () => { - const { result } = await Commands.transform(acceptanceCiceroMarkParsedFile, 'ciceromark', [], 'markdown_cicero', null, {}, {}); - result.should.eql(acceptanceMarkdownCicero); - }); - }); - - describe('#normalize', () => { - it('should roundtrip commonmark <-> markdown', async () => { - const { result } = await Commands.transform(acceptanceMarkdownFile, 'markdown', [], 'commonmark', null, {}, {roundtrip:true}); - result.should.eql(acceptanceMarkdown); - }); - }); -}); diff --git a/packages/markdown-cli/test/extension/toc.js b/packages/markdown-cli/test/extension/toc.js deleted file mode 100644 index 42e63cf1..00000000 --- a/packages/markdown-cli/test/extension/toc.js +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -// What nodes should we keep in a table of contents? -const keepContent = ['Heading']; -const keepNode = ['Document']; - -/** - * Create a table of contents in commonmark format - * @param {object} input - the commonmark document - * @return {object} - the table of contents - */ -function createToc(input) { - const result = Object.assign({}, input); - const typeName = input.$class.split('.').pop(); - if (keepContent.some((name) => typeName === name)) { - return [result]; - } - if (keepNode.some((name) => typeName === name)) { - result.nodes = input.nodes.flatMap(createToc); - return [result]; - } - return []; -} - -module.exports = { - format: { - name: 'toc', - docs: 'table of contents', - fileFormat: 'utf8' - }, - transforms: { - // Transform commonmark into a table of contents - commonmark: { - toc: (input, parameters, options) => createToc(input)[0], - }, - // Transform a table of contents back into commonmark - toc: { - commonmark: ((input, parameteres, options) => input), - }, - } -}; diff --git a/packages/markdown-cli/tsconfig.json b/packages/markdown-cli/tsconfig.json index d2e666ea..fb3e9f82 100644 --- a/packages/markdown-cli/tsconfig.json +++ b/packages/markdown-cli/tsconfig.json @@ -1,10 +1,11 @@ { + "extends": "../../tsconfig.base.json", "compilerOptions": { - "allowJs": true, + "rootDir": "src", + "outDir": "lib", "declaration": true, - "emitDeclarationOnly": true, - "outDir": "types", - "strict": false + "sourceMap": true }, - "include": ["index.js", "lib/**/*.js"] + "include": ["src/**/*.ts"], + "exclude": ["src/**/*.test.ts", "lib", "node_modules"] } diff --git a/packages/markdown-cli/tsconfig.test.json b/packages/markdown-cli/tsconfig.test.json new file mode 100644 index 00000000..58810225 --- /dev/null +++ b/packages/markdown-cli/tsconfig.test.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "rootDir": ".", + "noEmit": true + }, + "include": ["src/**/*.ts"], + "exclude": ["lib", "node_modules"] +} diff --git a/packages/markdown-cli/types/index.d.ts b/packages/markdown-cli/types/index.d.ts deleted file mode 100644 index b7988016..00000000 --- a/packages/markdown-cli/types/index.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -#!/usr/bin/env node -export {}; diff --git a/packages/markdown-cli/types/lib/commands.d.ts b/packages/markdown-cli/types/lib/commands.d.ts deleted file mode 100644 index 35e84b8d..00000000 --- a/packages/markdown-cli/types/lib/commands.d.ts +++ /dev/null @@ -1,66 +0,0 @@ -export = Commands; -/** - * Utility class that implements the commands exposed by the CLI. - * @class - */ -declare class Commands { - /** - * Load an input file - * @param {*} engine - the transformation engine - * @param {string} filePath - the file name - * @param {string} format - the format - * @returns {*} the content - of the file - */ - static loadFormatFromFile(engine: any, filePath: string, format: string): any; - /** - * Prints a format to string - * @param {*} engine - the transformation engine - * @param {*} input - the input - * @param {string} format - the format - * @returns {string} the string representation - */ - static printFormatToString(engine: any, input: any, format: string): string; - /** - * Prints a format to file - * @param {*} engine - the transformation engine - * @param {*} input the input - * @param {string} format the format - * @param {string} filePath the file name - */ - static printFormatToFile(engine: any, input: any, format: string, filePath: string): void; - /** - * Set a default for a file argument - * - * @param {object} argv the inbound argument values object - * @param {string} argName the argument name - * @param {string} argDefaultName the argument default name - * @param {Function} argDefaultFun how to compute the argument default - * @returns {object} a modified argument object - */ - static setDefaultFileArg(argv: object, argName: string, argDefaultName: string, argDefaultFun: Function): object; - /** - * Set default params before we transform - * - * @param {object} argv the inbound argument values object - * @returns {object} a modfied argument object - */ - static validateTransformArgs(argv: object): object; - /** - * Transform between formats - * - * @param {string} inputPath to the input file - * @param {string} from the source format - * @param {string[]} via intermediate formats - * @param {string} to the target format - * @param {string} outputPath to an output file - * @param {object} parameters the transform parameters - * @param {object} [options] configuration options - * @param {boolean} [options.verbose] verbose output - * @param {boolean} [options.roundtrip] roundtrip transform back to source format - * @returns {object} Promise to the result of parsing - */ - static transform(inputPath: string, from: string, via: string[], to: string, outputPath: string, parameters: object, options?: { - verbose?: boolean; - roundtrip?: boolean; - }): object; -} diff --git a/packages/markdown-common/.eslintrc.cjs b/packages/markdown-common/.eslintrc.cjs new file mode 100644 index 00000000..b1717c41 --- /dev/null +++ b/packages/markdown-common/.eslintrc.cjs @@ -0,0 +1,60 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +module.exports = { + root: true, + env: { + es2022: true, + node: true, + jest: true, + }, + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + ], + parser: '@typescript-eslint/parser', + parserOptions: { + ecmaVersion: 2022, + sourceType: 'module', + }, + plugins: ['@typescript-eslint'], + ignorePatterns: [ + 'node_modules/', + 'lib/', + 'coverage/', + '**/*.snap', + 'src/externalModels/', + ], + rules: { + 'indent': ['error', 4, { 'SwitchCase': 1 }], + 'linebreak-style': ['warn', 'unix'], + 'quotes': ['error', 'single', { 'avoidEscape': true, 'allowTemplateLiterals': true }], + 'semi': ['error', 'always'], + 'no-console': 'warn', + 'curly': 'error', + 'eqeqeq': 'error', + 'no-throw-literal': 'error', + 'no-var': 'error', + 'no-tabs': 'error', + 'no-trailing-spaces': 'error', + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-var-requires': 'off', + '@typescript-eslint/no-unused-vars': ['error', { 'args': 'none', 'ignoreRestSiblings': true, 'caughtErrors': 'none' }], + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/no-empty-function': 'off', + '@typescript-eslint/ban-ts-comment': 'off', + '@typescript-eslint/no-this-alias': 'off', + 'no-unused-vars': 'off', + }, +}; diff --git a/packages/markdown-common/.eslintrc.yml b/packages/markdown-common/.eslintrc.yml deleted file mode 100644 index ec0c5d88..00000000 --- a/packages/markdown-common/.eslintrc.yml +++ /dev/null @@ -1,47 +0,0 @@ -env: - es6: true - node: true - mocha: true -extends: 'eslint:recommended' -parserOptions: - ecmaVersion: 12 - sourceType: 'script' -rules: - indent: - - error - - 4 - linebreak-style: - - warn - - unix - quotes: - - error - - single - semi: - - error - - always - no-unused-vars: - - error - - args: none - no-console: warn - curly: error - eqeqeq: error - no-throw-literal: error - strict: error - no-var: error - dot-notation: error - no-tabs: error - no-trailing-spaces: error - # no-use-before-define: error - no-useless-call: error - no-with: error - operator-linebreak: error - require-jsdoc: - - error - - require: - ClassDeclaration: true - MethodDefinition: true - FunctionDeclaration: true - valid-jsdoc: - - error - - requireReturn: false - yoda: error diff --git a/packages/markdown-common/.gitignore b/packages/markdown-common/.gitignore index a6cf488d..4bd86293 100644 --- a/packages/markdown-common/.gitignore +++ b/packages/markdown-common/.gitignore @@ -15,6 +15,7 @@ pids /out /dist /umd +/lib # Directory for instrumented libs generated by jscoverage/JSCover lib-cov diff --git a/packages/markdown-common/README.md b/packages/markdown-common/README.md index 13d0a4fd..b94f3a19 100644 --- a/packages/markdown-common/README.md +++ b/packages/markdown-common/README.md @@ -1,100 +1,108 @@ # CommonMark Transformer -Converts markdown text to/from a DOM. +Converts markdown text to/from a CommonMark DOM. -## Usage +The CommonMark DOM is a Concerto model — see [`commonmark@0.5.0`](https://models.accordproject.org/markdown/commonmark@0.5.0.html). -To transform markdown text, first install the `markdown-common` package: +## Installation ``` -npm install @accordproject/markdown-common --save +npm install @accordproject/markdown-common ``` -Then in your JavaScript code: +Peer dependency: `@accordproject/concerto-core@^4.1.3`. -``` javascript -const CommonMarkTransformer = require('@accordproject/markdown-common').CommonMarkTransformer; -const transformer = new CommonMarkTransformer({ tagInfo : true }); -const json = transformer.fromMarkdown('# Heading\n\nThis is some `code`.\n\nFin.', 'json'); -console.log(JSON.stringify(json, null, 4)); -``` +## Usage -The output should be: +```ts +import { CommonMarkTransformer } from '@accordproject/markdown-common'; -``` json -{ - "$class": "org.accordproject.commonmark@0.5.0.Document", - "xmlns": "http://commonmark.org/xml/1.0", - "nodes": [ - { - "$class": "org.accordproject.commonmark@0.5.0.Heading", - "level": "1", - "nodes": [ - { - "$class": "org.accordproject.commonmark@0.5.0.Text", - "text": "Heading" - } - ] - }, - { - "$class": "org.accordproject.commonmark@0.5.0.Paragraph", - "nodes": [ - { - "$class": "org.accordproject.commonmark@0.5.0.Text", - "text": "This is some " - }, - { - "$class": "org.accordproject.commonmark@0.5.0.Code", - "text": "code" - }, - { - "$class": "org.accordproject.commonmark@0.5.0.Text", - "text": "." - } - ] - }, - { - "$class": "org.accordproject.commonmark@0.5.0.Paragraph", - "nodes": [ - { - "$class": "org.accordproject.commonmark@0.5.0.Text", - "text": "Fin." - } - ] - } - ] - } +const transformer = new CommonMarkTransformer(); +const dom = transformer.fromMarkdown('# Heading\n\nThis is some `code`.\n\nFin.'); +console.log(JSON.stringify(dom, null, 4)); ``` -Please refer to the [schema](https://models.accordproject.org/commonmark/markdown.html) for the details of all the the nodes that you should expect in the DOM. +CommonJS works too: + +```js +const { CommonMarkTransformer } = require('@accordproject/markdown-common'); +``` -You can then manipulate the DOM object, making any required changes: +The output is: -``` javascript -json.nodes[0].nodes[0].text = 'My New Heading'; +```json +{ + "$class": "org.accordproject.commonmark@0.5.0.Document", + "xmlns": "http://commonmark.org/xml/1.0", + "nodes": [ + { + "$class": "org.accordproject.commonmark@0.5.0.Heading", + "level": "1", + "nodes": [ + { + "$class": "org.accordproject.commonmark@0.5.0.Text", + "text": "Heading" + } + ] + }, + { + "$class": "org.accordproject.commonmark@0.5.0.Paragraph", + "nodes": [ + { + "$class": "org.accordproject.commonmark@0.5.0.Text", + "text": "This is some " + }, + { + "$class": "org.accordproject.commonmark@0.5.0.Code", + "text": "code" + }, + { + "$class": "org.accordproject.commonmark@0.5.0.Text", + "text": "." + } + ] + }, + { + "$class": "org.accordproject.commonmark@0.5.0.Paragraph", + "nodes": [ + { + "$class": "org.accordproject.commonmark@0.5.0.Text", + "text": "Fin." + } + ] + } + ] +} ``` -Finally converting the DOM back into a markdown string: +You can manipulate the DOM and convert it back to markdown: -``` javascript -const newMarkdown = commonMark.toMarkdown(json); +```ts +dom.nodes[0].nodes[0].text = 'My New Heading'; +const newMarkdown = transformer.toMarkdown(dom); console.log(newMarkdown); ``` The new markdown string will be: -``` markdown +```markdown My New Heading ==== - + This is some `code`. - + Fin. ``` -> Note how the original H1 heading has been normalized during conversion from `#` syntax to `====` syntax. In commonmark these are equivalent. +> Note how the H1 heading has been normalized during conversion from `#` syntax to `====` syntax. In CommonMark these are equivalent. + +## What this package exports + +- `CommonMarkTransformer` — markdown ↔ CommonMark DOM +- `ToMarkdownVisitor`, `FromCommonMarkVisitor`, `FromMarkdownIt` — lower-level building blocks +- `CommonMarkUtils` — helpers for rendering markdown (escaping, list prefixes, etc.) +- `Stack` — generic stack used by the visitors +- `CommonMarkModel`, `CiceroMarkModel`, `ConcertoMetaModel`, `TemplateMarkModel` — Concerto namespace/MODEL strings consumed by the other packages in this monorepo ## License Accord Project source code files are made available under the Apache License, Version 2.0 (Apache-2.0), located in the LICENSE file. Accord Project documentation files are made available under the Creative Commons Attribution 4.0 International License (CC-BY-4.0), available at http://creativecommons.org/licenses/by/4.0/. - -© 2017-2019 Clause, Inc. diff --git a/packages/markdown-common/index.js b/packages/markdown-common/index.js deleted file mode 100644 index 2c7f917e..00000000 --- a/packages/markdown-common/index.js +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -/** - * Export the framework and plugins - * @module markdown-common - */ - -module.exports.Stack = require('./lib/Stack'); - -// models -module.exports.CommonMarkModel = require('./lib/externalModels/CommonMarkModel'); -module.exports.CiceroMarkModel = require('./lib/externalModels/CiceroMarkModel'); -module.exports.ConcertoMetaModel = require('./lib/externalModels/ConcertoMetaModel'); -module.exports.TemplateMarkModel = require('./lib/externalModels/TemplateMarkModel'); - -module.exports.CommonMarkUtils = require('./lib/CommonMarkUtils'); -module.exports.FromCommonMarkVisitor = require('./lib/FromCommonMarkVisitor'); -module.exports.fromcommonmarkrules = require('./lib/fromcommonmarkrules'); - -module.exports.CommonMarkTransformer = require('./lib/CommonMarkTransformer'); -module.exports.ToMarkdownVisitor = require('./lib/ToMarkdownVisitor'); -module.exports.FromMarkdownIt = require('./lib/FromMarkdownIt'); diff --git a/packages/markdown-common/jest.config.js b/packages/markdown-common/jest.config.js index 958bd62d..c8c592ed 100644 --- a/packages/markdown-common/jest.config.js +++ b/packages/markdown-common/jest.config.js @@ -13,185 +13,18 @@ */ 'use strict'; -// For a detailed explanation regarding each configuration property, visit: -// https://jestjs.io/docs/en/configuration.html +/** @type {import('jest').Config} */ module.exports = { - // All imported modules in your tests should be mocked automatically - // automock: false, - - // Stop running tests after `n` failures - // bail: 0, - - // Respect "browser" field in package.json when resolving modules - // browser: false, - - // The directory where Jest should store its cached dependency information - // cacheDirectory: "/private/var/folders/tv/4ljndl3s2jg90nxd8h7f3bgr0000gn/T/jest_dx", - - // Automatically clear mock calls and instances between every test + preset: 'ts-jest', + testEnvironment: 'node', clearMocks: true, - - // Indicates whether the coverage information should be collected while executing the test - // collectCoverage: false, - - // An array of glob patterns indicating a set of files for which coverage information should be collected - collectCoverageFrom: [ 'lib/**/*.js' ], - - // The directory where Jest should output its coverage files + testMatch: ['/src/**/*.test.ts'], + collectCoverageFrom: ['src/**/*.ts', '!src/**/*.test.ts', '!src/**/*.d.ts'], coverageDirectory: 'coverage', - - // An array of regexp pattern strings used to skip coverage collection - coveragePathIgnorePatterns: [ - '/node_modules/' - ], - - // A list of reporter names that Jest uses when writing coverage reports - coverageReporters: [ - 'json', - 'text', - 'lcov', - 'html' - ], - - // An object that configures minimum threshold enforcement for coverage results - // coverageThreshold: null, - - // A path to a custom dependency extractor - // dependencyExtractor: null, - - // Make calling deprecated APIs throw helpful error messages - // errorOnDeprecated: false, - - // Force coverage collection from ignored files using an array of glob patterns - // forceCoverageMatch: [], - - // A path to a module which exports an async function that is triggered once before all test suites - // globalSetup: null, - - // A path to a module which exports an async function that is triggered once after all test suites - // globalTeardown: null, - - // A set of global variables that need to be available in all test environments - // globals: {}, - - // An array of directory names to be searched recursively up from the requiring module's location - // moduleDirectories: [ - // "node_modules" - // ], - - // An array of file extensions your modules use - // moduleFileExtensions: [ - // "js", - // "json", - // "jsx", - // "ts", - // "tsx", - // "node" - // ], - - // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader - // modulePathIgnorePatterns: [], - - // Activates notifications for test results - // notify: false, - - // An enum that specifies notification mode. Requires { notify: true } - // notifyMode: "failure-change", - - // A preset that is used as a base for Jest's configuration - // preset: null, - - // Run tests from one or more projects - // projects: null, - - // Use this configuration option to add custom reporters to Jest - // reporters: undefined, - - // Automatically reset mock state between every test - // resetMocks: false, - - // Reset the module registry before running each individual test - // resetModules: false, - - // A path to a custom resolver - // resolver: null, - - // Automatically restore mock state between every test - // restoreMocks: false, - - // The root directory that Jest should scan for tests and modules within - // rootDir: null, - - // A list of paths to directories that Jest should use to search for files in - // roots: [ - // "" - // ], - - // Allows you to use a custom runner instead of Jest's default test runner - // runner: "jest-runner", - - // The paths to modules that run some code to configure or set up the testing environment before each test - // setupFiles: [], - - // A list of paths to modules that run some code to configure or set up the testing framework before each test - // setupFilesAfterEnv: [], - - // A list of paths to snapshot serializer modules Jest should use for snapshot testing - // snapshotSerializers: [], - - // The test environment that will be used for testing - testEnvironment: 'node', - - // Options that will be passed to the testEnvironment - // testEnvironmentOptions: {}, - - // Adds a location field to test results - // testLocationInResults: false, - - // The glob patterns Jest uses to detect test files - // testMatch: [ - // "**/__tests__/**/*.[jt]s?(x)", - // "**/?(*.)+(spec|test).[tj]s?(x)" - // ], - - // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped - // testPathIgnorePatterns: [ - // "/node_modules/" - // ], - - // The regexp pattern or array of patterns that Jest uses to detect test files - // testRegex: [], - - // This option allows the use of a custom results processor - // testResultsProcessor: null, - - // This option allows use of a custom test runner - // testRunner: "jasmine2", - - // This option sets the URL for the jsdom environment. It is reflected in properties such as location.href - // testURL: "http://localhost", - - // Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout" - // timers: "real", - - // A map from regular expressions to paths to transformers - // transform: null, - - // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation - // transformIgnorePatterns: [ - // "/node_modules/" - // ], - - // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them - // unmockedModulePathPatterns: undefined, - - // Indicates whether each individual test should be reported during the run - // verbose: null, - - // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode - // watchPathIgnorePatterns: [], - - // Whether to use watchman for file crawling - // watchman: true, + coveragePathIgnorePatterns: ['/node_modules/'], + coverageReporters: ['json', 'text', 'lcov', 'html'], + transform: { + '^.+\\.tsx?$': ['ts-jest', { tsconfig: '/tsconfig.test.json' }], + }, }; diff --git a/packages/markdown-common/jsdoc.json b/packages/markdown-common/jsdoc.json deleted file mode 100644 index 0e564706..00000000 --- a/packages/markdown-common/jsdoc.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "tags": { - "allowUnknownTags": true, - "dictionaries": ["jsdoc", "closure"] - }, - "source": { - "include": [ - "./lib", - "./index.js" - ], - "includePattern": ".+\\.js(doc|x)?$" - }, - "plugins": ["plugins/markdown"], - "templates": { - "logoFile": "", - "cleverLinks": false, - "monospaceLinks": false, - "dateFormat": "ddd MMM Do YYYY", - "outputSourceFiles": true, - "outputSourcePath": true, - "systemName": "Accord Project Cicero SDK", - "footer": "", - "copyright": "Released under the Apache License v2.0", - "navType": "vertical", - "theme": "spacelab", - "linenums": true, - "collapseSymbols": false, - "inverseNav": true, - "protocol": "html://", - "methodHeadingReturns": false - }, - "markdown": { - "parser": "gfm", - "hardwrap": true - } -} \ No newline at end of file diff --git a/packages/markdown-common/lib/CommonMarkUtils.js b/packages/markdown-common/lib/CommonMarkUtils.js deleted file mode 100644 index acd2ad32..00000000 --- a/packages/markdown-common/lib/CommonMarkUtils.js +++ /dev/null @@ -1,357 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -const DOMParser = require('@xmldom/xmldom').DOMParser; -const CommonMarkModel = require('./externalModels/CommonMarkModel'); - -/** - * CommonMark Utilities - */ - -/** - * Initial block stack - * @return {*} the block stack - */ -function blocksInit() { - return { - first: true, - blocks: [], - }; -} - -/** - * Next node - * @param {*} stack the current block stack - */ -function blocksNextNode(stack) { - stack.first = false; -} - -/** - * enter block - * @param {*} stack the current block stack - * @param {string} blockType the block type - * @param {*} setFirst whether entering this block should set first - * @return {*} the block stack - */ -function blocksEnterBlock(stack, blockType, setFirst) { - let newStack = {}; - if(setFirst(blockType)) { - newStack.first = true; - } else { - newStack.first = stack.first; - } - newStack.blocks = stack.blocks.slice(); - newStack.blocks.push(blockType); - return newStack; -} - -/** - * Create a prefix within an existing line - * @param {Array} blocks - the ancestor blocks - * @return {string} the prefix to add to the current line - */ -function prefixInLine(blocks) { - let prefix = ''; - for (let i = blocks.length-1; i >= 0; i--) { - if (blocks[i] === 'Item' || blocks[i] === 'ListBlockDefinition') { - break; - } else if (blocks[i] === 'BlockQuote') { - prefix = '> ' + prefix; - } - } - return prefix; -} - -/** - * Create a new line - * @param {Array} blocks - the ancestor blocks - * @return {string} the prefixed new line - */ -function newLine(blocks) { - let prefix = ''; - for (let i = blocks.length-1; i >= 0; i--) { - if (blocks[i] === 'Item' || blocks[i] === 'ListBlockDefinition') { - prefix = ' ' + prefix; - } else if (blocks[i] === 'BlockQuote') { - prefix = '> ' + prefix; - } - } - return '\n' + prefix; -} - -/** - * Next node - * @param {*} parameters the current parameters - */ -function nextNode(parameters) { - blocksNextNode(parameters.stack); - if(parameters.index) { - parameters.index++; - } -} - -/** - * Set parameters for general blocks - * @param {*} ast - the current ast node - * @param {*} parametersOut - the current parameters - * @param {*} init - initial result value - * @param {*} setFirst whether entering this block should set first - * @return {*} the new parameters with block quote level incremented - */ -function mkParameters(ast, parametersOut, init, setFirst) { - let parameters = Object.assign({},parametersOut); // This is important to allow extra parameters to be passed - parameters.result = init; - parameters.stack = blocksEnterBlock(parametersOut.stack,ast.getType(),setFirst); - if(ast.getType() === 'List') { - parameters.indexInit = ast.start ? parseInt(ast.start) : 1; // Initial index - parameters.index = parameters.indexInit; // Current index - parameters.tight = ast.tight; // Tight or loose list - parameters.type = ast.type; // ordered or bulleted list - } - return parameters; -} - -/** - * Create a line prefix - * @param {*} parameters - the parameters - * @param {*} nb - number of newlines - * @return {string} the prefix - */ -function mkPrefix(parameters, nb) { - const stack = parameters.stack; - if (stack.first) { - return prefixInLine(stack.blocks); - } else { - const nl = newLine(stack.blocks); - return nl.repeat(nb); - } -} - -/** - * Create a single new line - * @param {*} parameters - the parameters - * @return {string} the prefix - */ -function mkNewLine(parameters) { - const stack = parameters.stack; - return newLine(stack.blocks); -} - -/** - * Create Setext heading - * @param {number} level - the heading level - * @return {string} the markup for the heading - */ -function mkSetextHeading(level) { - if (level === 1) { - return '===='; - } else { - return '----'; - } -} - -/** - * Create ATX heading - * @param {number} level - the heading level - * @return {string} the markup for the heading - */ -function mkATXHeading(level) { - return Array(level).fill('#').join(''); -} - -/** - * Create table heading - * @param {number} col - the number of columns - * @return {string} the markup for the table heading - */ -function mkTableHeading(col) { - return Array(col).fill('|---------').join('') + '|'; -} - -/** - * Adding escapes for text nodes - * @param {string} input - unescaped - * @return {string} escaped - */ -function escapeText(input) { - return input.replace(/[*`&>]/g, '\\$&') // Replaces special characters - .replace(/^(#+) /g, '\\$1 ') // Replaces heading markers - .replace(/^(\d+)\. /g, '$1\\. ') // Replaces ordered list markers - .replace(/^- /g, '\\- ') // Replaces unordered list markers - .replace(/^_/g, '\\_'); // Replaces thematic break markers -} - -/** - * Adding escapes for code blocks - * @param {string} input - unescaped - * @return {string} escaped - */ -function escapeCodeBlock(input) { - return input.replace(/`/g, '\\`'); -} - -/** - * Removing escapes - * @param {string} input - escaped - * @return {string} unescaped - */ -function unescapeCodeBlock(input) { - return input.replace(/\\`/g, '`'); -} - -/** - * Parses an HTML block and extracts the attributes, tag name and tag contents. - * Note that this will return null for strings like this: - * @param {string} string - the HTML block to parse - * @return {Object} - a tag object that holds the data for the html block - */ -function parseHtmlBlock(string) { - try { - const doc = (new DOMParser()).parseFromString(string, 'text/html'); - const item = doc.childNodes[0]; - const attributes = item.attributes; - const attributeObject = {}; - let attributeString = ''; - - for (let i = 0; i < attributes.length; i += 1) { - attributeString += `${attributes[i].name} = "${attributes[i].value}" `; - attributeObject[attributes[i].name] = attributes[i].value; - } - - const tag = {}; - tag.$class = `${CommonMarkModel.NAMESPACE}.TagInfo`; - tag.tagName = item.tagName.toLowerCase(); - tag.attributeString = attributeString; - tag.attributes = []; - for (const attName in attributeObject) { - if (Object.prototype.hasOwnProperty.call(attributeObject, attName)) { - const attValue = attributeObject[attName]; - tag.attributes.push({ - $class : `${CommonMarkModel.NAMESPACE}.Attribute`, - name : attName, - value : attValue, - }); - } - } - tag.content = item.textContent; - tag.closed = string.endsWith('/>'); - - return tag; - } catch (err) { - // no children, so we return null - return null; - } -} - -/** - * Merge adjacent Html nodes in a list of nodes - * @param {[*]} nodes - a list of nodes - * @param {boolean} tagInfo - whether to extract Html tags - * @returns {*} a new list of nodes with open/closed Html nodes merged - */ -function mergeAdjacentHtmlNodes(nodes, tagInfo) { - const result = []; - for(let n=0; n < nodes.length; n++) { - const cur = nodes[n]; - const next = n+1 < nodes.length ? nodes[n+1] : null; - - if(next && - cur.$class === (`${CommonMarkModel.NAMESPACE}.HtmlInline`) && - next.$class === (`${CommonMarkModel.NAMESPACE}.HtmlInline`) && - cur.tag && - next.text === ``) { - next.text = cur.text + next.text; // Fold text in next node, skip current node - next.tag = tagInfo ? parseHtmlBlock(next.text) : null; - } else { - result.push(cur); - } - } - return result; -} - -/** - * Determine the heading level - * - * @param {string} tag the heading tag - * @returns {string} the heading level - */ -function headingLevel(tag) { - switch(tag) { - case 'h1' : return '1'; - case 'h2' : return '2'; - case 'h3' : return '3'; - case 'h4' : return '4'; - case 'h5' : return '5'; - default: return '6'; - } -} - -/** - * Get an attribute value - * - * @param {*} attrs open ordered list attributes - * @param {string} name attribute name - * @param {*} def a default value - * @returns {string} the initial index - */ -function getAttr(attrs,name,def) { - if (attrs) { - const startAttrs = attrs.filter((x) => x[0] === name); - if (startAttrs[0]) { - return '' + startAttrs[0][1]; - } else { - return def; - } - } else { - return def; - } -} - -/** - * Trim single ending newline - * - * @param {string} text the input text - * @returns {string} the trimmed text - */ -function trimEndline(text) { - if (text.charAt(text.length-1) && text.charAt(text.length-1) === '\n') { - return text.substring(0,text.length-1); - } else { - return text; - } -} - -module.exports.blocksInit = blocksInit; - -module.exports.nextNode = nextNode; -module.exports.mkParameters = mkParameters; -module.exports.mkNewLine = mkNewLine; -module.exports.mkPrefix = mkPrefix; -module.exports.mkSetextHeading = mkSetextHeading; -module.exports.mkATXHeading = mkATXHeading; -module.exports.mkTableHeading = mkTableHeading; - -module.exports.escapeText = escapeText; -module.exports.escapeCodeBlock = escapeCodeBlock; -module.exports.unescapeCodeBlock = unescapeCodeBlock; - -module.exports.parseHtmlBlock = parseHtmlBlock; -module.exports.mergeAdjacentHtmlNodes = mergeAdjacentHtmlNodes; - -module.exports.headingLevel = headingLevel; -module.exports.getAttr = getAttr; -module.exports.trimEndline = trimEndline; diff --git a/packages/markdown-common/lib/externalModels/.gitignore b/packages/markdown-common/lib/externalModels/.gitignore deleted file mode 100644 index d965fc24..00000000 --- a/packages/markdown-common/lib/externalModels/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*Model.js diff --git a/packages/markdown-common/lib/externalModels/.npmignore b/packages/markdown-common/lib/externalModels/.npmignore deleted file mode 100644 index 8b137891..00000000 --- a/packages/markdown-common/lib/externalModels/.npmignore +++ /dev/null @@ -1 +0,0 @@ - diff --git a/packages/markdown-common/lib/externalModels/README.md b/packages/markdown-common/lib/externalModels/README.md deleted file mode 100644 index 0a0406ba..00000000 --- a/packages/markdown-common/lib/externalModels/README.md +++ /dev/null @@ -1,6 +0,0 @@ -# Warning - -The *Model.js files of this directory are populated during build. Refrain from adding files to this directory by hand. - -## License -Accord Project source code files are made available under the Apache License, Version 2.0 (Apache-2.0), located in the LICENSE file. Accord Project documentation files are made available under the Creative Commons Attribution 4.0 International License (CC-BY-4.0), available at http://creativecommons.org/licenses/by/4.0/. \ No newline at end of file diff --git a/packages/markdown-common/lib/fromcommonmarkrules.js b/packages/markdown-common/lib/fromcommonmarkrules.js deleted file mode 100644 index c1c6adf4..00000000 --- a/packages/markdown-common/lib/fromcommonmarkrules.js +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -const CommonMarkUtils = require('./CommonMarkUtils'); - -/** - * @typedef {Function} RuleFunction - * @param {*} visitor - * @param {*} thing - * @param {*} children - * @param {*} parameters - * @param {*} resultString - * @param {*} resultSeq - */ - -/** - * get text from a thing - * @param {object} thing - the thing - * @param {string} field - the field where to look for the text - * @param {*} escapeFun - optional function for escaping the text - * @return {string} the text in the thing - */ -function getText(thing,field,escapeFun) { - const text = thing[field] ? thing[field] : ''; - if(escapeFun) { - return escapeFun(text); - } else { - return text; - } -} - -/** @type {Object} */ -const rules = {}; -// Inlines -rules.Code = (visitor,thing,children,parameters,resultString,resultSeq) => { - const next = `\`${getText(thing,'text')}\``; - const result = [resultString(next)]; - resultSeq(parameters,result); -}; -rules.Emph = (visitor,thing,children,parameters,resultString,resultSeq) => { - const result = [resultString('*'),children,resultString('*')]; - resultSeq(parameters,result); -}; -rules.Strong = (visitor,thing,children,parameters,resultString,resultSeq) => { - const result = [resultString('**'),children,resultString('**')]; - resultSeq(parameters,result); -}; -rules.Link = (visitor,thing,children,parameters,resultString,resultSeq) => { - const next1 = '['; - const next2 = `](${thing.destination} "${getText(thing,'title')}")`; - const result = [resultString(next1),children,resultString(next2)]; - resultSeq(parameters,result); -}; -rules.Image = (visitor,thing,children,parameters,resultString,resultSeq) => { - const next1 = '!['; - const next2 = `](${thing.destination} "${getText(thing,'title')}")`; - const result = [resultString(next1),children,resultString(next2)]; - resultSeq(parameters,result); -}; -rules.HtmlInline = (visitor,thing,children,parameters,resultString,resultSeq) => { - const next = getText(thing,'text'); - const result = [resultString(next)]; - resultSeq(parameters,result); -}; -rules.Linebreak = (visitor,thing,children,parameters,resultString,resultSeq) => { - const next = `\\${CommonMarkUtils.mkPrefix(parameters,1)}`; - const result = [resultString(next)]; - resultSeq(parameters,result); -}; -rules.Softbreak = (visitor,thing,children,parameters,resultString,resultSeq) => { - const next = CommonMarkUtils.mkPrefix(parameters,1); - const result = [resultString(next)]; - resultSeq(parameters,result); -}; -rules.Text = (visitor,thing,children,parameters,resultString,resultSeq) => { - const next = getText(thing,'text',CommonMarkUtils.escapeText); - const result = [resultString(next)]; - resultSeq(parameters,result); -}; -// Leaf blocks -rules.ThematicBreak = (visitor,thing,children,parameters,resultString,resultSeq) => { - const next1 = CommonMarkUtils.mkPrefix(parameters,2); - const next2 = '---'; - const result = [resultString(next1),resultString(next2)]; - resultSeq(parameters,result); -}; -rules.Heading = (visitor,thing,children,parameters,resultString,resultSeq) => { - const level = parseInt(thing.level); - const next1 = CommonMarkUtils.mkPrefix(parameters,2); - if (level < 3 && children !== '') { // XXX empty children -- how to generalize that? - CommonMarkUtils.nextNode(parameters); - const next3 = CommonMarkUtils.mkPrefix(parameters,1); - const next4 = CommonMarkUtils.mkSetextHeading(level); - const result = [resultString(next1),children,resultString(next3),resultString(next4)]; - resultSeq(parameters,result); - } else { - const next2 = CommonMarkUtils.mkATXHeading(level); - const next3 = ' '; - const result = [resultString(next1),resultString(next2),resultString(next3),children]; - resultSeq(parameters,result); - } -}; -rules.CodeBlock = (visitor,thing,children,parameters,resultString,resultSeq) => { - const prefix = CommonMarkUtils.mkPrefix(parameters,2); - const newLine = CommonMarkUtils.mkNewLine(parameters); - const next1 = `${prefix}\`\`\` ${getText(thing,'info')}`; - const lines = getText(thing,'text',CommonMarkUtils.escapeCodeBlock).split('\n'); - const next2 = `${newLine}${lines.join(newLine)}\`\`\``; - const result = [resultString(next1),resultString(next2)]; - resultSeq(parameters,result); -}; -rules.HtmlBlock = (visitor,thing,children,parameters,resultString,resultSeq) => { - const nodeText = getText(thing,'text'); - const next1 = CommonMarkUtils.mkPrefix(parameters,2); - const next2 = nodeText; - const result = [resultString(next1),resultString(next2)]; - resultSeq(parameters,result); -}; -rules.Paragraph = (visitor,thing,children,parameters,resultString,resultSeq) => { - const next1 = CommonMarkUtils.mkPrefix(parameters,parameters.first ? 1 : 2); - const result = [resultString(next1),children]; - resultSeq(parameters,result); -}; -// Container blocks -rules.BlockQuote = (visitor,thing,children,parameters,resultString,resultSeq) => { - const result = [children]; - resultSeq(parameters,result); -}; -rules.Item = (visitor,thing,children,parameters,resultString,resultSeq) => { - const level = parameters.tight && parameters.tight === 'false' && parameters.index !== parameters.indexInit ? 2 : 1; - if(parameters.type === 'ordered') { - const next1 = `${CommonMarkUtils.mkPrefix(parameters,level)}${parameters.index}. `; - const result = [resultString(next1),children]; - resultSeq(parameters,result); - } else { - const next1 = `${CommonMarkUtils.mkPrefix(parameters,level)}- `; - const result = [resultString(next1),children]; - resultSeq(parameters,result); - } -}; -rules.List = (visitor,thing,children,parameters,resultString,resultSeq) => { - const result = [children]; - resultSeq(parameters,result); -}; -rules.Document = (visitor,thing,children,parameters,resultString,resultSeq) => { - const result = [children]; - resultSeq(parameters,result); -}; - -rules.Table = (visitor,thing,children,parameters,resultString,resultSeq) => { - const result = [children]; - resultSeq(parameters,result); -}; - -rules.TableBody = (visitor,thing,children,parameters,resultString,resultSeq) => { - const result = [children]; - resultSeq(parameters,result); -}; - -rules.TableRow = (visitor,thing,children,parameters,resultString,resultSeq) => { - const next1 = '|'; - const newLine = CommonMarkUtils.mkNewLine(parameters); - const result = [children, resultString(next1), newLine]; - resultSeq(parameters,result); -}; - -rules.TableCell = (visitor,thing,children,parameters,resultString,resultSeq) => { - const next1 = '|'; - const next2 = ' '; - const result = [resultString(next1),resultString(next2),children, resultString(next2)]; - resultSeq(parameters,result); -}; - -rules.TableHead = (visitor,thing,children,parameters,resultString,resultSeq) => { - const col = thing.nodes[0].nodes.length; - const next1 = CommonMarkUtils.mkTableHeading(col); - const newLine = CommonMarkUtils.mkNewLine(parameters); - const result = [children, resultString(next1), resultString(newLine)]; - resultSeq(parameters,result); -}; - -rules.HeaderCell = (visitor,thing,children,parameters,resultString,resultSeq) => { - const next1 = '|'; - const next2 = ' '; - const result = [resultString(next1),resultString(next2),children, resultString(next2)]; - resultSeq(parameters,result); -}; - -module.exports = rules; \ No newline at end of file diff --git a/packages/markdown-common/lib/removeFormatting.js b/packages/markdown-common/lib/removeFormatting.js deleted file mode 100644 index df857654..00000000 --- a/packages/markdown-common/lib/removeFormatting.js +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -const Stack = require('./Stack'); -const CommonMarkModel = require('./externalModels/CommonMarkModel'); - -/** - * Maps the keys in an object - * @param {*} obj input object - * @param {*} stack stack object - */ -function mapObject(obj, stack) { - switch (obj.$class) { - // remove these, visit children - case `${CommonMarkModel.NAMESPACE}.Emph`: - case `${CommonMarkModel.NAMESPACE}.Strong`: - case `${CommonMarkModel.NAMESPACE}.Document`: - case `${CommonMarkModel.NAMESPACE}.BlockQuote`: - obj.nodes.forEach(element => { - mapObject(element, stack); - }); - break; - // wrap in a para and process child nodes - case `${CommonMarkModel.NAMESPACE}.Paragraph`: - case `${CommonMarkModel.NAMESPACE}.Heading`: { - stack.push({ - $class: `${CommonMarkModel.NAMESPACE}.Paragraph`, - nodes: [] - }); - if (obj.nodes) { - obj.nodes.forEach(element => { - mapObject(element, stack); - }); - } - stack.pop(); - break; - } - // wrap in a para and grab text - case `${CommonMarkModel.NAMESPACE}.CodeBlock`: - case `${CommonMarkModel.NAMESPACE}.HtmlBlock`: - stack.append( { - $class: `${CommonMarkModel.NAMESPACE}.Paragraph`, - nodes: [ - { - $class: `${CommonMarkModel.NAMESPACE}.Text`, - text: obj.text - } - ] - }); - break; - // inline text - case `${CommonMarkModel.NAMESPACE}.Code`: - case `${CommonMarkModel.NAMESPACE}.HtmlInline`: - stack.append( { - $class: `${CommonMarkModel.NAMESPACE}.Text`, - text: obj.text - }); - break; - // get destination - case `${CommonMarkModel.NAMESPACE}.Link`: - stack.append( { - $class: `${CommonMarkModel.NAMESPACE}.Text`, - text: obj.destination - }); - break; - // get title - case `${CommonMarkModel.NAMESPACE}.Image`: - stack.append( { - $class: `${CommonMarkModel.NAMESPACE}.Text`, - text: obj.title - }); - break; - // do not insert a \ for linebreaks - case `${CommonMarkModel.NAMESPACE}.Linebreak`: - stack.append( { - $class: `${CommonMarkModel.NAMESPACE}.Text`, - text: '\n' - }); - break; - // copy - default: - stack.append(obj); - break; - } -} - -/** - * Removes rich text formatting nodes. - * @param {*} obj input object - * @returns {*} the modified object - */ -function removeFormatting(obj) { - const root = { - $class : `${CommonMarkModel.NAMESPACE}.Document`, - xmlns : obj.xmlns, - nodes: [] - }; - const stack = new Stack(); - stack.push(root, false); - mapObject(obj, stack); - return root; -} - -module.exports = removeFormatting; \ No newline at end of file diff --git a/packages/markdown-common/package.json b/packages/markdown-common/package.json index a92c93dc..8db46f97 100644 --- a/packages/markdown-common/package.json +++ b/packages/markdown-common/package.json @@ -1,6 +1,6 @@ { "name": "@accordproject/markdown-common", - "version": "0.16.25", + "version": "1.0.0", "description": "A framework for transforming markdown", "engines": { "node": ">=22", @@ -10,25 +10,23 @@ "access": "public" }, "files": [ - "lib", - "types", - "index.js" + "lib" ], - "main": "index.js", + "main": "lib/index.js", + "types": "lib/index.d.ts", "scripts": { - "pretest": "npm run lint", - "lint": "eslint .", + "pretest": "npm run lint && npm run build", + "lint": "eslint . --ext .ts", "postlint": "npm run licchk", "licchk": "license-check-and-add", - "test": "jest --timeOut=10000 --silent", + "test": "jest --silent", + "test:noisy": "jest", "test:updateSnapshot": "jest --updateSnapshot --silent", - "test:cov": "npm run lint && jest --timeOut=10000 --coverage --silent", - "jsdoc": "jsdoc -c jsdoc.json package.json", - "build": "npm run build:types", - "build:model-types": "node scripts/generate-model-types.js", - "build:types": "npm run build:model-types && tsc" + "test:cov": "npm run lint && npm run build && jest --coverage --silent", + "build": "tsc -p tsconfig.json", + "clean": "rimraf lib" }, - "typings": "types/index.d.ts", + "typings": "lib/index.d.ts", "repository": { "type": "git", "url": "git+https://github.com/accordproject/markdown-transform.git", @@ -46,13 +44,18 @@ }, "homepage": "https://github.com/accordproject/markdown-transform", "devDependencies": { - "@accordproject/concerto-codegen": "^4.0.1", "@accordproject/concerto-core": "^4.1.3", + "@types/jest": "^29.5.12", + "@types/markdown-it": "^14.1.2", + "@types/node": "^20.11.30", + "@typescript-eslint/eslint-plugin": "^7.4.0", + "@typescript-eslint/parser": "^7.4.0", "eslint": "8.57.1", "jest": "^29.7.0", "jest-diff": "^29.7.0", - "jsdoc": "^4.0.4", "license-check-and-add": "2.3.6", + "rimraf": "^5.0.5", + "ts-jest": "^29.1.2", "typescript": "^5.9.3" }, "peerDependencies": { @@ -63,14 +66,14 @@ "markdown-it": "^14.1.0" }, "license-check-and-add-config": { - "folder": "./lib", + "folder": "./src", "license": "header.txt", "exact_paths_method": "EXCLUDE", "exact_paths": [ "externalModels/.npmignore", "externalModels/.gitignore", "coverage", - "index.d.ts", + "__snapshots__", "./system", "LICENSE", "node_modules", @@ -88,7 +91,7 @@ ], "insert_license": false, "license_formats": { - "js|njk|pegjs|cto|acl|qry": { + "ts|tsx|js|njk|pegjs|cto|acl|qry": { "prepend": "/*", "append": " */", "eachLine": { diff --git a/packages/markdown-common/scripts/generate-model-types.js b/packages/markdown-common/scripts/generate-model-types.js deleted file mode 100644 index 3771f9e2..00000000 --- a/packages/markdown-common/scripts/generate-model-types.js +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -const { CodeGen } = require('@accordproject/concerto-codegen'); -const { ModelManager } = require('@accordproject/concerto-core'); -const fs = require('fs'); -const path = require('path'); - -const { CommonMarkModel, CiceroMarkModel, ConcertoMetaModel, TemplateMarkModel } = require('../index'); - -// Map raw namespace filename → clean output name. -// Update this map if any CTO namespace version bumps. -const NAME_MAP = { - 'concerto@1.0.0': 'concerto-base', - 'concerto.metamodel@1.0.0': 'concerto-metamodel', - 'org.accordproject.commonmark@0.5.0': 'commonmark', - 'org.accordproject.ciceromark@0.6.0': 'ciceromark', - 'org.accordproject.templatemark@0.5.0': 'templatemark', -}; - -// Namespaces auto-generated but not useful in the public API -const SKIP = new Set(['concerto.decorator@1.0.0']); - -const mm = new ModelManager(); -mm.addCTOModel(ConcertoMetaModel.MODEL, 'metamodel.cto'); -mm.addCTOModel(CommonMarkModel.MODEL, 'commonmark.cto'); -mm.addCTOModel(CiceroMarkModel.MODEL, 'ciceromark.cto'); -mm.addCTOModel(TemplateMarkModel.MODEL, 'templatemark.cto'); - -const outDir = path.resolve(__dirname, '../types/model'); -fs.mkdirSync(outDir, { recursive: true }); - -const files = {}; -mm.accept(new CodeGen.TypescriptVisitor(), { - fileWriter: { - openFile: (f) => { files[f] = ''; }, - writeLine: (i, l) => { files[Object.keys(files).at(-1)] += ' '.repeat(i) + l + '\n'; }, - closeFile: () => {} - } -}); - -for (const [rawFile, content] of Object.entries(files)) { - const ns = rawFile.replace(/\.ts$/, ''); - if (SKIP.has(ns)) continue; - const cleanName = NAME_MAP[ns]; - if (!cleanName) { console.warn('unmapped namespace:', ns); continue; } - - // Rewrite cross-file import paths to clean names - let out = content; - for (const [rawNs, clean] of Object.entries(NAME_MAP)) { - out = out.replaceAll(`'./${rawNs}'`, `'./${clean}'`); - } - // Strip generated eslint-disable comment - out = out.replace(/\/\* eslint-disable.*?\*\/\n/, ''); - - const outPath = path.join(outDir, `${cleanName}.d.ts`); - fs.writeFileSync(outPath, out); - console.log(`wrote types/model/${cleanName}.d.ts`); -} diff --git a/packages/markdown-common/lib/CommonMarkSpec.test.js b/packages/markdown-common/src/CommonMarkSpec.test.ts similarity index 79% rename from packages/markdown-common/lib/CommonMarkSpec.test.js rename to packages/markdown-common/src/CommonMarkSpec.test.ts index e14c8cc7..0fbd2662 100644 --- a/packages/markdown-common/lib/CommonMarkSpec.test.js +++ b/packages/markdown-common/src/CommonMarkSpec.test.ts @@ -12,22 +12,17 @@ * limitations under the License. */ -// @ts-nocheck -/* eslint-disable no-undef */ -'use strict'; +import * as fs from 'fs'; +import { diff } from 'jest-diff'; +import { CommonMarkTransformer } from './CommonMarkTransformer'; -const fs = require('fs'); -const diff = require('jest-diff'); - -const CommonMarkTransformer = require('./CommonMarkTransformer'); - -let commonMark = null; +let commonMark: CommonMarkTransformer; expect.extend({ - toMarkdownRoundtrip(markdownText) { - const json1 = commonMark.fromMarkdown(markdownText, 'json'); + toMarkdownRoundtrip(markdownText: string) { + const json1 = commonMark.fromMarkdown(markdownText); const newMarkdown = commonMark.toMarkdown(json1); - const json2 = commonMark.fromMarkdown(newMarkdown, 'json'); + const json2 = commonMark.fromMarkdown(newMarkdown); const pass = JSON.stringify(json1) === JSON.stringify(json2); const message = pass @@ -54,23 +49,27 @@ expect.extend({ }, }); -// @ts-ignore beforeAll(() => { commonMark = new CommonMarkTransformer(); }); +interface SpecExample { + markdown: string; + html: string; + section: string; + number: number; +} + /** * Extracts all the test md snippets from a commonmark spec file - * @param {string} testfile the file to use - * @return {*} the examples */ -function extractSpecTests(testfile) { - let data = fs.readFileSync(testfile, 'utf8'); - let examples = []; +function extractSpecTests(testfile: string): SpecExample[] { + const data = fs.readFileSync(testfile, 'utf8'); + const examples: SpecExample[] = []; let current_section = ''; let example_number = 0; - let tests = data - .replace(/\r\n?/g, '\n') // Normalize newlines for platform independence + const tests = data + .replace(/\r\n?/g, '\n') .replace(/^(.|[\n])*/m, ''); tests.replace(/^`{32} example\n([\s\S]*?)^\.\n([\s\S]*?)^`{32}$|^#{1,6} *(.*)$/gm, @@ -83,9 +82,10 @@ function extractSpecTests(testfile) { markdown: markdownSubmatch, html: htmlSubmatch, section: current_section, - number: example_number + number: example_number, }); } + return ''; }); return examples; } @@ -93,10 +93,9 @@ function extractSpecTests(testfile) { /** * Get the name and contents of all markdown snippets * used in a commonmark spec file - * @returns {*} an array of name/contents tuples */ -function getMarkdownSpecFiles() { - const result = []; +function getMarkdownSpecFiles(): [string, string][] { + const result: [string, string][] = []; const specExamples = extractSpecTests(__dirname + '/../test/data/spec.txt'); specExamples.forEach(function (example) { result.push([`${example.section}-${example.number}`, example.markdown]); @@ -108,7 +107,7 @@ function getMarkdownSpecFiles() { describe('markdown-spec', () => { getMarkdownSpecFiles().forEach(([file, markdownText]) => { it(`converts ${file} to concerto JSON`, () => { - const json = commonMark.fromMarkdown(markdownText, 'json'); + const json = commonMark.fromMarkdown(markdownText); expect(json).toMatchSnapshot(); }); @@ -122,4 +121,4 @@ describe('markdown-spec', () => { expect(markdownText).toMarkdownRoundtrip(); }); }); -}); \ No newline at end of file +}); diff --git a/packages/markdown-common/lib/CommonMarkTransformer.test.js b/packages/markdown-common/src/CommonMarkTransformer.test.ts similarity index 72% rename from packages/markdown-common/lib/CommonMarkTransformer.test.js rename to packages/markdown-common/src/CommonMarkTransformer.test.ts index 33fffa68..bba5b2b9 100644 --- a/packages/markdown-common/lib/CommonMarkTransformer.test.js +++ b/packages/markdown-common/src/CommonMarkTransformer.test.ts @@ -12,19 +12,14 @@ * limitations under the License. */ -// @ts-nocheck -/* eslint-disable no-undef */ -'use strict'; +import * as fs from 'fs'; +import { diff } from 'jest-diff'; +import { CommonMarkTransformer } from './CommonMarkTransformer'; -const fs = require('fs'); -const diff = require('jest-diff'); - -const CommonMarkTransformer = require('./CommonMarkTransformer'); - -let commonMark = null; +let commonMark: CommonMarkTransformer; expect.extend({ - toMarkdownRoundtrip(markdownText) { + toMarkdownRoundtrip(markdownText: string) { const json1 = commonMark.fromMarkdown(markdownText); const newMarkdown = commonMark.toMarkdown(json1); const json2 = commonMark.fromMarkdown(newMarkdown); @@ -33,43 +28,41 @@ expect.extend({ const message = pass ? () => this.utils.matcherHint(`toMarkdownRoundtrip - ${markdownText} -> ${newMarkdown}`, undefined, undefined, undefined) + - '\n\n' + - `Expected: ${this.utils.printExpected(json1)}\n` + - `Received: ${this.utils.printReceived(json2)}` + '\n\n' + + `Expected: ${this.utils.printExpected(json1)}\n` + + `Received: ${this.utils.printReceived(json2)}` : () => { const diffString = diff(json1, json2, { expand: true, }); return ( this.utils.matcherHint(`toMarkdownRoundtrip - ${JSON.stringify(markdownText)} -> ${JSON.stringify(newMarkdown)}`, undefined, undefined, undefined) + - '\n\n' + - (diffString && diffString.includes('- Expect') - ? `Difference:\n\n${diffString}` - : `Expected: ${this.utils.printExpected(json1)}\n` + - `Received: ${this.utils.printReceived(json2)}`) + '\n\n' + + (diffString && diffString.includes('- Expect') + ? `Difference:\n\n${diffString}` + : `Expected: ${this.utils.printExpected(json1)}\n` + + `Received: ${this.utils.printReceived(json2)}`) ); }; - return {actual: markdownText, message, pass}; + return { actual: markdownText, message, pass }; }, }); -// @ts-ignore beforeAll(() => { commonMark = new CommonMarkTransformer(); }); /** * Get the name and contents of all markdown test files - * @returns {*} an array of name/contents tuples */ -function getMarkdownFiles() { - const result = []; +function getMarkdownFiles(): [string, string][] { + const result: [string, string][] = []; const files = fs.readdirSync(__dirname + '/../test/data'); - files.forEach(function(file) { - if(file.endsWith('.md')) { - let contents = fs.readFileSync(__dirname + '/../test/data/' + file, 'utf8'); + files.forEach(function (file) { + if (file.endsWith('.md')) { + const contents = fs.readFileSync(__dirname + '/../test/data/' + file, 'utf8'); result.push([file, contents]); } }); @@ -78,7 +71,7 @@ function getMarkdownFiles() { } describe('markdown', () => { - getMarkdownFiles().forEach( ([file, markdownText]) => { + getMarkdownFiles().forEach(([file, markdownText]) => { it(`converts ${file} to concerto JSON`, () => { const json = commonMark.fromMarkdown(markdownText); expect(json).toMatchSnapshot(); @@ -91,7 +84,7 @@ describe('markdown', () => { }); describe('to plain text', () => { - getMarkdownFiles().forEach( ([file, markdownText]) => { + getMarkdownFiles().forEach(([file, markdownText]) => { it(file, () => { const json = commonMark.fromMarkdown(markdownText); const unformat = commonMark.removeFormatting(json); @@ -108,11 +101,9 @@ describe('readme', () => { it('converts example1 to CommonMark DOM', () => { const json = commonMark.fromMarkdown('# Heading\n\nThis is some `code`.\n\nFin.'); - // console.log(JSON.stringify(json, null, 4)); expect(json).toMatchSnapshot(); json.nodes[0].nodes[0].text = 'My New Heading'; const newMarkdown = commonMark.toMarkdown(json); - // console.log(newMarkdown); expect(newMarkdown).toMatchSnapshot(); }); }); @@ -121,7 +112,6 @@ describe('acceptance', () => { it('converts acceptance to CommonMark DOM', () => { const markdownText = fs.readFileSync(__dirname + '/../test/data/acceptance.md', 'utf8'); const json = commonMark.fromMarkdown(markdownText); - // console.log(JSON.stringify(json, null, 4)); expect(json).toMatchSnapshot(); const newMarkdown = commonMark.toMarkdown(json); expect(newMarkdown).toMatchSnapshot(); diff --git a/packages/markdown-common/lib/CommonMarkTransformer.js b/packages/markdown-common/src/CommonMarkTransformer.ts similarity index 52% rename from packages/markdown-common/lib/CommonMarkTransformer.js rename to packages/markdown-common/src/CommonMarkTransformer.ts index d916acba..48c5e6c3 100644 --- a/packages/markdown-common/lib/CommonMarkTransformer.js +++ b/packages/markdown-common/src/CommonMarkTransformer.ts @@ -12,31 +12,23 @@ * limitations under the License. */ -'use strict'; - -/** @typedef {import('@accordproject/markdown-common/types/model/commonmark').IDocument} IDocument */ -/** @typedef {import('@accordproject/markdown-common/types/model/commonmark').INode} INode */ - -const MarkdownIt = require('markdown-it'); -const FromMarkdownIt = require('./FromMarkdownIt'); - -const { ModelManager, Factory, Serializer } = require('@accordproject/concerto-core'); - -const ToMarkdownVisitor = require('./ToMarkdownVisitor'); -const removeFormatting = require('./removeFormatting'); -const CommonMarkModel = require('./externalModels/CommonMarkModel'); +import MarkdownIt from 'markdown-it'; +import { FromMarkdownIt } from './FromMarkdownIt'; +import { ModelManager, Factory, Serializer } from '@accordproject/concerto-core'; +import { ToMarkdownVisitor } from './ToMarkdownVisitor'; +import { removeFormatting } from './removeFormatting'; +import * as CommonMarkModel from './externalModels/CommonMarkModel'; /** * Parses markdown using the commonmark parser into the * intermediate representation: a JSON object that adheres to * the 'org.accordproject.commonmark' Concerto model. */ -class CommonMarkTransformer { - /** - * Construct the parser. - */ +export class CommonMarkTransformer { + serializer: Serializer; + constructor() { - const modelManager = new ModelManager({strict: true}); + const modelManager = new ModelManager(); modelManager.addCTOModel(CommonMarkModel.MODEL, 'commonmark.cto'); const factory = new Factory(modelManager); this.serializer = new Serializer(factory, modelManager); @@ -44,42 +36,31 @@ class CommonMarkTransformer { /** * Converts a CommonMark DOM to a markdown string - * @param {INode} input - CommonMark DOM (in JSON) - * @returns {string} the markdown string */ - toMarkdown(input) { + toMarkdown(input: any): string { const visitor = new ToMarkdownVisitor(); return visitor.toMarkdown(this.serializer.fromJSON(input)); } /** * Converts a CommonMark DOM to a CommonMark DOM with formatting removed - * @param {IDocument} input - CommonMark DOM (in JSON) - * @returns {IDocument} the CommonMark DOM with formatting nodes removed */ - removeFormatting(input) { + removeFormatting(input: any): any { return removeFormatting(input); } /** * Converts a markdown string into a token stream - * - * @param {string} markdown the string to parse - * @returns {object[]} a markdown-it token stream */ - toTokens(markdown) { - const parser = new MarkdownIt({html:true}); // XXX HTML inlines and code blocks true - const tokenStream = parser.parse(markdown,{}); - return tokenStream; + toTokens(markdown: string): any[] { + const parser = new MarkdownIt({ html: true }); + return parser.parse(markdown, {}); } /** * Converts a token stream into a CommonMark DOM object. - * - * @param {object[]} tokenStream the token stream - * @returns {IDocument} a Concerto object (DOM) for the markdown content */ - fromTokens(tokenStream) { + fromTokens(tokenStream: any[]): any { const fromMarkdownIt = new FromMarkdownIt(); const json = fromMarkdownIt.toCommonMark(tokenStream); @@ -90,24 +71,18 @@ class CommonMarkTransformer { /** * Converts a markdown string into a CommonMark DOM object. - * - * @param {string} markdown the string to parse - * @returns {IDocument} a CommonMark DOM (JSON) for the markdown content */ - fromMarkdown(markdown) { + fromMarkdown(markdown: string): any { const tokenStream = this.toTokens(markdown); return this.fromTokens(tokenStream); } /** * Retrieve the serializer used by the parser - * - * @returns {Serializer} a serializer capable of dealing with the Concerto */ - getSerializer() { + getSerializer(): Serializer { return this.serializer; } - } -module.exports = CommonMarkTransformer; \ No newline at end of file +export default CommonMarkTransformer; diff --git a/packages/markdown-common/src/CommonMarkUtils.ts b/packages/markdown-common/src/CommonMarkUtils.ts new file mode 100644 index 00000000..5f29b938 --- /dev/null +++ b/packages/markdown-common/src/CommonMarkUtils.ts @@ -0,0 +1,285 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { DOMParser } from '@xmldom/xmldom'; +import * as CommonMarkModel from './externalModels/CommonMarkModel'; + +export interface BlockStack { + first: boolean; + blocks: string[]; +} + +/** + * Initial block stack + */ +export function blocksInit(): BlockStack { + return { + first: true, + blocks: [], + }; +} + +/** + * Next node + */ +export function blocksNextNode(stack: BlockStack): void { + stack.first = false; +} + +/** + * enter block + */ +export function blocksEnterBlock(stack: BlockStack, blockType: string, setFirst: (b: string) => boolean): BlockStack { + const newStack: BlockStack = { first: false, blocks: [] }; + if (setFirst(blockType)) { + newStack.first = true; + } else { + newStack.first = stack.first; + } + newStack.blocks = stack.blocks.slice(); + newStack.blocks.push(blockType); + return newStack; +} + +/** + * Create a prefix within an existing line + */ +export function prefixInLine(blocks: string[]): string { + let prefix = ''; + for (let i = blocks.length - 1; i >= 0; i--) { + if (blocks[i] === 'Item' || blocks[i] === 'ListBlockDefinition') { + break; + } else if (blocks[i] === 'BlockQuote') { + prefix = '> ' + prefix; + } + } + return prefix; +} + +/** + * Create a new line + */ +export function newLine(blocks: string[]): string { + let prefix = ''; + for (let i = blocks.length - 1; i >= 0; i--) { + if (blocks[i] === 'Item' || blocks[i] === 'ListBlockDefinition') { + prefix = ' ' + prefix; + } else if (blocks[i] === 'BlockQuote') { + prefix = '> ' + prefix; + } + } + return '\n' + prefix; +} + +/** + * Next node + */ +export function nextNode(parameters: any): void { + blocksNextNode(parameters.stack); + if (parameters.index) { + parameters.index++; + } +} + +/** + * Set parameters for general blocks + */ +export function mkParameters(ast: any, parametersOut: any, init: any, setFirst: (b: string) => boolean): any { + const parameters = Object.assign({}, parametersOut); + parameters.result = init; + parameters.stack = blocksEnterBlock(parametersOut.stack, ast.getType(), setFirst); + if (ast.getType() === 'List') { + parameters.indexInit = ast.start ? parseInt(ast.start) : 1; + parameters.index = parameters.indexInit; + parameters.tight = ast.tight; + parameters.type = ast.type; + } + return parameters; +} + +/** + * Create a line prefix + */ +export function mkPrefix(parameters: any, nb: number): string { + const stack: BlockStack = parameters.stack; + if (stack.first) { + return prefixInLine(stack.blocks); + } else { + const nl = newLine(stack.blocks); + return nl.repeat(nb); + } +} + +/** + * Create a single new line + */ +export function mkNewLine(parameters: any): string { + const stack: BlockStack = parameters.stack; + return newLine(stack.blocks); +} + +/** + * Create Setext heading + */ +export function mkSetextHeading(level: number): string { + if (level === 1) { + return '===='; + } else { + return '----'; + } +} + +/** + * Create ATX heading + */ +export function mkATXHeading(level: number): string { + return Array(level).fill('#').join(''); +} + +/** + * Create table heading + */ +export function mkTableHeading(col: number): string { + return Array(col).fill('|---------').join('') + '|'; +} + +/** + * Adding escapes for text nodes + */ +export function escapeText(input: string): string { + return input.replace(/[*`&>]/g, '\\$&') + .replace(/^(#+) /g, '\\$1 ') + .replace(/^(\d+)\. /g, '$1\\. ') + .replace(/^- /g, '\\- ') + .replace(/^_/g, '\\_'); +} + +/** + * Adding escapes for code blocks + */ +export function escapeCodeBlock(input: string): string { + return input.replace(/`/g, '\\`'); +} + +/** + * Removing escapes + */ +export function unescapeCodeBlock(input: string): string { + return input.replace(/\\`/g, '`'); +} + +/** + * Parses an HTML block and extracts the attributes, tag name and tag contents. + * Note that this will return null for strings like this: + */ +export function parseHtmlBlock(input: string): any { + try { + const doc = (new DOMParser()).parseFromString(input, 'text/html'); + const item: any = doc.childNodes[0]; + const attributes = item.attributes; + const attributeObject: Record = {}; + let attributeString = ''; + + for (let i = 0; i < attributes.length; i += 1) { + attributeString += `${attributes[i].name} = "${attributes[i].value}" `; + attributeObject[attributes[i].name] = attributes[i].value; + } + + const tag: any = {}; + tag.$class = `${CommonMarkModel.NAMESPACE}.TagInfo`; + tag.tagName = item.tagName.toLowerCase(); + tag.attributeString = attributeString; + tag.attributes = []; + for (const attName in attributeObject) { + if (Object.prototype.hasOwnProperty.call(attributeObject, attName)) { + const attValue = attributeObject[attName]; + tag.attributes.push({ + $class: `${CommonMarkModel.NAMESPACE}.Attribute`, + name: attName, + value: attValue, + }); + } + } + tag.content = item.textContent; + tag.closed = input.endsWith('/>'); + + return tag; + } catch (err) { + return null; + } +} + +/** + * Merge adjacent Html nodes in a list of nodes + */ +export function mergeAdjacentHtmlNodes(nodes: any[], tagInfo: boolean): any[] { + const result: any[] = []; + for (let n = 0; n < nodes.length; n++) { + const cur = nodes[n]; + const next = n + 1 < nodes.length ? nodes[n + 1] : null; + + if (next && + cur.$class === (`${CommonMarkModel.NAMESPACE}.HtmlInline`) && + next.$class === (`${CommonMarkModel.NAMESPACE}.HtmlInline`) && + cur.tag && + next.text === ``) { + next.text = cur.text + next.text; + next.tag = tagInfo ? parseHtmlBlock(next.text) : null; + } else { + result.push(cur); + } + } + return result; +} + +/** + * Determine the heading level + */ +export function headingLevel(tag: string): string { + switch (tag) { + case 'h1': return '1'; + case 'h2': return '2'; + case 'h3': return '3'; + case 'h4': return '4'; + case 'h5': return '5'; + default: return '6'; + } +} + +/** + * Get an attribute value + */ +export function getAttr(attrs: any, name: string, def: any): string { + if (attrs) { + const startAttrs = attrs.filter((x: any) => x[0] === name); + if (startAttrs[0]) { + return '' + startAttrs[0][1]; + } else { + return def; + } + } else { + return def; + } +} + +/** + * Trim single ending newline + */ +export function trimEndline(text: string): string { + if (text.charAt(text.length - 1) && text.charAt(text.length - 1) === '\n') { + return text.substring(0, text.length - 1); + } else { + return text; + } +} diff --git a/packages/markdown-common/lib/FromCommonMarkVisitor.js b/packages/markdown-common/src/FromCommonMarkVisitor.ts similarity index 54% rename from packages/markdown-common/lib/FromCommonMarkVisitor.js rename to packages/markdown-common/src/FromCommonMarkVisitor.ts index e9520849..62fbf626 100644 --- a/packages/markdown-common/lib/FromCommonMarkVisitor.js +++ b/packages/markdown-common/src/FromCommonMarkVisitor.ts @@ -12,23 +12,39 @@ * limitations under the License. */ -'use strict'; +import * as CommonMarkUtils from './CommonMarkUtils'; -const CommonMarkUtils = require('./CommonMarkUtils'); +export type RuleFunction = ( + visitor: FromCommonMarkVisitor, + thing: any, + children: any, + parameters: any, + resultString: (s: string) => any, + resultSeq: (parameters: any, result: any) => void, +) => void; + +export type Rules = Record; /** * Converts a CommonMark DOM to something else */ -class FromCommonMarkVisitor { +export class FromCommonMarkVisitor { + options: any; + resultString: (s: string) => any; + resultSeq: (parameters: any, result: any) => void; + rules: Rules; + setFirst: (b: string) => boolean; + /** * Construct the visitor. - * @param {object} options configuration options - * @param {*} resultString how to create a result from a string - * @param {*} resultSeq how to sequentially combine results - * @param {object} rules how to process each node type - * @param {*} setFirst whether entering this block should set first */ - constructor(options,resultString,resultSeq,rules,setFirst) { + constructor( + options: any, + resultString: (s: string) => any, + resultSeq: (parameters: any, result: any) => void, + rules: Rules, + setFirst: (b: string) => boolean, + ) { this.options = options; this.resultString = resultString; this.resultSeq = resultSeq; @@ -38,16 +54,11 @@ class FromCommonMarkVisitor { /** * Visits a sub-tree - * @param {*} visitor - the visitor to use - * @param {*} thing - the node to visit - * @param {*} parameters - the current parameters - * @param {string} field - where to find the children nodes - * @returns {*} the result for the sub tree */ - visitChildren(visitor, thing, parameters, field = 'nodes') { + visitChildren(visitor: FromCommonMarkVisitor, thing: any, parameters: any, field = 'nodes'): any { const parametersIn = CommonMarkUtils.mkParameters(thing, parameters, this.resultString(''), this.setFirst); - if(thing[field]) { - thing[field].forEach(node => { + if (thing[field]) { + thing[field].forEach((node: any) => { node.accept(visitor, parametersIn); CommonMarkUtils.nextNode(parametersIn); }); @@ -57,19 +68,16 @@ class FromCommonMarkVisitor { /** * Visit a node - * @param {*} thing the object being visited - * @param {*} parameters the parameters */ - visit(thing, parameters) { + visit(thing: any, parameters: any): void { const children = this.visitChildren(this, thing, parameters); const rule = this.rules[thing.getType()]; if (rule) { - // Passing 'this' so that rules can call the visitor if need be (not used for commonmark) - rule(this,thing,children,parameters,this.resultString,this.resultSeq); + rule(this, thing, children, parameters, this.resultString, this.resultSeq); } else { throw new Error(`No rule to handle type ${thing.getType()}`); } } } -module.exports = FromCommonMarkVisitor; \ No newline at end of file +export default FromCommonMarkVisitor; diff --git a/packages/markdown-common/lib/FromMarkdownIt.test.js b/packages/markdown-common/src/FromMarkdownIt.test.ts similarity index 81% rename from packages/markdown-common/lib/FromMarkdownIt.test.js rename to packages/markdown-common/src/FromMarkdownIt.test.ts index fd38aaa0..9e0067fa 100644 --- a/packages/markdown-common/lib/FromMarkdownIt.test.js +++ b/packages/markdown-common/src/FromMarkdownIt.test.ts @@ -12,13 +12,9 @@ * limitations under the License. */ -// @ts-nocheck -/* eslint-disable no-undef */ -'use strict'; - -const getAttr = require('./CommonMarkUtils').getAttr; -const FromMarkdownIt = require('./FromMarkdownIt'); -const CommonMarkModel = require('../lib/externalModels/CommonMarkModel'); +import { getAttr } from './CommonMarkUtils'; +import { FromMarkdownIt } from './FromMarkdownIt'; +import * as CommonMarkModel from './externalModels/CommonMarkModel'; const inlines = [{ 'type': 'text', @@ -33,9 +29,9 @@ const inlines = [{ 'info': '', 'meta': null, 'block': false, - 'hidden': false + 'hidden': false, }]; -const tokens = (inlines) => [{ +const tokens = (inlines: any[]) => [{ 'type': 'paragraph_open', 'tag': 'p', 'attrs': null, @@ -48,8 +44,8 @@ const tokens = (inlines) => [{ 'info': '', 'meta': null, 'block': true, - 'hidden': false -},{ + 'hidden': false, +}, { 'type': 'inline', 'tag': '', 'attrs': null, @@ -62,8 +58,8 @@ const tokens = (inlines) => [{ 'info': '', 'meta': null, 'block': true, - 'hidden': false -},{ + 'hidden': false, +}, { 'type': 'paragraph_close', 'tag': 'p', 'attrs': null, @@ -76,28 +72,28 @@ const tokens = (inlines) => [{ 'info': '', 'meta': null, 'block': true, - 'hidden': false + 'hidden': false, }]; const expected = { - '$class':`${CommonMarkModel.NAMESPACE}.Document`, - 'xmlns':'http://commonmark.org/xml/1.0', - 'nodes':[{ - '$class':`${CommonMarkModel.NAMESPACE}.Paragraph`, - 'nodes':[{ - '$class':`${CommonMarkModel.NAMESPACE}.Text`, - 'text':'This is some text.' - }] - }] + '$class': `${CommonMarkModel.NAMESPACE}.Document`, + 'xmlns': 'http://commonmark.org/xml/1.0', + 'nodes': [{ + '$class': `${CommonMarkModel.NAMESPACE}.Paragraph`, + 'nodes': [{ + '$class': `${CommonMarkModel.NAMESPACE}.Text`, + 'text': 'This is some text.', + }], + }], }; -const rules = { inlines: {}, blocks: {}}; +const rules: any = { inlines: {}, blocks: {} }; const ifOpenRule = { tag: 'ConditionalDefinition', leaf: false, open: true, close: false, - enter: (node,token,callback) => { - node.name = getAttr(token.attrs,'name',null); + enter: (node: any, token: any) => { + node.name = getAttr(token.attrs, 'name', null); node.whenTrue = null; node.whenFalse = null; }, @@ -108,14 +104,14 @@ const ifCloseRule = { leaf: false, open: false, close: true, - exit: (node,token,callback) => { + exit: (node: any) => { if (node.whenTrue) { node.whenFalse = node.nodes ? node.nodes : []; } else { node.whenTrue = node.nodes ? node.nodes : []; node.whenFalse = []; } - delete node.nodes; // Delete children (now in whenTrue or whenFalse) + delete node.nodes; }, skipEmpty: false, }; @@ -149,7 +145,7 @@ describe('FromMarkdownIt', () => { 'info': '', 'meta': null, 'block': true, - 'hidden': false + 'hidden': false, }; expect(() => { return fromMarkdownIt.toCommonMark(tokens(inlines).concat([wrongToken])); @@ -171,7 +167,7 @@ describe('FromMarkdownIt', () => { 'info': '', 'meta': null, 'block': true, - 'hidden': false + 'hidden': false, }; expect(() => { return fromMarkdownIt.toCommonMark(tokens(inlines.concat([wrongToken]))); diff --git a/packages/markdown-common/lib/FromMarkdownIt.js b/packages/markdown-common/src/FromMarkdownIt.ts similarity index 55% rename from packages/markdown-common/lib/FromMarkdownIt.js rename to packages/markdown-common/src/FromMarkdownIt.ts index 41127065..1262d603 100644 --- a/packages/markdown-common/lib/FromMarkdownIt.js +++ b/packages/markdown-common/src/FromMarkdownIt.ts @@ -12,25 +12,26 @@ * limitations under the License. */ -'use strict'; +import { Stack } from './Stack'; +import { mergeAdjacentHtmlNodes } from './CommonMarkUtils'; +import defaultRules from './tocommonmarkrules'; +import type { MarkdownItRules } from './tocommonmarkrules'; +import * as CommonMarkModel from './externalModels/CommonMarkModel'; -const Stack = require('./Stack'); -const { mergeAdjacentHtmlNodes } = require('./CommonMarkUtils'); -const tocommonmarkrules = require('./tocommonmarkrules'); -const CommonMarkModel = require('../lib/externalModels/CommonMarkModel'); /** * Converts a markdown-it token stream to a CommonMark DOM */ -class FromMarkdownIt { +export class FromMarkdownIt { + rules: MarkdownItRules; + /** * Construct the transformer - * @param {*[]} rules - the rules for each kind of markdown-it tokens */ - constructor(rules) { - this.rules = tocommonmarkrules; + constructor(rules?: { inlines?: any; blocks?: any }) { + this.rules = defaultRules; if (rules) { - this.rules.inlines = Object.assign(this.rules.inlines,rules.inlines); - this.rules.blocks = Object.assign(this.rules.blocks,rules.blocks); + this.rules.inlines = Object.assign(this.rules.inlines, rules.inlines); + this.rules.blocks = Object.assign(this.rules.blocks, rules.blocks); } } @@ -38,50 +39,39 @@ class FromMarkdownIt { * Takes the stack of constructed inline nodes * properly closing them (if the close token is missing in the markdown) * returns the final root node for the inline - * - * @param {*[]} rules - the rules for each kind of markdown-it tokens - * @param {*[]} stack - the stack of constructed nodes - * @returns {*} the final inline node */ - static closeInlines(rules,stack) { + static closeInlines(rules: MarkdownItRules, stack: Stack): any { let currentNode = stack.pop(); - while(stack.peek()) { + while (stack.peek()) { const rule = Object.values(rules.inlines).find((x) => x.tag === currentNode.$class && x.exit); if (rule && rule.exit) { - rule.exit(currentNode,null,FromMarkdownIt.inlineCallback(rules)); + rule.exit(currentNode, null, FromMarkdownIt.inlineCallback(rules)); } currentNode = stack.pop(); } - return mergeAdjacentHtmlNodes(currentNode.nodes,true); + return mergeAdjacentHtmlNodes(currentNode.nodes, true); } /** * Create a callback for inlines - * - * @param {*[]} rules - the rules for each kind of markdown-it tokens - * @returns {*} the callback */ - static inlineCallback(rules) { - return (tokens) => { + static inlineCallback(rules: MarkdownItRules): (tokens: any[]) => any[] { + return (tokens: any[]) => { const stack = new Stack(); - FromMarkdownIt.inlineToCommonMark(rules,tokens,stack); // Updates the stack - return FromMarkdownIt.closeInlines(rules,stack); + FromMarkdownIt.inlineToCommonMark(rules, tokens, stack); + return FromMarkdownIt.closeInlines(rules, stack); }; } /** * Process an inline node to CommonMark DOM - * - * @param {*[]} rules - the rules for each kind of markdown-it tokens - * @param {*} tokens - the content of the inline node - * @param {*[]} stack - the stack of constructed nodes */ - static inlineToCommonMark(rules,tokens,stack) { + static inlineToCommonMark(rules: MarkdownItRules, tokens: any[], stack: Stack): void { const rootNode = { '$class': `${CommonMarkModel.NAMESPACE}.Inline`, - 'nodes': [], + 'nodes': [] as any[], }; - stack.push(rootNode,false); + stack.push(rootNode, false); for (let i = 0; i < tokens.length; i++) { const token = tokens[i]; const rule = rules.inlines[token.type]; @@ -89,54 +79,50 @@ class FromMarkdownIt { throw new Error('Unknown inline type ' + token.type); } if (rule.leaf) { - const node = { $class: rule.tag }; - if (rule.enter) { rule.enter(node,token,FromMarkdownIt.inlineCallback(rules)); } + const node: any = { $class: rule.tag }; + if (rule.enter) { rule.enter(node, token, FromMarkdownIt.inlineCallback(rules)); } if (!(rule.skipEmpty && node.text === '')) { stack.append(node); } } else if (rule.open && rule.close) { - const node = { $class: rule.tag }; - if (rule.enter) { rule.enter(node,token,FromMarkdownIt.inlineCallback(rules)); } + const node: any = { $class: rule.tag }; + if (rule.enter) { rule.enter(node, token, FromMarkdownIt.inlineCallback(rules)); } stack.append(node); } else if (rule.open) { - const node = { $class: rule.tag }; - if (rule.enter) { rule.enter(node,token,FromMarkdownIt.inlineCallback(rules)); } + const node: any = { $class: rule.tag }; + if (rule.enter) { rule.enter(node, token, FromMarkdownIt.inlineCallback(rules)); } node.nodes = []; stack.push(node, true); } else if (rule.close) { const node = stack.pop(); - if (rule.exit) { rule.exit(node,token,FromMarkdownIt.inlineCallback(rules)); } + if (rule.exit) { rule.exit(node, token, FromMarkdownIt.inlineCallback(rules)); } } else { const node = stack.peek(); - if (rule.enter) { rule.enter(node,token,FromMarkdownIt.inlineCallback(rules)); } + if (rule.enter) { rule.enter(node, token, FromMarkdownIt.inlineCallback(rules)); } } } } /** * Transform a block token stream to CommonMark DOM - * - * @param {*[]} rules - the rules for each kind of markdown-it tokens - * @param {*} tokens - the markdown-it token stream - * @returns {*} the CommonMark nodes */ - static blockToCommonMark(rules,tokens) { - let stack = new Stack(); - let tight = new Stack(); - const rootNode = { + static blockToCommonMark(rules: MarkdownItRules, tokens: any[]): any { + const stack = new Stack(); + const tight = new Stack(); + const rootNode: any = { '$class': `${CommonMarkModel.NAMESPACE}.Document`, - 'xmlns' : 'http://commonmark.org/xml/1.0', - 'nodes': [], + 'xmlns': 'http://commonmark.org/xml/1.0', + 'nodes': [] as any[], }; - stack.push(rootNode,false); - tight.push({tight:'true'},false); + stack.push(rootNode, false); + tight.push({ tight: 'true' }, false); - for(let i = 0; i < tokens.length; i++) { + for (let i = 0; i < tokens.length; i++) { const token = tokens[i]; // Special purpose to recover tight v loose information in list nodes if (token.type === 'bullet_list_open' || token.type === 'ordered_list_open') { - tight.push({tight:'true'},false); + tight.push({ tight: 'true' }, false); } else if (token.type === 'bullet_list_close' || token.type === 'ordered_list_close') { const isTight = tight.pop(); const listNode = stack.peek(); @@ -150,7 +136,7 @@ class FromMarkdownIt { const currentNode = stack.peek(); if (currentNode) { currentNode.nodes = FromMarkdownIt.inlineCallback(rules)(token.children); - continue; // Continue for loop on next token + continue; } else { throw new Error('Malformed token stream: no current node'); } @@ -162,24 +148,24 @@ class FromMarkdownIt { } if (rule.leaf) { - const node = { $class: rule.tag }; - if (rule.enter) { rule.enter(node,token,FromMarkdownIt.inlineCallback(rules)); } + const node: any = { $class: rule.tag }; + if (rule.enter) { rule.enter(node, token, FromMarkdownIt.inlineCallback(rules)); } stack.append(node); } else if (rule.open) { - const node = { $class: rule.tag }; - if (rule.enter) { rule.enter(node,token,FromMarkdownIt.inlineCallback(rules)); } + const node: any = { $class: rule.tag }; + if (rule.enter) { rule.enter(node, token, FromMarkdownIt.inlineCallback(rules)); } node.nodes = []; stack.push(node, true); } else if (rule.close) { const node = stack.pop(); - if (rule.exit) { rule.exit(node,token,FromMarkdownIt.inlineCallback(rules)); } + if (rule.exit) { rule.exit(node, token, FromMarkdownIt.inlineCallback(rules)); } } } if (!rootNode.nodes || rootNode.nodes.length === 0) { rootNode.nodes.push({ '$class': `${CommonMarkModel.NAMESPACE}.Paragraph`, - 'nodes': [ { '$class': `${CommonMarkModel.NAMESPACE}.Text`, 'text': '' } ] + 'nodes': [{ '$class': `${CommonMarkModel.NAMESPACE}.Text`, 'text': '' }], }); } return rootNode; @@ -187,14 +173,10 @@ class FromMarkdownIt { /** * Transform a token stream to CommonMark DOM - * - * @param {*} tokens - the markdown-it token stream - * @returns {*} the CommonMark nodes */ - toCommonMark(tokens) { - return FromMarkdownIt.blockToCommonMark(this.rules,tokens); + toCommonMark(tokens: any[]): any { + return FromMarkdownIt.blockToCommonMark(this.rules, tokens); } - } -module.exports = FromMarkdownIt; +export default FromMarkdownIt; diff --git a/packages/markdown-common/lib/Stack.test.js b/packages/markdown-common/src/Stack.test.ts similarity index 75% rename from packages/markdown-common/lib/Stack.test.js rename to packages/markdown-common/src/Stack.test.ts index 3f8c591a..e7ea8d8c 100644 --- a/packages/markdown-common/lib/Stack.test.js +++ b/packages/markdown-common/src/Stack.test.ts @@ -12,17 +12,13 @@ * limitations under the License. */ -// @ts-nocheck -/* eslint-disable no-undef */ -'use strict'; - -const Stack = require('./Stack'); +import { Stack } from './Stack'; describe('Stack', () => { it('#integers', () => { const stack = new Stack(); expect(stack.peek()).toBeNull(); - stack.push(1,false); + stack.push(1, false); expect(stack.peek()).toBe(1); expect(stack.pop()).toBe(1); expect(stack.peek()).toBeNull(); @@ -31,11 +27,11 @@ describe('Stack', () => { it('#nodes', () => { const stack = new Stack(); expect(stack.peek()).toBeNull(); - stack.push({nodes:[]},false); + stack.push({ nodes: [] }, false); expect(stack.peek().nodes.length).toBe(0); - stack.push({nodes:[1]},true); + stack.push({ nodes: [1] }, true); expect(stack.peek().nodes.length).toBe(1); - stack.push({nodes:[2,3]},true); + stack.push({ nodes: [2, 3] }, true); expect(stack.peek().nodes.length).toBe(2); stack.clear(); expect(stack.peek()).toBeNull(); @@ -44,7 +40,7 @@ describe('Stack', () => { it('#invalid', () => { const stack = new Stack(); expect(stack.peek()).toBeNull(); - stack.push({foo:[]},false); - expect(() => stack.push({foo:[]},true)).toThrow('Cannot append. Invalid stack: [\n {\n "foo": []\n }\n]'); + stack.push({ foo: [] }, false); + expect(() => stack.push({ foo: [] }, true)).toThrow('Cannot append. Invalid stack: [\n {\n "foo": []\n }\n]'); }); }); diff --git a/packages/markdown-common/lib/Stack.js b/packages/markdown-common/src/Stack.ts similarity index 76% rename from packages/markdown-common/lib/Stack.js rename to packages/markdown-common/src/Stack.ts index 12a94592..a44c2f48 100644 --- a/packages/markdown-common/lib/Stack.js +++ b/packages/markdown-common/src/Stack.ts @@ -12,58 +12,51 @@ * limitations under the License. */ -'use strict'; - /** * Manages a stack of objects */ -class Stack { +export class Stack { + stack: any[]; - /** - * Constructor - */ constructor() { + this.stack = []; this.clear(); } /** * Clears the stack */ - clear() { + clear(): void { this.stack = []; } /** * Returns the top of the stack or null if the stack is empty - * @return {*} the top of the stack */ - peek() { - if(this.stack.length === 0) { + peek(): any { + if (this.stack.length === 0) { return null; } - return this.stack[this.stack.length - 1]; } /** * Pushes a new object to the top of the stack - * @param {*} obj the node to push - * @param {boolean} appendItem whether the item is also appended as a child to + * @param obj the node to push + * @param appendItem whether the item is also appended as a child to * the item at the top of the stack */ - push(obj, appendItem = true) { + push(obj: any, appendItem = true): void { if (appendItem) { this.append(obj); } - this.stack.push(obj); } /** * Appends an object to the 'nodes' array of the item at the top of the stack - * @param {*} obj the item to append to the top node */ - append(obj) { + append(obj: any): void { const top = this.peek(); if (top && top.nodes) { @@ -75,11 +68,10 @@ class Stack { /** * Pops the top of the stack. - * @return {*} the top of the stack */ - pop() { + pop(): any { return this.stack.pop(); } } -module.exports = Stack; +export default Stack; diff --git a/packages/markdown-common/lib/ToMarkdownVisitor.test.js b/packages/markdown-common/src/ToMarkdownVisitor.test.ts similarity index 65% rename from packages/markdown-common/lib/ToMarkdownVisitor.test.js rename to packages/markdown-common/src/ToMarkdownVisitor.test.ts index 9102c4c2..35f9b656 100644 --- a/packages/markdown-common/lib/ToMarkdownVisitor.test.js +++ b/packages/markdown-common/src/ToMarkdownVisitor.test.ts @@ -12,41 +12,37 @@ * limitations under the License. */ -// @ts-nocheck -/* eslint-disable no-undef */ -'use strict'; - -const { ModelManager, Factory, Serializer } = require('@accordproject/concerto-core'); -const CommonMarkModel = require('./externalModels/CommonMarkModel'); -const ToMarkdownVisitor = require('./ToMarkdownVisitor'); +import { ModelManager, Factory, Serializer } from '@accordproject/concerto-core'; +import * as CommonMarkModel from './externalModels/CommonMarkModel'; +import { ToMarkdownVisitor } from './ToMarkdownVisitor'; const commonmark = { - '$class':`${CommonMarkModel.NAMESPACE}.Document`, - 'xmlns':'http://commonmark.org/xml/1.0', - 'nodes':[{ - '$class':`${CommonMarkModel.NAMESPACE}.Paragraph`, - 'nodes':[{ - '$class':`${CommonMarkModel.NAMESPACE}.Text`, - 'text':'This is some text.' - }] - }] + '$class': `${CommonMarkModel.NAMESPACE}.Document`, + 'xmlns': 'http://commonmark.org/xml/1.0', + 'nodes': [{ + '$class': `${CommonMarkModel.NAMESPACE}.Paragraph`, + 'nodes': [{ + '$class': `${CommonMarkModel.NAMESPACE}.Text`, + 'text': 'This is some text.', + }], + }], }; const commonmarkErr = { - '$class':`${CommonMarkModel.NAMESPACE}.Document`, - 'xmlns':'http://commonmark.org/xml/1.0', - 'nodes':[{ - '$class':`${CommonMarkModel.NAMESPACE}.FOO`, - 'nodes':[{ - '$class':`${CommonMarkModel.NAMESPACE}.Text`, - 'text':'This is some text.' - }] - }] + '$class': `${CommonMarkModel.NAMESPACE}.Document`, + 'xmlns': 'http://commonmark.org/xml/1.0', + 'nodes': [{ + '$class': `${CommonMarkModel.NAMESPACE}.FOO`, + 'nodes': [{ + '$class': `${CommonMarkModel.NAMESPACE}.Text`, + 'text': 'This is some text.', + }], + }], }; const expected = 'This is some text.'; -let serializer; +let serializer: Serializer; beforeAll(() => { - const modelManager = new ModelManager({strict: true}); + const modelManager = new ModelManager(); modelManager.addCTOModel(CommonMarkModel.MODEL, 'commonmark.cto'); const factory = new Factory(modelManager); serializer = new Serializer(factory, modelManager); diff --git a/packages/markdown-common/lib/ToMarkdownVisitor.js b/packages/markdown-common/src/ToMarkdownVisitor.ts similarity index 63% rename from packages/markdown-common/lib/ToMarkdownVisitor.js rename to packages/markdown-common/src/ToMarkdownVisitor.ts index 1bdc51c7..242d3cc5 100644 --- a/packages/markdown-common/lib/ToMarkdownVisitor.js +++ b/packages/markdown-common/src/ToMarkdownVisitor.ts @@ -12,11 +12,9 @@ * limitations under the License. */ -'use strict'; - -const CommonMarkUtils = require('./CommonMarkUtils'); -const FromCommonMarkVisitor = require('./FromCommonMarkVisitor'); -const fromcommonmarkrules = require('./fromcommonmarkrules'); +import * as CommonMarkUtils from './CommonMarkUtils'; +import { FromCommonMarkVisitor } from './FromCommonMarkVisitor'; +import fromcommonmarkrules from './fromcommonmarkrules'; /** * Converts a CommonMark DOM to a markdown string. @@ -28,33 +26,24 @@ const fromcommonmarkrules = require('./fromcommonmarkrules'); * * The resulting AST *should* be equivalent however. */ -class ToMarkdownVisitor extends FromCommonMarkVisitor { - /** - * Construct the visitor. - */ +export class ToMarkdownVisitor extends FromCommonMarkVisitor { constructor() { - const resultString = (result) => { - return result; - }; - const resultSeq = (parameters,result) => { + const resultString = (result: string) => result; + const resultSeq = (parameters: any, result: any[]) => { result.forEach((next) => { parameters.result += next; }); }; - const setFirst = (thingType) => { - return thingType === 'Item' ? true : false; - }; + const setFirst = (thingType: string) => thingType === 'Item'; const rules = fromcommonmarkrules; - super({},resultString,resultSeq,rules,setFirst); + super({}, resultString, resultSeq, rules, setFirst); } /** * Converts a CommonMark DOM to a markdown string - * @param {object} input - CommonMark DOM (as a Concerto object) - * @returns {string} the markdown string */ - toMarkdown(input) { - const parameters = {}; + toMarkdown(input: any): string { + const parameters: any = {}; parameters.result = this.resultString(''); parameters.stack = CommonMarkUtils.blocksInit(); input.accept(this, parameters); @@ -62,4 +51,4 @@ class ToMarkdownVisitor extends FromCommonMarkVisitor { } } -module.exports = ToMarkdownVisitor; \ No newline at end of file +export default ToMarkdownVisitor; diff --git a/packages/markdown-common/lib/__snapshots__/CommonMarkSpec.test.js.snap b/packages/markdown-common/src/__snapshots__/CommonMarkSpec.test.ts.snap similarity index 100% rename from packages/markdown-common/lib/__snapshots__/CommonMarkSpec.test.js.snap rename to packages/markdown-common/src/__snapshots__/CommonMarkSpec.test.ts.snap diff --git a/packages/markdown-common/lib/__snapshots__/CommonMarkTransformer.test.js.snap b/packages/markdown-common/src/__snapshots__/CommonMarkTransformer.test.ts.snap similarity index 100% rename from packages/markdown-common/lib/__snapshots__/CommonMarkTransformer.test.js.snap rename to packages/markdown-common/src/__snapshots__/CommonMarkTransformer.test.ts.snap diff --git a/packages/markdown-common/src/externalModels/CiceroMarkModel.ts b/packages/markdown-common/src/externalModels/CiceroMarkModel.ts new file mode 100644 index 00000000..dec9d7cc --- /dev/null +++ b/packages/markdown-common/src/externalModels/CiceroMarkModel.ts @@ -0,0 +1,98 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export const NAMESPACE = 'org.accordproject.ciceromark@0.6.0'; + +export const MODEL = ` +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +concerto version "^3.0.0" +namespace org.accordproject.ciceromark@0.6.0 + +import org.accordproject.commonmark@0.5.0.Child from https://models.accordproject.org/markdown/commonmark@0.5.0.cto +import concerto.metamodel@1.0.0.Decorator from https://models.accordproject.org/concerto/metamodel@1.0.0.cto + +/** + * A model for Accord Project extensions to commonmark + */ + +abstract concept Element extends Child { + o String name + o String elementType optional + o Decorator[] decorators optional +} + +concept Variable extends Element { + o String value + o String identifiedBy optional +} + +concept FormattedVariable extends Variable { + o String format +} + +concept EnumVariable extends Variable { + o String[] enumValues +} + +concept Formula extends Element { + o String value + o String[] dependencies optional + o String code optional +} + +abstract concept Block extends Element { +} + +concept Clause extends Block { + o String src optional +} + +concept Contract extends Block { + o String src optional +} + +concept Conditional extends Block { + o Boolean isTrue + o Child[] whenTrue + o Child[] whenFalse +} + +concept Optional extends Block { + o Boolean hasSome + o Child[] whenSome + o Child[] whenNone +} + +concept ListBlock extends Block { + o String type + o String tight + o String start optional + o String delimiter optional +} + +`; + +export default { NAMESPACE, MODEL }; diff --git a/packages/markdown-common/src/externalModels/CommonMarkModel.ts b/packages/markdown-common/src/externalModels/CommonMarkModel.ts new file mode 100644 index 00000000..2ea97a09 --- /dev/null +++ b/packages/markdown-common/src/externalModels/CommonMarkModel.ts @@ -0,0 +1,151 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export const NAMESPACE = 'org.accordproject.commonmark@0.5.0'; + +export const MODEL = ` +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +concerto version "^3.0.0" +namespace org.accordproject.commonmark@0.5.0 + +/** + * A model for a commonmark format markdown file + */ + +abstract concept Node { + o String text optional + o Node[] nodes optional + o Integer startLine optional + o Integer endLine optional +} + +abstract concept Root extends Node { +} + +abstract concept Child extends Node { +} + +concept Text extends Child { +} + +concept Attribute { + o String name + o String value +} +concept TagInfo { + o String tagName + o String attributeString + o Attribute[] attributes + o String content + o Boolean closed +} + +concept CodeBlock extends Child { + o String info optional + o TagInfo tag optional +} + +concept Code extends Child { + o String info optional +} + +concept HtmlInline extends Child { + o TagInfo tag optional +} + +concept HtmlBlock extends Child { + o TagInfo tag optional +} + +concept Emph extends Child { +} + +concept Strong extends Child { +} + +concept BlockQuote extends Child { +} + +concept Heading extends Child { + o String level +} + +concept ThematicBreak extends Child { +} + +concept Softbreak extends Child { +} + +concept Linebreak extends Child { +} + +concept Link extends Child { + o String destination + o String title +} + +concept Image extends Child { + o String destination + o String title +} + +concept Paragraph extends Child { +} + +concept List extends Child { + o String type + o String start optional + o String tight + o String delimiter optional +} + +concept Item extends Child { +} + +concept Document extends Root { + o String xmlns +} + +concept Table extends Child{ +} + +concept TableHead extends Child{ +} + +concept TableBody extends Child{ +} + +concept TableRow extends Child{ +} + +concept HeaderCell extends Child{ +} + +concept TableCell extends Child{ +} +`; + +export default { NAMESPACE, MODEL }; diff --git a/packages/markdown-common/src/externalModels/ConcertoMetaModel.ts b/packages/markdown-common/src/externalModels/ConcertoMetaModel.ts new file mode 100644 index 00000000..8fb2a9b0 --- /dev/null +++ b/packages/markdown-common/src/externalModels/ConcertoMetaModel.ts @@ -0,0 +1,297 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export const NAMESPACE = 'concerto.metamodel@1.0.0'; + +export const MODEL = ` +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +concerto version "^3.0.0" + +@DotNetNamespace("AccordProject.Concerto.Metamodel") +namespace concerto.metamodel@1.0.0 + +concept Position { + o Integer line + o Integer column + o Integer offset +} + +concept Range { + o Position start + o Position end + o String source optional +} + +concept TypeIdentifier { + o String name + o String namespace optional +} + +abstract concept DecoratorLiteral { + o Range location optional +} + +concept DecoratorString extends DecoratorLiteral { + o String value +} + +concept DecoratorNumber extends DecoratorLiteral { + o Double value +} + +concept DecoratorBoolean extends DecoratorLiteral { + o Boolean value +} + +concept DecoratorTypeReference extends DecoratorLiteral { + o TypeIdentifier type + o Boolean isArray default=false +} + +concept Decorator { + o String name + o DecoratorLiteral[] arguments optional + o Range location optional +} + +concept Identified { +} + +concept IdentifiedBy extends Identified { + o String name +} + +abstract concept Declaration { + o String name regex=/^(\\p{Lu}|\\p{Ll}|\\p{Lt}|\\p{Lm}|\\p{Lo}|\\p{Nl}|\\$|_|\\\\u[0-9A-Fa-f]{4})(?:\\p{Lu}|\\p{Ll}|\\p{Lt}|\\p{Lm}|\\p{Lo}|\\p{Nl}|\\$|_|\\\\u[0-9A-Fa-f]{4}|\\p{Mn}|\\p{Mc}|\\p{Nd}|\\p{Pc}|\\u200C|\\u200D)*$/u + o Decorator[] decorators optional + o Range location optional +} + +abstract concept MapKeyType { + o Decorator[] decorators optional + o Range location optional +} + +abstract concept MapValueType { + o Decorator[] decorators optional + o Range location optional +} + +concept MapDeclaration extends Declaration { + o MapKeyType key + o MapValueType value +} + +concept StringMapKeyType extends MapKeyType {} +concept DateTimeMapKeyType extends MapKeyType {} + +concept ObjectMapKeyType extends MapKeyType { + o TypeIdentifier type +} + +concept BooleanMapValueType extends MapValueType {} +concept DateTimeMapValueType extends MapValueType {} +concept StringMapValueType extends MapValueType {} +concept IntegerMapValueType extends MapValueType {} +concept LongMapValueType extends MapValueType {} +concept DoubleMapValueType extends MapValueType {} + +concept ObjectMapValueType extends MapValueType { + o TypeIdentifier type +} + +concept RelationshipMapValueType extends MapValueType { + o TypeIdentifier type +} + +concept EnumDeclaration extends Declaration { + o EnumProperty[] properties +} + +concept EnumProperty { + o String name regex=/^(\\p{Lu}|\\p{Ll}|\\p{Lt}|\\p{Lm}|\\p{Lo}|\\p{Nl}|\\$|_|\\\\u[0-9A-Fa-f]{4})(?:\\p{Lu}|\\p{Ll}|\\p{Lt}|\\p{Lm}|\\p{Lo}|\\p{Nl}|\\$|_|\\\\u[0-9A-Fa-f]{4}|\\p{Mn}|\\p{Mc}|\\p{Nd}|\\p{Pc}|\\u200C|\\u200D)*$/u + o Decorator[] decorators optional + o Range location optional +} + +concept ConceptDeclaration extends Declaration { + o Boolean isAbstract default=false + o Identified identified optional + o TypeIdentifier superType optional + o Property[] properties +} + +concept AssetDeclaration extends ConceptDeclaration { +} + +concept ParticipantDeclaration extends ConceptDeclaration { +} + +concept TransactionDeclaration extends ConceptDeclaration { +} + +concept EventDeclaration extends ConceptDeclaration { +} + +abstract concept Property { + o String name regex=/^(\\p{Lu}|\\p{Ll}|\\p{Lt}|\\p{Lm}|\\p{Lo}|\\p{Nl}|\\$|_|\\\\u[0-9A-Fa-f]{4})(?:\\p{Lu}|\\p{Ll}|\\p{Lt}|\\p{Lm}|\\p{Lo}|\\p{Nl}|\\$|_|\\\\u[0-9A-Fa-f]{4}|\\p{Mn}|\\p{Mc}|\\p{Nd}|\\p{Pc}|\\u200C|\\u200D)*$/u + o Boolean isArray default=false + o Boolean isOptional default=false + o Decorator[] decorators optional + o Range location optional +} + +concept RelationshipProperty extends Property { + o TypeIdentifier type +} + +concept ObjectProperty extends Property { + o String defaultValue optional + o TypeIdentifier type +} + +concept BooleanProperty extends Property { + o Boolean defaultValue optional +} + +concept DateTimeProperty extends Property { +} + +concept StringProperty extends Property { + o String defaultValue optional + o StringRegexValidator validator optional + o StringLengthValidator lengthValidator optional +} + +concept StringRegexValidator { + o String pattern + o String flags +} + +concept StringLengthValidator { + o Integer minLength optional + o Integer maxLength optional +} + +concept DoubleProperty extends Property { + o Double defaultValue optional + o DoubleDomainValidator validator optional +} + +concept DoubleDomainValidator { + o Double lower optional + o Double upper optional +} + +concept IntegerProperty extends Property { + o Integer defaultValue optional + o IntegerDomainValidator validator optional +} + +concept IntegerDomainValidator { + o Integer lower optional + o Integer upper optional +} + +concept LongProperty extends Property { + o Long defaultValue optional + o LongDomainValidator validator optional +} + +concept LongDomainValidator { + o Long lower optional + o Long upper optional +} + +concept AliasedType{ + o String name + o String aliasedName +} +abstract concept Import { + o String namespace + o String uri optional +} + +concept ImportAll extends Import { +} + +concept ImportType extends Import { + o String name +} + +concept ImportTypes extends Import { + o String[] types + o AliasedType[] aliasedTypes optional +} + +concept Model { + o String namespace + o String sourceUri optional + o String concertoVersion optional + o Import[] imports optional + o Declaration[] declarations optional + o Decorator[] decorators optional +} + +concept Models { + o Model[] models +} + +abstract concept ScalarDeclaration extends Declaration { +} + +concept BooleanScalar extends ScalarDeclaration { + o Boolean defaultValue optional +} + +concept IntegerScalar extends ScalarDeclaration { + o Integer defaultValue optional + o IntegerDomainValidator validator optional +} + +concept LongScalar extends ScalarDeclaration { + o Long defaultValue optional + o LongDomainValidator validator optional +} + +concept DoubleScalar extends ScalarDeclaration { + o Double defaultValue optional + o DoubleDomainValidator validator optional +} + +concept StringScalar extends ScalarDeclaration { + o String defaultValue optional + o StringRegexValidator validator optional + o StringLengthValidator lengthValidator optional +} + +concept DateTimeScalar extends ScalarDeclaration { + o String defaultValue optional +} + +`; + +export default { NAMESPACE, MODEL }; diff --git a/packages/markdown-common/src/externalModels/TemplateMarkModel.ts b/packages/markdown-common/src/externalModels/TemplateMarkModel.ts new file mode 100644 index 00000000..0145ae54 --- /dev/null +++ b/packages/markdown-common/src/externalModels/TemplateMarkModel.ts @@ -0,0 +1,139 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export const NAMESPACE = 'org.accordproject.templatemark@0.5.0'; + +export const MODEL = ` +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +concerto version "^3.0.0" +namespace org.accordproject.templatemark@0.5.0 + +import org.accordproject.commonmark@0.5.0.Child from https://models.accordproject.org/markdown/commonmark@0.5.0.cto +import concerto.metamodel@1.0.0.Decorator from https://models.accordproject.org/concerto/metamodel@1.0.0.cto + +/** + * A model for Accord Project template extensions to commonmark + */ + +/** + * Identifiers for code strings in Formula Definition nodes + */ +enum CodeType { + o TYPESCRIPT + o ES_2020 +} + +/** + * User provided code, along with a code language identifier + */ +concept Code { + o CodeType type + o String contents +} + +abstract concept ElementDefinition extends Child { + o String name + o String elementType optional + o Decorator[] decorators optional +} + +concept VariableDefinition extends ElementDefinition { + o String identifiedBy optional +} + +concept FormattedVariableDefinition extends VariableDefinition { + o String format +} + +concept EnumVariableDefinition extends VariableDefinition { + o String[] enumValues +} + +concept FormulaDefinition extends ElementDefinition { + o String[] dependencies optional // name of variables on which the formula depends + o Code code +} + +abstract concept BlockDefinition extends ElementDefinition { +} + +concept ClauseDefinition extends BlockDefinition { + o Code condition optional +} + +concept ContractDefinition extends BlockDefinition { +} + +concept WithDefinition extends BlockDefinition { +} + +concept ConditionalDefinition extends BlockDefinition { + o Child[] whenTrue + o Child[] whenFalse + o Code condition optional + o String[] dependencies optional +} + +concept OptionalDefinition extends BlockDefinition { + o Child[] whenSome + o Child[] whenNone +} +concept JoinDefinition extends BlockDefinition { + // if separator is set, we just use that + o String separator optional + // if separator is not set, we use the Intl.ListFormat, paramaterized by locale, type and style + o String locale optional + o String type optional + o String style optional +} + +concept ListBlockDefinition extends BlockDefinition { + o String type + o String tight + o String start optional + o String delimiter optional +} + +concept ForeachBlockDefinition extends BlockDefinition { +} + +concept WithBlockDefinition extends BlockDefinition { +} + +concept ConditionalBlockDefinition extends BlockDefinition { + o Child[] whenTrue + o Child[] whenFalse + o Code condition optional +} + +concept OptionalBlockDefinition extends BlockDefinition { + o Child[] whenSome + o Child[] whenNone +} + +`; + +export default { NAMESPACE, MODEL }; diff --git a/packages/markdown-common/src/fromcommonmarkrules.ts b/packages/markdown-common/src/fromcommonmarkrules.ts new file mode 100644 index 00000000..5243772c --- /dev/null +++ b/packages/markdown-common/src/fromcommonmarkrules.ts @@ -0,0 +1,187 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as CommonMarkUtils from './CommonMarkUtils'; +import type { Rules } from './FromCommonMarkVisitor'; + +/** + * get text from a thing + */ +function getText(thing: any, field: string, escapeFun?: (s: string) => string): string { + const text = thing[field] ? thing[field] : ''; + if (escapeFun) { + return escapeFun(text); + } else { + return text; + } +} + +const rules: Rules = {}; + +// Inlines +rules.Code = (visitor, thing, children, parameters, resultString, resultSeq) => { + const next = `\`${getText(thing, 'text')}\``; + const result = [resultString(next)]; + resultSeq(parameters, result); +}; +rules.Emph = (visitor, thing, children, parameters, resultString, resultSeq) => { + const result = [resultString('*'), children, resultString('*')]; + resultSeq(parameters, result); +}; +rules.Strong = (visitor, thing, children, parameters, resultString, resultSeq) => { + const result = [resultString('**'), children, resultString('**')]; + resultSeq(parameters, result); +}; +rules.Link = (visitor, thing, children, parameters, resultString, resultSeq) => { + const next1 = '['; + const next2 = `](${thing.destination} "${getText(thing, 'title')}")`; + const result = [resultString(next1), children, resultString(next2)]; + resultSeq(parameters, result); +}; +rules.Image = (visitor, thing, children, parameters, resultString, resultSeq) => { + const next1 = '!['; + const next2 = `](${thing.destination} "${getText(thing, 'title')}")`; + const result = [resultString(next1), children, resultString(next2)]; + resultSeq(parameters, result); +}; +rules.HtmlInline = (visitor, thing, children, parameters, resultString, resultSeq) => { + const next = getText(thing, 'text'); + const result = [resultString(next)]; + resultSeq(parameters, result); +}; +rules.Linebreak = (visitor, thing, children, parameters, resultString, resultSeq) => { + const next = `\\${CommonMarkUtils.mkPrefix(parameters, 1)}`; + const result = [resultString(next)]; + resultSeq(parameters, result); +}; +rules.Softbreak = (visitor, thing, children, parameters, resultString, resultSeq) => { + const next = CommonMarkUtils.mkPrefix(parameters, 1); + const result = [resultString(next)]; + resultSeq(parameters, result); +}; +rules.Text = (visitor, thing, children, parameters, resultString, resultSeq) => { + const next = getText(thing, 'text', CommonMarkUtils.escapeText); + const result = [resultString(next)]; + resultSeq(parameters, result); +}; +// Leaf blocks +rules.ThematicBreak = (visitor, thing, children, parameters, resultString, resultSeq) => { + const next1 = CommonMarkUtils.mkPrefix(parameters, 2); + const next2 = '---'; + const result = [resultString(next1), resultString(next2)]; + resultSeq(parameters, result); +}; +rules.Heading = (visitor, thing, children, parameters, resultString, resultSeq) => { + const level = parseInt(thing.level); + const next1 = CommonMarkUtils.mkPrefix(parameters, 2); + if (level < 3 && children !== '') { + CommonMarkUtils.nextNode(parameters); + const next3 = CommonMarkUtils.mkPrefix(parameters, 1); + const next4 = CommonMarkUtils.mkSetextHeading(level); + const result = [resultString(next1), children, resultString(next3), resultString(next4)]; + resultSeq(parameters, result); + } else { + const next2 = CommonMarkUtils.mkATXHeading(level); + const next3 = ' '; + const result = [resultString(next1), resultString(next2), resultString(next3), children]; + resultSeq(parameters, result); + } +}; +rules.CodeBlock = (visitor, thing, children, parameters, resultString, resultSeq) => { + const prefix = CommonMarkUtils.mkPrefix(parameters, 2); + const newLine = CommonMarkUtils.mkNewLine(parameters); + const next1 = `${prefix}\`\`\` ${getText(thing, 'info')}`; + const lines = getText(thing, 'text', CommonMarkUtils.escapeCodeBlock).split('\n'); + const next2 = `${newLine}${lines.join(newLine)}\`\`\``; + const result = [resultString(next1), resultString(next2)]; + resultSeq(parameters, result); +}; +rules.HtmlBlock = (visitor, thing, children, parameters, resultString, resultSeq) => { + const nodeText = getText(thing, 'text'); + const next1 = CommonMarkUtils.mkPrefix(parameters, 2); + const next2 = nodeText; + const result = [resultString(next1), resultString(next2)]; + resultSeq(parameters, result); +}; +rules.Paragraph = (visitor, thing, children, parameters, resultString, resultSeq) => { + const next1 = CommonMarkUtils.mkPrefix(parameters, parameters.first ? 1 : 2); + const result = [resultString(next1), children]; + resultSeq(parameters, result); +}; +// Container blocks +rules.BlockQuote = (visitor, thing, children, parameters, resultString, resultSeq) => { + const result = [children]; + resultSeq(parameters, result); +}; +rules.Item = (visitor, thing, children, parameters, resultString, resultSeq) => { + const level = parameters.tight && parameters.tight === 'false' && parameters.index !== parameters.indexInit ? 2 : 1; + if (parameters.type === 'ordered') { + const next1 = `${CommonMarkUtils.mkPrefix(parameters, level)}${parameters.index}. `; + const result = [resultString(next1), children]; + resultSeq(parameters, result); + } else { + const next1 = `${CommonMarkUtils.mkPrefix(parameters, level)}- `; + const result = [resultString(next1), children]; + resultSeq(parameters, result); + } +}; +rules.List = (visitor, thing, children, parameters, resultString, resultSeq) => { + const result = [children]; + resultSeq(parameters, result); +}; +rules.Document = (visitor, thing, children, parameters, resultString, resultSeq) => { + const result = [children]; + resultSeq(parameters, result); +}; + +rules.Table = (visitor, thing, children, parameters, resultString, resultSeq) => { + const result = [children]; + resultSeq(parameters, result); +}; + +rules.TableBody = (visitor, thing, children, parameters, resultString, resultSeq) => { + const result = [children]; + resultSeq(parameters, result); +}; + +rules.TableRow = (visitor, thing, children, parameters, resultString, resultSeq) => { + const next1 = '|'; + const newLine = CommonMarkUtils.mkNewLine(parameters); + const result = [children, resultString(next1), newLine]; + resultSeq(parameters, result); +}; + +rules.TableCell = (visitor, thing, children, parameters, resultString, resultSeq) => { + const next1 = '|'; + const next2 = ' '; + const result = [resultString(next1), resultString(next2), children, resultString(next2)]; + resultSeq(parameters, result); +}; + +rules.TableHead = (visitor, thing, children, parameters, resultString, resultSeq) => { + const col = thing.nodes[0].nodes.length; + const next1 = CommonMarkUtils.mkTableHeading(col); + const newLine = CommonMarkUtils.mkNewLine(parameters); + const result = [children, resultString(next1), resultString(newLine)]; + resultSeq(parameters, result); +}; + +rules.HeaderCell = (visitor, thing, children, parameters, resultString, resultSeq) => { + const next1 = '|'; + const next2 = ' '; + const result = [resultString(next1), resultString(next2), children, resultString(next2)]; + resultSeq(parameters, result); +}; + +export default rules; diff --git a/packages/markdown-common/src/index.ts b/packages/markdown-common/src/index.ts new file mode 100644 index 00000000..fbaec2c1 --- /dev/null +++ b/packages/markdown-common/src/index.ts @@ -0,0 +1,53 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Stack } from './Stack'; +import * as CommonMarkModel from './externalModels/CommonMarkModel'; +import * as CiceroMarkModel from './externalModels/CiceroMarkModel'; +import * as ConcertoMetaModel from './externalModels/ConcertoMetaModel'; +import * as TemplateMarkModel from './externalModels/TemplateMarkModel'; +import * as CommonMarkUtils from './CommonMarkUtils'; +import { FromCommonMarkVisitor } from './FromCommonMarkVisitor'; +import fromcommonmarkrules from './fromcommonmarkrules'; +import { CommonMarkTransformer } from './CommonMarkTransformer'; +import { ToMarkdownVisitor } from './ToMarkdownVisitor'; +import { FromMarkdownIt } from './FromMarkdownIt'; + +export { + Stack, + CommonMarkModel, + CiceroMarkModel, + ConcertoMetaModel, + TemplateMarkModel, + CommonMarkUtils, + FromCommonMarkVisitor, + fromcommonmarkrules, + CommonMarkTransformer, + ToMarkdownVisitor, + FromMarkdownIt, +}; + +export default { + Stack, + CommonMarkModel, + CiceroMarkModel, + ConcertoMetaModel, + TemplateMarkModel, + CommonMarkUtils, + FromCommonMarkVisitor, + fromcommonmarkrules, + CommonMarkTransformer, + ToMarkdownVisitor, + FromMarkdownIt, +}; diff --git a/packages/markdown-it-cicero/index.js b/packages/markdown-common/src/jest.d.ts old mode 100755 new mode 100644 similarity index 83% rename from packages/markdown-it-cicero/index.js rename to packages/markdown-common/src/jest.d.ts index 664ae2f2..0a6e64fd --- a/packages/markdown-it-cicero/index.js +++ b/packages/markdown-common/src/jest.d.ts @@ -12,11 +12,8 @@ * limitations under the License. */ -'use strict'; - -/** - * Export the plugins - * @module markdown-it-template - */ - -module.exports = require('./lib'); +declare namespace jest { + interface Matchers { + toMarkdownRoundtrip(): R; + } +} diff --git a/packages/markdown-common/src/removeFormatting.ts b/packages/markdown-common/src/removeFormatting.ts new file mode 100644 index 00000000..61c3801f --- /dev/null +++ b/packages/markdown-common/src/removeFormatting.ts @@ -0,0 +1,111 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Stack } from './Stack'; +import * as CommonMarkModel from './externalModels/CommonMarkModel'; + +/** + * Maps the keys in an object + */ +function mapObject(obj: any, stack: Stack): void { + switch (obj.$class) { + // remove these, visit children + case `${CommonMarkModel.NAMESPACE}.Emph`: + case `${CommonMarkModel.NAMESPACE}.Strong`: + case `${CommonMarkModel.NAMESPACE}.Document`: + case `${CommonMarkModel.NAMESPACE}.BlockQuote`: + obj.nodes.forEach((element: any) => { + mapObject(element, stack); + }); + break; + // wrap in a para and process child nodes + case `${CommonMarkModel.NAMESPACE}.Paragraph`: + case `${CommonMarkModel.NAMESPACE}.Heading`: { + stack.push({ + $class: `${CommonMarkModel.NAMESPACE}.Paragraph`, + nodes: [], + }); + if (obj.nodes) { + obj.nodes.forEach((element: any) => { + mapObject(element, stack); + }); + } + stack.pop(); + break; + } + // wrap in a para and grab text + case `${CommonMarkModel.NAMESPACE}.CodeBlock`: + case `${CommonMarkModel.NAMESPACE}.HtmlBlock`: + stack.append({ + $class: `${CommonMarkModel.NAMESPACE}.Paragraph`, + nodes: [ + { + $class: `${CommonMarkModel.NAMESPACE}.Text`, + text: obj.text, + }, + ], + }); + break; + // inline text + case `${CommonMarkModel.NAMESPACE}.Code`: + case `${CommonMarkModel.NAMESPACE}.HtmlInline`: + stack.append({ + $class: `${CommonMarkModel.NAMESPACE}.Text`, + text: obj.text, + }); + break; + // get destination + case `${CommonMarkModel.NAMESPACE}.Link`: + stack.append({ + $class: `${CommonMarkModel.NAMESPACE}.Text`, + text: obj.destination, + }); + break; + // get title + case `${CommonMarkModel.NAMESPACE}.Image`: + stack.append({ + $class: `${CommonMarkModel.NAMESPACE}.Text`, + text: obj.title, + }); + break; + // do not insert a \ for linebreaks + case `${CommonMarkModel.NAMESPACE}.Linebreak`: + stack.append({ + $class: `${CommonMarkModel.NAMESPACE}.Text`, + text: '\n', + }); + break; + // copy + default: + stack.append(obj); + break; + } +} + +/** + * Removes rich text formatting nodes. + */ +export function removeFormatting(obj: any): any { + const root = { + $class: `${CommonMarkModel.NAMESPACE}.Document`, + xmlns: obj.xmlns, + nodes: [], + }; + const stack = new Stack(); + stack.push(root, false); + mapObject(obj, stack); + return root; +} + +export default removeFormatting; diff --git a/packages/markdown-common/lib/tocommonmarkrules.js b/packages/markdown-common/src/tocommonmarkrules.ts similarity index 68% rename from packages/markdown-common/lib/tocommonmarkrules.js rename to packages/markdown-common/src/tocommonmarkrules.ts index b56a2334..faf6f8df 100644 --- a/packages/markdown-common/lib/tocommonmarkrules.js +++ b/packages/markdown-common/src/tocommonmarkrules.ts @@ -12,119 +12,132 @@ * limitations under the License. */ -'use strict'; +import { unescapeCodeBlock, parseHtmlBlock, headingLevel, getAttr, trimEndline } from './CommonMarkUtils'; +import * as CommonMarkModel from './externalModels/CommonMarkModel'; -const { unescapeCodeBlock, parseHtmlBlock, headingLevel, getAttr, trimEndline } = require('./CommonMarkUtils'); -const CommonMarkModel = require('./externalModels/CommonMarkModel'); +export interface MarkdownItRule { + tag: string; + leaf: boolean; + open: boolean; + close: boolean; + skipEmpty?: boolean; + enter?: (node: any, token: any, callback: (tokens: any[]) => any[]) => void; + exit?: (node: any, token: any, callback: (tokens: any[]) => any[]) => void; +} + +export interface MarkdownItRules { + inlines: Record; + blocks: Record; +} // Inline rules -const textRule = { +const textRule: MarkdownItRule = { tag: `${CommonMarkModel.NAMESPACE}.Text`, leaf: true, open: false, close: false, - enter: (node,token,callback) => { node.text = token.content; }, + enter: (node, token) => { node.text = token.content; }, skipEmpty: true, }; -const codeInlineRule = { +const codeInlineRule: MarkdownItRule = { tag: `${CommonMarkModel.NAMESPACE}.Code`, leaf: true, open: false, close: false, - enter: (node,token,callback) => { node.text = token.content; }, + enter: (node, token) => { node.text = token.content; }, skipEmpty: false, }; -const softbreakRule = { +const softbreakRule: MarkdownItRule = { tag: `${CommonMarkModel.NAMESPACE}.Softbreak`, leaf: true, open: false, close: false, skipEmpty: false, }; -const hardbreakRule = { +const hardbreakRule: MarkdownItRule = { tag: `${CommonMarkModel.NAMESPACE}.Linebreak`, leaf: true, open: false, close: false, skipEmpty: false, }; -const htmlInlineRule = { +const htmlInlineRule: MarkdownItRule = { tag: `${CommonMarkModel.NAMESPACE}.HtmlInline`, leaf: true, open: false, close: false, - enter: (node,token,callback) => { + enter: (node, token) => { node.text = token.content; node.tag = parseHtmlBlock(token.content); }, skipEmpty: false, }; -const strongOpenRule = { +const strongOpenRule: MarkdownItRule = { tag: `${CommonMarkModel.NAMESPACE}.Strong`, leaf: false, open: true, close: false, skipEmpty: false, }; -const strongCloseRule = { +const strongCloseRule: MarkdownItRule = { tag: `${CommonMarkModel.NAMESPACE}.Strong`, leaf: false, open: false, close: true, skipEmpty: false, }; -const emphOpenRule = { +const emphOpenRule: MarkdownItRule = { tag: `${CommonMarkModel.NAMESPACE}.Emph`, leaf: false, open: true, close: false, skipEmpty: false, }; -const emphCloseRule = { +const emphCloseRule: MarkdownItRule = { tag: `${CommonMarkModel.NAMESPACE}.Emph`, leaf: false, open: false, close: true, skipEmpty: false, }; -const linkOpenRule = { +const linkOpenRule: MarkdownItRule = { tag: `${CommonMarkModel.NAMESPACE}.Link`, leaf: false, open: true, close: false, - enter: (node,token,callback) => { - node.destination = getAttr(token.attrs,'href',''); - node.title = getAttr(token.attrs,'title',''); + enter: (node, token) => { + node.destination = getAttr(token.attrs, 'href', ''); + node.title = getAttr(token.attrs, 'title', ''); }, skipEmpty: false, }; -const linkCloseRule = { +const linkCloseRule: MarkdownItRule = { tag: `${CommonMarkModel.NAMESPACE}.Link`, leaf: false, open: false, close: true, skipEmpty: false, }; -const imageRule = { +const imageRule: MarkdownItRule = { tag: `${CommonMarkModel.NAMESPACE}.Image`, leaf: false, open: true, close: true, - enter: (node,token,callback) => { - node.destination = getAttr(token.attrs,'src',''); - node.title = getAttr(token.attrs,'title',''); + enter: (node, token, callback) => { + node.destination = getAttr(token.attrs, 'src', ''); + node.title = getAttr(token.attrs, 'title', ''); node.nodes = callback(token.children); }, skipEmpty: false, }; // Block rules -const codeBlockRule = { +const codeBlockRule: MarkdownItRule = { tag: `${CommonMarkModel.NAMESPACE}.CodeBlock`, leaf: true, open: false, close: false, - enter: (node,token,callback) => { + enter: (node, token) => { const info = token.info.trim(); node.info = info ? info : null; node.tag = parseHtmlBlock(info); @@ -132,208 +145,204 @@ const codeBlockRule = { }, }; const fenceRule = codeBlockRule; -const htmlBlockRule = { +const htmlBlockRule: MarkdownItRule = { tag: `${CommonMarkModel.NAMESPACE}.HtmlBlock`, leaf: true, open: false, close: false, - enter: (node,token,callback) => { + enter: (node, token) => { const content = trimEndline(token.content); node.tag = parseHtmlBlock(content); node.text = content; }, }; -const hrRule = { +const hrRule: MarkdownItRule = { tag: `${CommonMarkModel.NAMESPACE}.ThematicBreak`, leaf: true, open: false, close: false, - enter: (node,token,callback) => { - }, + enter: () => { /* no-op */ }, }; -const paragraphOpenRule = { +const paragraphOpenRule: MarkdownItRule = { tag: `${CommonMarkModel.NAMESPACE}.Paragraph`, leaf: false, open: true, close: false, - enter: (node,token,callback) => { - }, + enter: () => { /* no-op */ }, }; -const paragraphCloseRule = { +const paragraphCloseRule: MarkdownItRule = { tag: `${CommonMarkModel.NAMESPACE}.Paragraph`, leaf: false, open: false, close: true, }; -const headingOpenRule = { +const headingOpenRule: MarkdownItRule = { tag: `${CommonMarkModel.NAMESPACE}.Heading`, leaf: false, open: true, close: false, - enter: (node,token,callback) => { + enter: (node, token) => { node.level = headingLevel(token.tag); }, }; -const headingCloseRule = { +const headingCloseRule: MarkdownItRule = { tag: `${CommonMarkModel.NAMESPACE}.Heading`, leaf: false, open: false, close: true, }; -const blockQuoteOpenRule = { +const blockQuoteOpenRule: MarkdownItRule = { tag: `${CommonMarkModel.NAMESPACE}.BlockQuote`, leaf: false, open: true, close: false, - enter: (node,token,callback) => { - }, + enter: () => { /* no-op */ }, }; -const blockQuoteCloseRule = { +const blockQuoteCloseRule: MarkdownItRule = { tag: `${CommonMarkModel.NAMESPACE}.BlockQuote`, leaf: false, open: false, close: true, }; -const bulletListOpenRule = { +const bulletListOpenRule: MarkdownItRule = { tag: `${CommonMarkModel.NAMESPACE}.List`, leaf: false, open: true, close: false, - enter: (node,token,callback) => { + enter: (node) => { node.type = 'bullet'; - node.tight = 'true'; // XXX Default but can be overridden when closing the list tag + node.tight = 'true'; }, }; -const bulletListCloseRule = { +const bulletListCloseRule: MarkdownItRule = { tag: `${CommonMarkModel.NAMESPACE}.List`, leaf: false, open: false, close: true, }; -const orderedListOpenRule = { +const orderedListOpenRule: MarkdownItRule = { tag: `${CommonMarkModel.NAMESPACE}.List`, leaf: false, open: true, close: false, - enter: (node,token,callback) => { + enter: (node, token) => { node.type = 'ordered'; - node.start = getAttr(token.attrs,'start','1'); - node.tight = 'true'; // XXX Default but can be overridden when closing the list tag + node.start = getAttr(token.attrs, 'start', '1'); + node.tight = 'true'; node.delimiter = token.markup === ')' ? 'paren' : 'period'; }, }; -const orderedListCloseRule = { +const orderedListCloseRule: MarkdownItRule = { tag: `${CommonMarkModel.NAMESPACE}.List`, leaf: false, open: false, close: true, }; -const listItemOpenRule = { +const listItemOpenRule: MarkdownItRule = { tag: `${CommonMarkModel.NAMESPACE}.Item`, leaf: false, open: true, close: false, - enter: (node,token,callback) => { - }, + enter: () => { /* no-op */ }, }; -const listItemCloseRule = { +const listItemCloseRule: MarkdownItRule = { tag: `${CommonMarkModel.NAMESPACE}.List`, leaf: false, open: false, close: true, }; -const tableOpenRule = { +const tableOpenRule: MarkdownItRule = { tag: `${CommonMarkModel.NAMESPACE}.Table`, leaf: false, open: true, close: false, - enter: (node, token, callback) => {}, + enter: () => { /* no-op */ }, }; -const tableCloseRule = { +const tableCloseRule: MarkdownItRule = { tag: `${CommonMarkModel.NAMESPACE}.Table`, leaf: false, open: false, close: true, }; -const tableHeadOpenRule = { +const tableHeadOpenRule: MarkdownItRule = { tag: `${CommonMarkModel.NAMESPACE}.TableHead`, leaf: false, open: true, close: false, - enter: (node, token, callback) => {}, + enter: () => { /* no-op */ }, }; -const tableHeadCloseRule = { +const tableHeadCloseRule: MarkdownItRule = { tag: `${CommonMarkModel.NAMESPACE}.TableHead`, leaf: false, open: false, close: true, }; -const tableBodyOpenRule = { +const tableBodyOpenRule: MarkdownItRule = { tag: `${CommonMarkModel.NAMESPACE}.TableBody`, leaf: false, open: true, close: false, - enter: (node, token, callback) => {}, + enter: () => { /* no-op */ }, }; -const tableBodyCloseRule = { +const tableBodyCloseRule: MarkdownItRule = { tag: `${CommonMarkModel.NAMESPACE}.TableBody`, leaf: false, open: false, close: true, }; -const tableRowOpenRule = { +const tableRowOpenRule: MarkdownItRule = { tag: `${CommonMarkModel.NAMESPACE}.TableRow`, leaf: false, open: true, close: false, - enter: (node, token, callback) => {}, + enter: () => { /* no-op */ }, }; -const tableRowCloseRule = { +const tableRowCloseRule: MarkdownItRule = { tag: `${CommonMarkModel.NAMESPACE}.TableRow`, leaf: false, open: false, close: true, }; -const headerCellOpenRule = { +const headerCellOpenRule: MarkdownItRule = { tag: `${CommonMarkModel.NAMESPACE}.HeaderCell`, leaf: false, open: true, close: false, - enter: (node, token, callback) => {}, + enter: () => { /* no-op */ }, }; -const headerCellCloseRule = { +const headerCellCloseRule: MarkdownItRule = { tag: `${CommonMarkModel.NAMESPACE}.HeaderCell`, leaf: false, open: false, close: true, }; -const tableCellOpenRule = { +const tableCellOpenRule: MarkdownItRule = { tag: `${CommonMarkModel.NAMESPACE}.TableCell`, leaf: false, open: true, close: false, - enter: (node, token, callback) => {}, + enter: () => { /* no-op */ }, }; -const tableCellCloseRule = { +const tableCellCloseRule: MarkdownItRule = { tag: `${CommonMarkModel.NAMESPACE}.TableCell`, leaf: false, open: false, close: true, }; -const rules = { inlines: {}, blocks: {}}; +const rules: MarkdownItRules = { inlines: {}, blocks: {} }; rules.inlines.text = textRule; rules.inlines.code_inline = codeInlineRule; rules.inlines.softbreak = softbreakRule; @@ -376,4 +385,4 @@ rules.blocks.th_close = headerCellCloseRule; rules.blocks.td_open = tableCellOpenRule; rules.blocks.td_close = tableCellCloseRule; -module.exports = rules; +export default rules; diff --git a/packages/markdown-common/tsconfig.json b/packages/markdown-common/tsconfig.json index 11a143cd..fb3e9f82 100644 --- a/packages/markdown-common/tsconfig.json +++ b/packages/markdown-common/tsconfig.json @@ -1,11 +1,11 @@ { + "extends": "../../tsconfig.base.json", "compilerOptions": { - "allowJs": true, + "rootDir": "src", + "outDir": "lib", "declaration": true, - "emitDeclarationOnly": true, - "outDir": "types", - "strict": false + "sourceMap": true }, - "include": ["index.js", "lib/**/*.js"], - "exclude": ["lib/**/*.test.js"] -} \ No newline at end of file + "include": ["src/**/*.ts"], + "exclude": ["src/**/*.test.ts", "lib", "node_modules"] +} diff --git a/packages/markdown-common/tsconfig.test.json b/packages/markdown-common/tsconfig.test.json new file mode 100644 index 00000000..58810225 --- /dev/null +++ b/packages/markdown-common/tsconfig.test.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "rootDir": ".", + "noEmit": true + }, + "include": ["src/**/*.ts"], + "exclude": ["lib", "node_modules"] +} diff --git a/packages/markdown-common/types/index.d.ts b/packages/markdown-common/types/index.d.ts deleted file mode 100644 index 6d14d56d..00000000 --- a/packages/markdown-common/types/index.d.ts +++ /dev/null @@ -1,13 +0,0 @@ -export const Stack: typeof import("./lib/Stack"); -export const CommonMarkModel: typeof import("./lib/externalModels/CommonMarkModel"); -export const CiceroMarkModel: typeof import("./lib/externalModels/CiceroMarkModel"); -export const ConcertoMetaModel: typeof import("./lib/externalModels/ConcertoMetaModel"); -export const TemplateMarkModel: typeof import("./lib/externalModels/TemplateMarkModel"); -export const CommonMarkUtils: typeof import("./lib/CommonMarkUtils"); -export const FromCommonMarkVisitor: typeof import("./lib/FromCommonMarkVisitor"); -export const fromcommonmarkrules: { - [x: string]: Function; -}; -export const CommonMarkTransformer: typeof import("./lib/CommonMarkTransformer"); -export const ToMarkdownVisitor: typeof import("./lib/ToMarkdownVisitor"); -export const FromMarkdownIt: typeof import("./lib/FromMarkdownIt"); diff --git a/packages/markdown-common/types/lib/CommonMarkTransformer.d.ts b/packages/markdown-common/types/lib/CommonMarkTransformer.d.ts deleted file mode 100644 index 4b50c4fd..00000000 --- a/packages/markdown-common/types/lib/CommonMarkTransformer.d.ts +++ /dev/null @@ -1,54 +0,0 @@ -export = CommonMarkTransformer; -/** - * Parses markdown using the commonmark parser into the - * intermediate representation: a JSON object that adheres to - * the 'org.accordproject.commonmark' Concerto model. - */ -declare class CommonMarkTransformer { - serializer: Serializer; - /** - * Converts a CommonMark DOM to a markdown string - * @param {INode} input - CommonMark DOM (in JSON) - * @returns {string} the markdown string - */ - toMarkdown(input: INode): string; - /** - * Converts a CommonMark DOM to a CommonMark DOM with formatting removed - * @param {IDocument} input - CommonMark DOM (in JSON) - * @returns {IDocument} the CommonMark DOM with formatting nodes removed - */ - removeFormatting(input: IDocument): IDocument; - /** - * Converts a markdown string into a token stream - * - * @param {string} markdown the string to parse - * @returns {object[]} a markdown-it token stream - */ - toTokens(markdown: string): object[]; - /** - * Converts a token stream into a CommonMark DOM object. - * - * @param {object[]} tokenStream the token stream - * @returns {IDocument} a Concerto object (DOM) for the markdown content - */ - fromTokens(tokenStream: object[]): IDocument; - /** - * Converts a markdown string into a CommonMark DOM object. - * - * @param {string} markdown the string to parse - * @returns {IDocument} a CommonMark DOM (JSON) for the markdown content - */ - fromMarkdown(markdown: string): IDocument; - /** - * Retrieve the serializer used by the parser - * - * @returns {Serializer} a serializer capable of dealing with the Concerto - */ - getSerializer(): Serializer; -} -declare namespace CommonMarkTransformer { - export { IDocument, INode }; -} -import { Serializer } from "@accordproject/concerto-core"; -type IDocument = import("@accordproject/markdown-common/types/model/commonmark").IDocument; -type INode = import("@accordproject/markdown-common/types/model/commonmark").INode; diff --git a/packages/markdown-common/types/lib/CommonMarkUtils.d.ts b/packages/markdown-common/types/lib/CommonMarkUtils.d.ts deleted file mode 100644 index d829a9fd..00000000 --- a/packages/markdown-common/types/lib/CommonMarkUtils.d.ts +++ /dev/null @@ -1,108 +0,0 @@ -/** - * CommonMark Utilities - */ -/** - * Initial block stack - * @return {*} the block stack - */ -export function blocksInit(): any; -/** - * Next node - * @param {*} parameters the current parameters - */ -export function nextNode(parameters: any): void; -/** - * Set parameters for general blocks - * @param {*} ast - the current ast node - * @param {*} parametersOut - the current parameters - * @param {*} init - initial result value - * @param {*} setFirst whether entering this block should set first - * @return {*} the new parameters with block quote level incremented - */ -export function mkParameters(ast: any, parametersOut: any, init: any, setFirst: any): any; -/** - * Create a single new line - * @param {*} parameters - the parameters - * @return {string} the prefix - */ -export function mkNewLine(parameters: any): string; -/** - * Create a line prefix - * @param {*} parameters - the parameters - * @param {*} nb - number of newlines - * @return {string} the prefix - */ -export function mkPrefix(parameters: any, nb: any): string; -/** - * Create Setext heading - * @param {number} level - the heading level - * @return {string} the markup for the heading - */ -export function mkSetextHeading(level: number): string; -/** - * Create ATX heading - * @param {number} level - the heading level - * @return {string} the markup for the heading - */ -export function mkATXHeading(level: number): string; -/** - * Create table heading - * @param {number} col - the number of columns - * @return {string} the markup for the table heading - */ -export function mkTableHeading(col: number): string; -/** - * Adding escapes for text nodes - * @param {string} input - unescaped - * @return {string} escaped - */ -export function escapeText(input: string): string; -/** - * Adding escapes for code blocks - * @param {string} input - unescaped - * @return {string} escaped - */ -export function escapeCodeBlock(input: string): string; -/** - * Removing escapes - * @param {string} input - escaped - * @return {string} unescaped - */ -export function unescapeCodeBlock(input: string): string; -/** - * Parses an HTML block and extracts the attributes, tag name and tag contents. - * Note that this will return null for strings like this: - * @param {string} string - the HTML block to parse - * @return {Object} - a tag object that holds the data for the html block - */ -export function parseHtmlBlock(string: string): any; -/** - * Merge adjacent Html nodes in a list of nodes - * @param {[*]} nodes - a list of nodes - * @param {boolean} tagInfo - whether to extract Html tags - * @returns {*} a new list of nodes with open/closed Html nodes merged - */ -export function mergeAdjacentHtmlNodes(nodes: [any], tagInfo: boolean): any; -/** - * Determine the heading level - * - * @param {string} tag the heading tag - * @returns {string} the heading level - */ -export function headingLevel(tag: string): string; -/** - * Get an attribute value - * - * @param {*} attrs open ordered list attributes - * @param {string} name attribute name - * @param {*} def a default value - * @returns {string} the initial index - */ -export function getAttr(attrs: any, name: string, def: any): string; -/** - * Trim single ending newline - * - * @param {string} text the input text - * @returns {string} the trimmed text - */ -export function trimEndline(text: string): string; diff --git a/packages/markdown-common/types/lib/FromCommonMarkVisitor.d.ts b/packages/markdown-common/types/lib/FromCommonMarkVisitor.d.ts deleted file mode 100644 index 46135ff5..00000000 --- a/packages/markdown-common/types/lib/FromCommonMarkVisitor.d.ts +++ /dev/null @@ -1,35 +0,0 @@ -export = FromCommonMarkVisitor; -/** - * Converts a CommonMark DOM to something else - */ -declare class FromCommonMarkVisitor { - /** - * Construct the visitor. - * @param {object} options configuration options - * @param {*} resultString how to create a result from a string - * @param {*} resultSeq how to sequentially combine results - * @param {object} rules how to process each node type - * @param {*} setFirst whether entering this block should set first - */ - constructor(options: object, resultString: any, resultSeq: any, rules: object, setFirst: any); - options: any; - resultString: any; - resultSeq: any; - rules: any; - setFirst: any; - /** - * Visits a sub-tree - * @param {*} visitor - the visitor to use - * @param {*} thing - the node to visit - * @param {*} parameters - the current parameters - * @param {string} field - where to find the children nodes - * @returns {*} the result for the sub tree - */ - visitChildren(visitor: any, thing: any, parameters: any, field?: string): any; - /** - * Visit a node - * @param {*} thing the object being visited - * @param {*} parameters the parameters - */ - visit(thing: any, parameters: any): void; -} diff --git a/packages/markdown-common/types/lib/FromMarkdownIt.d.ts b/packages/markdown-common/types/lib/FromMarkdownIt.d.ts deleted file mode 100644 index e78f6104..00000000 --- a/packages/markdown-common/types/lib/FromMarkdownIt.d.ts +++ /dev/null @@ -1,55 +0,0 @@ -export = FromMarkdownIt; -/** - * Converts a markdown-it token stream to a CommonMark DOM - */ -declare class FromMarkdownIt { - /** - * Takes the stack of constructed inline nodes - * properly closing them (if the close token is missing in the markdown) - * returns the final root node for the inline - * - * @param {*[]} rules - the rules for each kind of markdown-it tokens - * @param {*[]} stack - the stack of constructed nodes - * @returns {*} the final inline node - */ - static closeInlines(rules: any[], stack: any[]): any; - /** - * Create a callback for inlines - * - * @param {*[]} rules - the rules for each kind of markdown-it tokens - * @returns {*} the callback - */ - static inlineCallback(rules: any[]): any; - /** - * Process an inline node to CommonMark DOM - * - * @param {*[]} rules - the rules for each kind of markdown-it tokens - * @param {*} tokens - the content of the inline node - * @param {*[]} stack - the stack of constructed nodes - */ - static inlineToCommonMark(rules: any[], tokens: any, stack: any[]): void; - /** - * Transform a block token stream to CommonMark DOM - * - * @param {*[]} rules - the rules for each kind of markdown-it tokens - * @param {*} tokens - the markdown-it token stream - * @returns {*} the CommonMark nodes - */ - static blockToCommonMark(rules: any[], tokens: any): any; - /** - * Construct the transformer - * @param {*[]} rules - the rules for each kind of markdown-it tokens - */ - constructor(rules: any[]); - rules: { - inlines: {}; - blocks: {}; - }; - /** - * Transform a token stream to CommonMark DOM - * - * @param {*} tokens - the markdown-it token stream - * @returns {*} the CommonMark nodes - */ - toCommonMark(tokens: any): any; -} diff --git a/packages/markdown-common/types/lib/Stack.d.ts b/packages/markdown-common/types/lib/Stack.d.ts deleted file mode 100644 index b3acfdc3..00000000 --- a/packages/markdown-common/types/lib/Stack.d.ts +++ /dev/null @@ -1,33 +0,0 @@ -export = Stack; -/** - * Manages a stack of objects - */ -declare class Stack { - /** - * Clears the stack - */ - clear(): void; - stack: any[]; - /** - * Returns the top of the stack or null if the stack is empty - * @return {*} the top of the stack - */ - peek(): any; - /** - * Pushes a new object to the top of the stack - * @param {*} obj the node to push - * @param {boolean} appendItem whether the item is also appended as a child to - * the item at the top of the stack - */ - push(obj: any, appendItem?: boolean): void; - /** - * Appends an object to the 'nodes' array of the item at the top of the stack - * @param {*} obj the item to append to the top node - */ - append(obj: any): void; - /** - * Pops the top of the stack. - * @return {*} the top of the stack - */ - pop(): any; -} diff --git a/packages/markdown-common/types/lib/ToMarkdownVisitor.d.ts b/packages/markdown-common/types/lib/ToMarkdownVisitor.d.ts deleted file mode 100644 index 0982b97d..00000000 --- a/packages/markdown-common/types/lib/ToMarkdownVisitor.d.ts +++ /dev/null @@ -1,24 +0,0 @@ -export = ToMarkdownVisitor; -/** - * Converts a CommonMark DOM to a markdown string. - * - * Note that there are multiple ways of representing the same CommonMark DOM as text, - * so this transformation is not guaranteed to equivalent if you roundtrip - * markdown content. For example an H1 can be specified using either '#' or '=' - * notation. - * - * The resulting AST *should* be equivalent however. - */ -declare class ToMarkdownVisitor extends FromCommonMarkVisitor { - /** - * Construct the visitor. - */ - constructor(); - /** - * Converts a CommonMark DOM to a markdown string - * @param {object} input - CommonMark DOM (as a Concerto object) - * @returns {string} the markdown string - */ - toMarkdown(input: object): string; -} -import FromCommonMarkVisitor = require("./FromCommonMarkVisitor"); diff --git a/packages/markdown-common/types/lib/externalModels/CiceroMarkModel.d.ts b/packages/markdown-common/types/lib/externalModels/CiceroMarkModel.d.ts deleted file mode 100644 index ce12b16d..00000000 --- a/packages/markdown-common/types/lib/externalModels/CiceroMarkModel.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export const NAMESPACE: "org.accordproject.ciceromark@0.6.0"; -export const MODEL: "\n/*\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nconcerto version \"^3.0.0\"\nnamespace org.accordproject.ciceromark@0.6.0\n\nimport org.accordproject.commonmark@0.5.0.Child from https://models.accordproject.org/markdown/commonmark@0.5.0.cto\nimport concerto.metamodel@1.0.0.Decorator from https://models.accordproject.org/concerto/metamodel@1.0.0.cto\n\n/**\n * A model for Accord Project extensions to commonmark\n */\n\nabstract concept Element extends Child {\n o String name\n o String elementType optional\n o Decorator[] decorators optional\n}\n\nconcept Variable extends Element {\n o String value\n o String identifiedBy optional\n}\n\nconcept FormattedVariable extends Variable {\n o String format\n}\n\nconcept EnumVariable extends Variable {\n o String[] enumValues\n}\n\nconcept Formula extends Element {\n o String value\n o String[] dependencies optional\n o String code optional\n}\n\nabstract concept Block extends Element {\n}\n\nconcept Clause extends Block {\n o String src optional\n}\n\nconcept Contract extends Block {\n o String src optional\n}\n\nconcept Conditional extends Block {\n o Boolean isTrue\n o Child[] whenTrue\n o Child[] whenFalse\n}\n\nconcept Optional extends Block {\n o Boolean hasSome\n o Child[] whenSome\n o Child[] whenNone\n}\n\nconcept ListBlock extends Block {\n o String type\n o String tight\n o String start optional\n o String delimiter optional\n}\n\n"; diff --git a/packages/markdown-common/types/lib/externalModels/CommonMarkModel.d.ts b/packages/markdown-common/types/lib/externalModels/CommonMarkModel.d.ts deleted file mode 100644 index 60d7610f..00000000 --- a/packages/markdown-common/types/lib/externalModels/CommonMarkModel.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export const NAMESPACE: "org.accordproject.commonmark@0.5.0"; -export const MODEL: "\n/*\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nconcerto version \"^3.0.0\"\nnamespace org.accordproject.commonmark@0.5.0\n\n/**\n * A model for a commonmark format markdown file\n */\n\nabstract concept Node {\n o String text optional\n o Node[] nodes optional\n o Integer startLine optional\n o Integer endLine optional\n}\n\nabstract concept Root extends Node {\n}\n\nabstract concept Child extends Node {\n}\n\nconcept Text extends Child {\n}\n\nconcept Attribute {\n o String name\n o String value\n}\nconcept TagInfo {\n o String tagName\n o String attributeString\n o Attribute[] attributes\n o String content\n o Boolean closed\n}\n\nconcept CodeBlock extends Child {\n o String info optional\n o TagInfo tag optional\n}\n\nconcept Code extends Child {\n o String info optional\n}\n\nconcept HtmlInline extends Child {\n o TagInfo tag optional\n}\n\nconcept HtmlBlock extends Child {\n o TagInfo tag optional\n}\n\nconcept Emph extends Child {\n}\n\nconcept Strong extends Child {\n}\n\nconcept BlockQuote extends Child {\n}\n\nconcept Heading extends Child {\n o String level\n}\n\nconcept ThematicBreak extends Child {\n}\n\nconcept Softbreak extends Child {\n}\n\nconcept Linebreak extends Child {\n}\n\nconcept Link extends Child {\n o String destination\n o String title\n}\n\nconcept Image extends Child {\n o String destination\n o String title\n}\n\nconcept Paragraph extends Child {\n}\n\nconcept List extends Child {\n o String type\n o String start optional\n o String tight\n o String delimiter optional\n}\n\nconcept Item extends Child {\n}\n\nconcept Document extends Root {\n o String xmlns\n}\n\nconcept Table extends Child{ \n}\n\nconcept TableHead extends Child{\n}\n\nconcept TableBody extends Child{\n}\n\nconcept TableRow extends Child{\n} \n\nconcept HeaderCell extends Child{\n}\n\nconcept TableCell extends Child{\n}\n"; diff --git a/packages/markdown-common/types/lib/externalModels/ConcertoMetaModel.d.ts b/packages/markdown-common/types/lib/externalModels/ConcertoMetaModel.d.ts deleted file mode 100644 index 3b9ecd24..00000000 --- a/packages/markdown-common/types/lib/externalModels/ConcertoMetaModel.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export const NAMESPACE: "concerto.metamodel@1.0.0"; -export const MODEL: "\n/*\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nconcerto version \"^3.0.0\"\n\n@DotNetNamespace(\"AccordProject.Concerto.Metamodel\")\nnamespace concerto.metamodel@1.0.0\n\nconcept Position {\n o Integer line\n o Integer column\n o Integer offset\n}\n\nconcept Range {\n o Position start\n o Position end\n o String source optional\n}\n\nconcept TypeIdentifier {\n o String name\n o String namespace optional\n}\n\nabstract concept DecoratorLiteral {\n o Range location optional\n}\n\nconcept DecoratorString extends DecoratorLiteral {\n o String value\n}\n\nconcept DecoratorNumber extends DecoratorLiteral {\n o Double value\n}\n\nconcept DecoratorBoolean extends DecoratorLiteral {\n o Boolean value\n}\n\nconcept DecoratorTypeReference extends DecoratorLiteral {\n o TypeIdentifier type\n o Boolean isArray default=false\n}\n\nconcept Decorator {\n o String name\n o DecoratorLiteral[] arguments optional\n o Range location optional\n}\n\nconcept Identified {\n}\n\nconcept IdentifiedBy extends Identified {\n o String name\n}\n\nabstract concept Declaration {\n o String name regex=/^(\\p{Lu}|\\p{Ll}|\\p{Lt}|\\p{Lm}|\\p{Lo}|\\p{Nl}|\\$|_|\\\\u[0-9A-Fa-f]{4})(?:\\p{Lu}|\\p{Ll}|\\p{Lt}|\\p{Lm}|\\p{Lo}|\\p{Nl}|\\$|_|\\\\u[0-9A-Fa-f]{4}|\\p{Mn}|\\p{Mc}|\\p{Nd}|\\p{Pc}|\\u200C|\\u200D)*$/u\n o Decorator[] decorators optional\n o Range location optional\n}\n\nabstract concept MapKeyType {\n o Decorator[] decorators optional\n o Range location optional\n}\n\nabstract concept MapValueType {\n o Decorator[] decorators optional\n o Range location optional\n}\n\nconcept MapDeclaration extends Declaration {\n o MapKeyType key\n o MapValueType value\n}\n\nconcept StringMapKeyType extends MapKeyType {}\nconcept DateTimeMapKeyType extends MapKeyType {}\n\nconcept ObjectMapKeyType extends MapKeyType {\n o TypeIdentifier type\n}\n\nconcept BooleanMapValueType extends MapValueType {}\nconcept DateTimeMapValueType extends MapValueType {}\nconcept StringMapValueType extends MapValueType {}\nconcept IntegerMapValueType extends MapValueType {}\nconcept LongMapValueType extends MapValueType {}\nconcept DoubleMapValueType extends MapValueType {}\n\nconcept ObjectMapValueType extends MapValueType {\n o TypeIdentifier type\n}\n\nconcept RelationshipMapValueType extends MapValueType {\n o TypeIdentifier type\n}\n\nconcept EnumDeclaration extends Declaration {\n o EnumProperty[] properties\n}\n\nconcept EnumProperty {\n o String name regex=/^(\\p{Lu}|\\p{Ll}|\\p{Lt}|\\p{Lm}|\\p{Lo}|\\p{Nl}|\\$|_|\\\\u[0-9A-Fa-f]{4})(?:\\p{Lu}|\\p{Ll}|\\p{Lt}|\\p{Lm}|\\p{Lo}|\\p{Nl}|\\$|_|\\\\u[0-9A-Fa-f]{4}|\\p{Mn}|\\p{Mc}|\\p{Nd}|\\p{Pc}|\\u200C|\\u200D)*$/u\n o Decorator[] decorators optional\n o Range location optional\n}\n\nconcept ConceptDeclaration extends Declaration {\n o Boolean isAbstract default=false\n o Identified identified optional\n o TypeIdentifier superType optional\n o Property[] properties\n}\n\nconcept AssetDeclaration extends ConceptDeclaration {\n}\n\nconcept ParticipantDeclaration extends ConceptDeclaration {\n}\n\nconcept TransactionDeclaration extends ConceptDeclaration {\n}\n\nconcept EventDeclaration extends ConceptDeclaration {\n}\n\nabstract concept Property {\n o String name regex=/^(\\p{Lu}|\\p{Ll}|\\p{Lt}|\\p{Lm}|\\p{Lo}|\\p{Nl}|\\$|_|\\\\u[0-9A-Fa-f]{4})(?:\\p{Lu}|\\p{Ll}|\\p{Lt}|\\p{Lm}|\\p{Lo}|\\p{Nl}|\\$|_|\\\\u[0-9A-Fa-f]{4}|\\p{Mn}|\\p{Mc}|\\p{Nd}|\\p{Pc}|\\u200C|\\u200D)*$/u\n o Boolean isArray default=false\n o Boolean isOptional default=false\n o Decorator[] decorators optional\n o Range location optional\n}\n\nconcept RelationshipProperty extends Property {\n o TypeIdentifier type\n}\n\nconcept ObjectProperty extends Property {\n o String defaultValue optional\n o TypeIdentifier type\n}\n\nconcept BooleanProperty extends Property {\n o Boolean defaultValue optional\n}\n\nconcept DateTimeProperty extends Property {\n}\n\nconcept StringProperty extends Property {\n o String defaultValue optional\n o StringRegexValidator validator optional\n o StringLengthValidator lengthValidator optional\n}\n\nconcept StringRegexValidator {\n o String pattern\n o String flags\n}\n\nconcept StringLengthValidator {\n o Integer minLength optional\n o Integer maxLength optional\n}\n\nconcept DoubleProperty extends Property {\n o Double defaultValue optional\n o DoubleDomainValidator validator optional\n}\n\nconcept DoubleDomainValidator {\n o Double lower optional\n o Double upper optional\n}\n\nconcept IntegerProperty extends Property {\n o Integer defaultValue optional\n o IntegerDomainValidator validator optional\n}\n\nconcept IntegerDomainValidator {\n o Integer lower optional\n o Integer upper optional\n}\n\nconcept LongProperty extends Property {\n o Long defaultValue optional\n o LongDomainValidator validator optional\n}\n\nconcept LongDomainValidator {\n o Long lower optional\n o Long upper optional\n}\n\nconcept AliasedType{\n o String name\n o String aliasedName\n}\nabstract concept Import {\n o String namespace\n o String uri optional\n}\n\nconcept ImportAll extends Import {\n}\n\nconcept ImportType extends Import {\n o String name\n}\n\nconcept ImportTypes extends Import {\n o String[] types\n o AliasedType[] aliasedTypes optional\n}\n\nconcept Model {\n o String namespace\n o String sourceUri optional\n o String concertoVersion optional\n o Import[] imports optional\n o Declaration[] declarations optional\n o Decorator[] decorators optional\n}\n\nconcept Models {\n o Model[] models\n}\n\nabstract concept ScalarDeclaration extends Declaration {\n}\n\nconcept BooleanScalar extends ScalarDeclaration {\n o Boolean defaultValue optional\n}\n\nconcept IntegerScalar extends ScalarDeclaration {\n o Integer defaultValue optional\n o IntegerDomainValidator validator optional\n}\n\nconcept LongScalar extends ScalarDeclaration {\n o Long defaultValue optional\n o LongDomainValidator validator optional\n}\n\nconcept DoubleScalar extends ScalarDeclaration {\n o Double defaultValue optional\n o DoubleDomainValidator validator optional\n}\n\nconcept StringScalar extends ScalarDeclaration {\n o String defaultValue optional\n o StringRegexValidator validator optional\n o StringLengthValidator lengthValidator optional\n}\n\nconcept DateTimeScalar extends ScalarDeclaration {\n o String defaultValue optional\n}\n\n"; diff --git a/packages/markdown-common/types/lib/externalModels/TemplateMarkModel.d.ts b/packages/markdown-common/types/lib/externalModels/TemplateMarkModel.d.ts deleted file mode 100644 index d4fdf80c..00000000 --- a/packages/markdown-common/types/lib/externalModels/TemplateMarkModel.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export const NAMESPACE: "org.accordproject.templatemark@0.5.0"; -export const MODEL: "\n/*\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nconcerto version \"^3.0.0\"\nnamespace org.accordproject.templatemark@0.5.0\n\nimport org.accordproject.commonmark@0.5.0.Child from https://models.accordproject.org/markdown/commonmark@0.5.0.cto\nimport concerto.metamodel@1.0.0.Decorator from https://models.accordproject.org/concerto/metamodel@1.0.0.cto\n\n/**\n * A model for Accord Project template extensions to commonmark\n */\n\n/**\n * Identifiers for code strings in Formula Definition nodes\n */\nenum CodeType {\n o TYPESCRIPT\n o ES_2020\n}\n\n/**\n * User provided code, along with a code language identifier\n */\nconcept Code {\n o CodeType type\n o String contents\n}\n\nabstract concept ElementDefinition extends Child {\n o String name\n o String elementType optional\n o Decorator[] decorators optional\n}\n\nconcept VariableDefinition extends ElementDefinition {\n o String identifiedBy optional\n}\n\nconcept FormattedVariableDefinition extends VariableDefinition {\n o String format\n}\n\nconcept EnumVariableDefinition extends VariableDefinition {\n o String[] enumValues\n}\n\nconcept FormulaDefinition extends ElementDefinition {\n o String[] dependencies optional // name of variables on which the formula depends\n o Code code\n}\n\nabstract concept BlockDefinition extends ElementDefinition {\n}\n\nconcept ClauseDefinition extends BlockDefinition {\n o Code condition optional\n}\n\nconcept ContractDefinition extends BlockDefinition {\n}\n\nconcept WithDefinition extends BlockDefinition {\n}\n\nconcept ConditionalDefinition extends BlockDefinition {\n o Child[] whenTrue\n o Child[] whenFalse\n o Code condition optional\n o String[] dependencies optional\n}\n\nconcept OptionalDefinition extends BlockDefinition {\n o Child[] whenSome\n o Child[] whenNone\n}\nconcept JoinDefinition extends BlockDefinition {\n // if separator is set, we just use that\n o String separator optional\n // if separator is not set, we use the Intl.ListFormat, paramaterized by locale, type and style\n o String locale optional\n o String type optional\n o String style optional\n}\n\nconcept ListBlockDefinition extends BlockDefinition {\n o String type\n o String tight\n o String start optional\n o String delimiter optional\n}\n\nconcept ForeachBlockDefinition extends BlockDefinition {\n}\n\nconcept WithBlockDefinition extends BlockDefinition {\n}\n\nconcept ConditionalBlockDefinition extends BlockDefinition {\n o Child[] whenTrue\n o Child[] whenFalse\n o Code condition optional\n}\n\nconcept OptionalBlockDefinition extends BlockDefinition {\n o Child[] whenSome\n o Child[] whenNone\n}\n\n"; diff --git a/packages/markdown-common/types/lib/fromcommonmarkrules.d.ts b/packages/markdown-common/types/lib/fromcommonmarkrules.d.ts deleted file mode 100644 index 01740527..00000000 --- a/packages/markdown-common/types/lib/fromcommonmarkrules.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -export = rules; -/** @type {Object} */ -declare const rules: { - [x: string]: Function; -}; diff --git a/packages/markdown-common/types/lib/removeFormatting.d.ts b/packages/markdown-common/types/lib/removeFormatting.d.ts deleted file mode 100644 index de1f13b3..00000000 --- a/packages/markdown-common/types/lib/removeFormatting.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -export = removeFormatting; -/** - * Removes rich text formatting nodes. - * @param {*} obj input object - * @returns {*} the modified object - */ -declare function removeFormatting(obj: any): any; diff --git a/packages/markdown-common/types/lib/tocommonmarkrules.d.ts b/packages/markdown-common/types/lib/tocommonmarkrules.d.ts deleted file mode 100644 index 3b567c72..00000000 --- a/packages/markdown-common/types/lib/tocommonmarkrules.d.ts +++ /dev/null @@ -1,494 +0,0 @@ -declare namespace textRule { - let tag: string; - let leaf: boolean; - let open: boolean; - let close: boolean; - function enter(node: any, token: any, callback: any): void; - let skipEmpty: boolean; -} -declare namespace codeInlineRule { - let tag_1: string; - export { tag_1 as tag }; - let leaf_1: boolean; - export { leaf_1 as leaf }; - let open_1: boolean; - export { open_1 as open }; - let close_1: boolean; - export { close_1 as close }; - export function enter_1(node: any, token: any, callback: any): void; - export { enter_1 as enter }; - let skipEmpty_1: boolean; - export { skipEmpty_1 as skipEmpty }; -} -declare namespace softbreakRule { - let tag_2: string; - export { tag_2 as tag }; - let leaf_2: boolean; - export { leaf_2 as leaf }; - let open_2: boolean; - export { open_2 as open }; - let close_2: boolean; - export { close_2 as close }; - let skipEmpty_2: boolean; - export { skipEmpty_2 as skipEmpty }; -} -declare namespace hardbreakRule { - let tag_3: string; - export { tag_3 as tag }; - let leaf_3: boolean; - export { leaf_3 as leaf }; - let open_3: boolean; - export { open_3 as open }; - let close_3: boolean; - export { close_3 as close }; - let skipEmpty_3: boolean; - export { skipEmpty_3 as skipEmpty }; -} -declare namespace htmlInlineRule { - let tag_4: string; - export { tag_4 as tag }; - let leaf_4: boolean; - export { leaf_4 as leaf }; - let open_4: boolean; - export { open_4 as open }; - let close_4: boolean; - export { close_4 as close }; - export function enter_2(node: any, token: any, callback: any): void; - export { enter_2 as enter }; - let skipEmpty_4: boolean; - export { skipEmpty_4 as skipEmpty }; -} -declare namespace strongOpenRule { - let tag_5: string; - export { tag_5 as tag }; - let leaf_5: boolean; - export { leaf_5 as leaf }; - let open_5: boolean; - export { open_5 as open }; - let close_5: boolean; - export { close_5 as close }; - let skipEmpty_5: boolean; - export { skipEmpty_5 as skipEmpty }; -} -declare namespace strongCloseRule { - let tag_6: string; - export { tag_6 as tag }; - let leaf_6: boolean; - export { leaf_6 as leaf }; - let open_6: boolean; - export { open_6 as open }; - let close_6: boolean; - export { close_6 as close }; - let skipEmpty_6: boolean; - export { skipEmpty_6 as skipEmpty }; -} -declare namespace emphOpenRule { - let tag_7: string; - export { tag_7 as tag }; - let leaf_7: boolean; - export { leaf_7 as leaf }; - let open_7: boolean; - export { open_7 as open }; - let close_7: boolean; - export { close_7 as close }; - let skipEmpty_7: boolean; - export { skipEmpty_7 as skipEmpty }; -} -declare namespace emphCloseRule { - let tag_8: string; - export { tag_8 as tag }; - let leaf_8: boolean; - export { leaf_8 as leaf }; - let open_8: boolean; - export { open_8 as open }; - let close_8: boolean; - export { close_8 as close }; - let skipEmpty_8: boolean; - export { skipEmpty_8 as skipEmpty }; -} -declare namespace linkOpenRule { - let tag_9: string; - export { tag_9 as tag }; - let leaf_9: boolean; - export { leaf_9 as leaf }; - let open_9: boolean; - export { open_9 as open }; - let close_9: boolean; - export { close_9 as close }; - export function enter_3(node: any, token: any, callback: any): void; - export { enter_3 as enter }; - let skipEmpty_9: boolean; - export { skipEmpty_9 as skipEmpty }; -} -declare namespace linkCloseRule { - let tag_10: string; - export { tag_10 as tag }; - let leaf_10: boolean; - export { leaf_10 as leaf }; - let open_10: boolean; - export { open_10 as open }; - let close_10: boolean; - export { close_10 as close }; - let skipEmpty_10: boolean; - export { skipEmpty_10 as skipEmpty }; -} -declare namespace imageRule { - let tag_11: string; - export { tag_11 as tag }; - let leaf_11: boolean; - export { leaf_11 as leaf }; - let open_11: boolean; - export { open_11 as open }; - let close_11: boolean; - export { close_11 as close }; - export function enter_4(node: any, token: any, callback: any): void; - export { enter_4 as enter }; - let skipEmpty_11: boolean; - export { skipEmpty_11 as skipEmpty }; -} -declare namespace codeBlockRule { - let tag_12: string; - export { tag_12 as tag }; - let leaf_12: boolean; - export { leaf_12 as leaf }; - let open_12: boolean; - export { open_12 as open }; - let close_12: boolean; - export { close_12 as close }; - export function enter_5(node: any, token: any, callback: any): void; - export { enter_5 as enter }; -} -declare namespace fenceRule { } -declare namespace htmlBlockRule { - let tag_13: string; - export { tag_13 as tag }; - let leaf_13: boolean; - export { leaf_13 as leaf }; - let open_13: boolean; - export { open_13 as open }; - let close_13: boolean; - export { close_13 as close }; - export function enter_6(node: any, token: any, callback: any): void; - export { enter_6 as enter }; -} -declare namespace hrRule { - let tag_14: string; - export { tag_14 as tag }; - let leaf_14: boolean; - export { leaf_14 as leaf }; - let open_14: boolean; - export { open_14 as open }; - let close_14: boolean; - export { close_14 as close }; - export function enter_7(node: any, token: any, callback: any): void; - export { enter_7 as enter }; -} -declare namespace paragraphOpenRule { - let tag_15: string; - export { tag_15 as tag }; - let leaf_15: boolean; - export { leaf_15 as leaf }; - let open_15: boolean; - export { open_15 as open }; - let close_15: boolean; - export { close_15 as close }; - export function enter_8(node: any, token: any, callback: any): void; - export { enter_8 as enter }; -} -declare namespace paragraphCloseRule { - let tag_16: string; - export { tag_16 as tag }; - let leaf_16: boolean; - export { leaf_16 as leaf }; - let open_16: boolean; - export { open_16 as open }; - let close_16: boolean; - export { close_16 as close }; -} -declare namespace headingOpenRule { - let tag_17: string; - export { tag_17 as tag }; - let leaf_17: boolean; - export { leaf_17 as leaf }; - let open_17: boolean; - export { open_17 as open }; - let close_17: boolean; - export { close_17 as close }; - export function enter_9(node: any, token: any, callback: any): void; - export { enter_9 as enter }; -} -declare namespace headingCloseRule { - let tag_18: string; - export { tag_18 as tag }; - let leaf_18: boolean; - export { leaf_18 as leaf }; - let open_18: boolean; - export { open_18 as open }; - let close_18: boolean; - export { close_18 as close }; -} -declare namespace blockQuoteOpenRule { - let tag_19: string; - export { tag_19 as tag }; - let leaf_19: boolean; - export { leaf_19 as leaf }; - let open_19: boolean; - export { open_19 as open }; - let close_19: boolean; - export { close_19 as close }; - export function enter_10(node: any, token: any, callback: any): void; - export { enter_10 as enter }; -} -declare namespace blockQuoteCloseRule { - let tag_20: string; - export { tag_20 as tag }; - let leaf_20: boolean; - export { leaf_20 as leaf }; - let open_20: boolean; - export { open_20 as open }; - let close_20: boolean; - export { close_20 as close }; -} -declare namespace bulletListOpenRule { - let tag_21: string; - export { tag_21 as tag }; - let leaf_21: boolean; - export { leaf_21 as leaf }; - let open_21: boolean; - export { open_21 as open }; - let close_21: boolean; - export { close_21 as close }; - export function enter_11(node: any, token: any, callback: any): void; - export { enter_11 as enter }; -} -declare namespace bulletListCloseRule { - let tag_22: string; - export { tag_22 as tag }; - let leaf_22: boolean; - export { leaf_22 as leaf }; - let open_22: boolean; - export { open_22 as open }; - let close_22: boolean; - export { close_22 as close }; -} -declare namespace orderedListOpenRule { - let tag_23: string; - export { tag_23 as tag }; - let leaf_23: boolean; - export { leaf_23 as leaf }; - let open_23: boolean; - export { open_23 as open }; - let close_23: boolean; - export { close_23 as close }; - export function enter_12(node: any, token: any, callback: any): void; - export { enter_12 as enter }; -} -declare namespace orderedListCloseRule { - let tag_24: string; - export { tag_24 as tag }; - let leaf_24: boolean; - export { leaf_24 as leaf }; - let open_24: boolean; - export { open_24 as open }; - let close_24: boolean; - export { close_24 as close }; -} -declare namespace listItemOpenRule { - let tag_25: string; - export { tag_25 as tag }; - let leaf_25: boolean; - export { leaf_25 as leaf }; - let open_25: boolean; - export { open_25 as open }; - let close_25: boolean; - export { close_25 as close }; - export function enter_13(node: any, token: any, callback: any): void; - export { enter_13 as enter }; -} -declare namespace listItemCloseRule { - let tag_26: string; - export { tag_26 as tag }; - let leaf_26: boolean; - export { leaf_26 as leaf }; - let open_26: boolean; - export { open_26 as open }; - let close_26: boolean; - export { close_26 as close }; -} -declare namespace tableOpenRule { - let tag_27: string; - export { tag_27 as tag }; - let leaf_27: boolean; - export { leaf_27 as leaf }; - let open_27: boolean; - export { open_27 as open }; - let close_27: boolean; - export { close_27 as close }; - export function enter_14(node: any, token: any, callback: any): void; - export { enter_14 as enter }; -} -declare namespace tableCloseRule { - let tag_28: string; - export { tag_28 as tag }; - let leaf_28: boolean; - export { leaf_28 as leaf }; - let open_28: boolean; - export { open_28 as open }; - let close_28: boolean; - export { close_28 as close }; -} -declare namespace tableHeadOpenRule { - let tag_29: string; - export { tag_29 as tag }; - let leaf_29: boolean; - export { leaf_29 as leaf }; - let open_29: boolean; - export { open_29 as open }; - let close_29: boolean; - export { close_29 as close }; - export function enter_15(node: any, token: any, callback: any): void; - export { enter_15 as enter }; -} -declare namespace tableHeadCloseRule { - let tag_30: string; - export { tag_30 as tag }; - let leaf_30: boolean; - export { leaf_30 as leaf }; - let open_30: boolean; - export { open_30 as open }; - let close_30: boolean; - export { close_30 as close }; -} -declare namespace tableBodyOpenRule { - let tag_31: string; - export { tag_31 as tag }; - let leaf_31: boolean; - export { leaf_31 as leaf }; - let open_31: boolean; - export { open_31 as open }; - let close_31: boolean; - export { close_31 as close }; - export function enter_16(node: any, token: any, callback: any): void; - export { enter_16 as enter }; -} -declare namespace tableBodyCloseRule { - let tag_32: string; - export { tag_32 as tag }; - let leaf_32: boolean; - export { leaf_32 as leaf }; - let open_32: boolean; - export { open_32 as open }; - let close_32: boolean; - export { close_32 as close }; -} -declare namespace tableRowOpenRule { - let tag_33: string; - export { tag_33 as tag }; - let leaf_33: boolean; - export { leaf_33 as leaf }; - let open_33: boolean; - export { open_33 as open }; - let close_33: boolean; - export { close_33 as close }; - export function enter_17(node: any, token: any, callback: any): void; - export { enter_17 as enter }; -} -declare namespace tableRowCloseRule { - let tag_34: string; - export { tag_34 as tag }; - let leaf_34: boolean; - export { leaf_34 as leaf }; - let open_34: boolean; - export { open_34 as open }; - let close_34: boolean; - export { close_34 as close }; -} -declare namespace headerCellOpenRule { - let tag_35: string; - export { tag_35 as tag }; - let leaf_35: boolean; - export { leaf_35 as leaf }; - let open_35: boolean; - export { open_35 as open }; - let close_35: boolean; - export { close_35 as close }; - export function enter_18(node: any, token: any, callback: any): void; - export { enter_18 as enter }; -} -declare namespace headerCellCloseRule { - let tag_36: string; - export { tag_36 as tag }; - let leaf_36: boolean; - export { leaf_36 as leaf }; - let open_36: boolean; - export { open_36 as open }; - let close_36: boolean; - export { close_36 as close }; -} -declare namespace tableCellOpenRule { - let tag_37: string; - export { tag_37 as tag }; - let leaf_37: boolean; - export { leaf_37 as leaf }; - let open_37: boolean; - export { open_37 as open }; - let close_37: boolean; - export { close_37 as close }; - export function enter_19(node: any, token: any, callback: any): void; - export { enter_19 as enter }; -} -declare namespace tableCellCloseRule { - let tag_38: string; - export { tag_38 as tag }; - let leaf_38: boolean; - export { leaf_38 as leaf }; - let open_38: boolean; - export { open_38 as open }; - let close_38: boolean; - export { close_38 as close }; -} -export namespace inlines { - export { textRule as text }; - export { codeInlineRule as code_inline }; - export { softbreakRule as softbreak }; - export { hardbreakRule as hardbreak }; - export { htmlInlineRule as html_inline }; - export { strongOpenRule as strong_open }; - export { strongCloseRule as strong_close }; - export { emphOpenRule as em_open }; - export { emphCloseRule as em_close }; - export { linkOpenRule as link_open }; - export { linkCloseRule as link_close }; - export { imageRule as image }; -} -export namespace blocks { - export { codeBlockRule as code_block }; - export { fenceRule as fence }; - export { htmlBlockRule as html_block }; - export { hrRule as hr }; - export { paragraphOpenRule as paragraph_open }; - export { paragraphCloseRule as paragraph_close }; - export { headingOpenRule as heading_open }; - export { headingCloseRule as heading_close }; - export { blockQuoteOpenRule as blockquote_open }; - export { blockQuoteCloseRule as blockquote_close }; - export { bulletListOpenRule as bullet_list_open }; - export { bulletListCloseRule as bullet_list_close }; - export { orderedListOpenRule as ordered_list_open }; - export { orderedListCloseRule as ordered_list_close }; - export { listItemOpenRule as list_item_open }; - export { listItemCloseRule as list_item_close }; - export { tableOpenRule as table_open }; - export { tableCloseRule as table_close }; - export { tableHeadOpenRule as thead_open }; - export { tableHeadCloseRule as thead_close }; - export { tableBodyOpenRule as tbody_open }; - export { tableBodyCloseRule as tbody_close }; - export { tableRowOpenRule as tr_open }; - export { tableRowCloseRule as tr_close }; - export { headerCellOpenRule as th_open }; - export { headerCellCloseRule as th_close }; - export { tableCellOpenRule as td_open }; - export { tableCellCloseRule as td_close }; -} -export {}; diff --git a/packages/markdown-common/types/model/ciceromark.d.ts b/packages/markdown-common/types/model/ciceromark.d.ts deleted file mode 100644 index 85c9bf2b..00000000 --- a/packages/markdown-common/types/model/ciceromark.d.ts +++ /dev/null @@ -1,81 +0,0 @@ -// Generated code for namespace: org.accordproject.ciceromark@0.6.0 - -// imports - -// Warning: Beware of circular dependencies when modifying these imports - -// Warning: Beware of circular dependencies when modifying these imports - -// Warning: Beware of circular dependencies when modifying these imports -import {IChild,INode} from './commonmark'; -import {IDecorator} from './concerto-metamodel'; - -// interfaces -export interface IElement extends IChild { - name: string; - elementType?: string; - decorators?: IDecorator[]; -} - -export type ElementUnion = IVariable | -IFormula | -IBlock; - -export interface IVariable extends IElement { - value: string; - identifiedBy?: string; -} - -export type VariableUnion = IFormattedVariable | -IEnumVariable; - -export interface IFormattedVariable extends IVariable { - format: string; -} - -export interface IEnumVariable extends IVariable { - enumValues: string[]; -} - -export interface IFormula extends IElement { - value: string; - dependencies?: string[]; - code?: string; -} - -export interface IBlock extends IElement { -} - -export type BlockUnion = IClause | -IContract | -IConditional | -IOptional | -IListBlock; - -export interface IClause extends IBlock { - src?: string; -} - -export interface IContract extends IBlock { - src?: string; -} - -export interface IConditional extends IBlock { - isTrue: boolean; - whenTrue: IChild[]; - whenFalse: IChild[]; -} - -export interface IOptional extends IBlock { - hasSome: boolean; - whenSome: IChild[]; - whenNone: IChild[]; -} - -export interface IListBlock extends IBlock { - type: string; - tight: string; - start?: string; - delimiter?: string; -} - diff --git a/packages/markdown-common/types/model/commonmark.d.ts b/packages/markdown-common/types/model/commonmark.d.ts deleted file mode 100644 index 45c32a4f..00000000 --- a/packages/markdown-common/types/model/commonmark.d.ts +++ /dev/null @@ -1,162 +0,0 @@ -// Generated code for namespace: org.accordproject.commonmark@0.5.0 - -// imports - -// Warning: Beware of circular dependencies when modifying these imports - -// Warning: Beware of circular dependencies when modifying these imports - -// Warning: Beware of circular dependencies when modifying these imports -import type { - IElement -} from './ciceromark'; -import type { - IElementDefinition -} from './templatemark'; -import {IConcept} from './concerto-base'; - -// interfaces -export interface INode extends IConcept { - text?: string; - nodes?: INode[]; - startLine?: number; - endLine?: number; -} - -export type NodeUnion = IRoot | -IChild; - -export interface IRoot extends INode { -} - -export type RootUnion = IDocument; - -export interface IChild extends INode { -} - -export type ChildUnion = IText | -ICodeBlock | -ICode | -IHtmlInline | -IHtmlBlock | -IEmph | -IStrong | -IBlockQuote | -IHeading | -IThematicBreak | -ISoftbreak | -ILinebreak | -ILink | -IImage | -IParagraph | -IList | -IItem | -ITable | -ITableHead | -ITableBody | -ITableRow | -IHeaderCell | -ITableCell | -IElement | -IElementDefinition; - -export interface IText extends IChild { -} - -export interface IAttribute extends IConcept { - name: string; - value: string; -} - -export interface ITagInfo extends IConcept { - tagName: string; - attributeString: string; - attributes: IAttribute[]; - content: string; - closed: boolean; -} - -export interface ICodeBlock extends IChild { - info?: string; - tag?: ITagInfo; -} - -export interface ICode extends IChild { - info?: string; -} - -export interface IHtmlInline extends IChild { - tag?: ITagInfo; -} - -export interface IHtmlBlock extends IChild { - tag?: ITagInfo; -} - -export interface IEmph extends IChild { -} - -export interface IStrong extends IChild { -} - -export interface IBlockQuote extends IChild { -} - -export interface IHeading extends IChild { - level: string; -} - -export interface IThematicBreak extends IChild { -} - -export interface ISoftbreak extends IChild { -} - -export interface ILinebreak extends IChild { -} - -export interface ILink extends IChild { - destination: string; - title: string; -} - -export interface IImage extends IChild { - destination: string; - title: string; -} - -export interface IParagraph extends IChild { -} - -export interface IList extends IChild { - type: string; - start?: string; - tight: string; - delimiter?: string; -} - -export interface IItem extends IChild { -} - -export interface IDocument extends IRoot { - xmlns: string; -} - -export interface ITable extends IChild { -} - -export interface ITableHead extends IChild { -} - -export interface ITableBody extends IChild { -} - -export interface ITableRow extends IChild { -} - -export interface IHeaderCell extends IChild { -} - -export interface ITableCell extends IChild { -} - diff --git a/packages/markdown-common/types/model/concerto-base.d.ts b/packages/markdown-common/types/model/concerto-base.d.ts deleted file mode 100644 index 469820cd..00000000 --- a/packages/markdown-common/types/model/concerto-base.d.ts +++ /dev/null @@ -1,83 +0,0 @@ -// Generated code for namespace: concerto@1.0.0 - -// imports - -// Warning: Beware of circular dependencies when modifying these imports -import type { - IPosition, - IRange, - ITypeIdentifier, - IDecoratorLiteral, - IDecorator, - IIdentified, - IDeclaration, - IMapKeyType, - IMapValueType, - IEnumProperty, - IProperty, - IStringRegexValidator, - IStringLengthValidator, - IDoubleDomainValidator, - IIntegerDomainValidator, - ILongDomainValidator, - IAliasedType, - IImport, - IModel, - IModels -} from './concerto-metamodel'; -import type { - INode, - IAttribute, - ITagInfo -} from './commonmark'; -import type { - CodeType, - ICode -} from './templatemark'; - -// interfaces -export interface IConcept { - $class: string; -} - -export type ConceptUnion = IPosition | -IRange | -ITypeIdentifier | -IDecoratorLiteral | -IDecorator | -IIdentified | -IDeclaration | -IMapKeyType | -IMapValueType | -IEnumProperty | -IProperty | -IStringRegexValidator | -IStringLengthValidator | -IDoubleDomainValidator | -IIntegerDomainValidator | -ILongDomainValidator | -IAliasedType | -IImport | -IModel | -IModels | -INode | -IAttribute | -ITagInfo | -ICode; - -export interface IAsset extends IConcept { - $identifier: string; -} - -export interface IParticipant extends IConcept { - $identifier: string; -} - -export interface ITransaction extends IConcept { - $timestamp: Date; -} - -export interface IEvent extends IConcept { - $timestamp: Date; -} - diff --git a/packages/markdown-common/types/model/concerto-metamodel.d.ts b/packages/markdown-common/types/model/concerto-metamodel.d.ts deleted file mode 100644 index cf4392af..00000000 --- a/packages/markdown-common/types/model/concerto-metamodel.d.ts +++ /dev/null @@ -1,348 +0,0 @@ -// Generated code for namespace: concerto.metamodel@1.0.0 - -// imports - -// Warning: Beware of circular dependencies when modifying these imports - -// Warning: Beware of circular dependencies when modifying these imports - -// Warning: Beware of circular dependencies when modifying these imports - -// Warning: Beware of circular dependencies when modifying these imports - -// Warning: Beware of circular dependencies when modifying these imports - -// Warning: Beware of circular dependencies when modifying these imports - -// Warning: Beware of circular dependencies when modifying these imports - -// Warning: Beware of circular dependencies when modifying these imports - -// Warning: Beware of circular dependencies when modifying these imports -import {IConcept} from './concerto-base'; - -// interfaces -export interface IPosition extends IConcept { - line: number; - column: number; - offset: number; -} - -export interface IRange extends IConcept { - start: IPosition; - end: IPosition; - source?: string; -} - -export interface ITypeIdentifier extends IConcept { - name: string; - namespace?: string; -} - -export interface IDecoratorLiteral extends IConcept { - location?: IRange; -} - -export type DecoratorLiteralUnion = IDecoratorString | -IDecoratorNumber | -IDecoratorBoolean | -IDecoratorTypeReference; - -export interface IDecoratorString extends IDecoratorLiteral { - value: string; -} - -export interface IDecoratorNumber extends IDecoratorLiteral { - value: number; -} - -export interface IDecoratorBoolean extends IDecoratorLiteral { - value: boolean; -} - -export interface IDecoratorTypeReference extends IDecoratorLiteral { - type: ITypeIdentifier; - isArray: boolean; -} - -export interface IDecorator extends IConcept { - name: string; - arguments?: IDecoratorLiteral[]; - location?: IRange; -} - -export interface IIdentified extends IConcept { -} - -export type IdentifiedUnion = IIdentifiedBy; - -export interface IIdentifiedBy extends IIdentified { - name: string; -} - -export interface IDeclaration extends IConcept { - name: string; - decorators?: IDecorator[]; - location?: IRange; -} - -export type DeclarationUnion = IMapDeclaration | -IEnumDeclaration | -IConceptDeclaration | -IScalarDeclaration; - -export interface IMapKeyType extends IConcept { - decorators?: IDecorator[]; - location?: IRange; -} - -export type MapKeyTypeUnion = IStringMapKeyType | -IDateTimeMapKeyType | -IObjectMapKeyType; - -export interface IMapValueType extends IConcept { - decorators?: IDecorator[]; - location?: IRange; -} - -export type MapValueTypeUnion = IBooleanMapValueType | -IDateTimeMapValueType | -IStringMapValueType | -IIntegerMapValueType | -ILongMapValueType | -IDoubleMapValueType | -IObjectMapValueType | -IRelationshipMapValueType; - -export interface IMapDeclaration extends IDeclaration { - key: IMapKeyType; - value: IMapValueType; -} - -export interface IStringMapKeyType extends IMapKeyType { -} - -export interface IDateTimeMapKeyType extends IMapKeyType { -} - -export interface IObjectMapKeyType extends IMapKeyType { - type: ITypeIdentifier; -} - -export interface IBooleanMapValueType extends IMapValueType { -} - -export interface IDateTimeMapValueType extends IMapValueType { -} - -export interface IStringMapValueType extends IMapValueType { -} - -export interface IIntegerMapValueType extends IMapValueType { -} - -export interface ILongMapValueType extends IMapValueType { -} - -export interface IDoubleMapValueType extends IMapValueType { -} - -export interface IObjectMapValueType extends IMapValueType { - type: ITypeIdentifier; -} - -export interface IRelationshipMapValueType extends IMapValueType { - type: ITypeIdentifier; -} - -export interface IEnumDeclaration extends IDeclaration { - properties: IEnumProperty[]; -} - -export interface IEnumProperty extends IConcept { - name: string; - decorators?: IDecorator[]; - location?: IRange; -} - -export interface IConceptDeclaration extends IDeclaration { - isAbstract: boolean; - identified?: IIdentified; - superType?: ITypeIdentifier; - properties: IProperty[]; -} - -export type ConceptDeclarationUnion = IAssetDeclaration | -IParticipantDeclaration | -ITransactionDeclaration | -IEventDeclaration; - -export interface IAssetDeclaration extends IConceptDeclaration { -} - -export interface IParticipantDeclaration extends IConceptDeclaration { -} - -export interface ITransactionDeclaration extends IConceptDeclaration { -} - -export interface IEventDeclaration extends IConceptDeclaration { -} - -export interface IProperty extends IConcept { - name: string; - isArray: boolean; - isOptional: boolean; - decorators?: IDecorator[]; - location?: IRange; -} - -export type PropertyUnion = IRelationshipProperty | -IObjectProperty | -IBooleanProperty | -IDateTimeProperty | -IStringProperty | -IDoubleProperty | -IIntegerProperty | -ILongProperty; - -export interface IRelationshipProperty extends IProperty { - type: ITypeIdentifier; -} - -export interface IObjectProperty extends IProperty { - defaultValue?: string; - type: ITypeIdentifier; -} - -export interface IBooleanProperty extends IProperty { - defaultValue?: boolean; -} - -export interface IDateTimeProperty extends IProperty { -} - -export interface IStringProperty extends IProperty { - defaultValue?: string; - validator?: IStringRegexValidator; - lengthValidator?: IStringLengthValidator; -} - -export interface IStringRegexValidator extends IConcept { - pattern: string; - flags: string; -} - -export interface IStringLengthValidator extends IConcept { - minLength?: number; - maxLength?: number; -} - -export interface IDoubleProperty extends IProperty { - defaultValue?: number; - validator?: IDoubleDomainValidator; -} - -export interface IDoubleDomainValidator extends IConcept { - lower?: number; - upper?: number; -} - -export interface IIntegerProperty extends IProperty { - defaultValue?: number; - validator?: IIntegerDomainValidator; -} - -export interface IIntegerDomainValidator extends IConcept { - lower?: number; - upper?: number; -} - -export interface ILongProperty extends IProperty { - defaultValue?: number; - validator?: ILongDomainValidator; -} - -export interface ILongDomainValidator extends IConcept { - lower?: number; - upper?: number; -} - -export interface IAliasedType extends IConcept { - name: string; - aliasedName: string; -} - -export interface IImport extends IConcept { - namespace: string; - uri?: string; -} - -export type ImportUnion = IImportAll | -IImportType | -IImportTypes; - -export interface IImportAll extends IImport { -} - -export interface IImportType extends IImport { - name: string; -} - -export interface IImportTypes extends IImport { - types: string[]; - aliasedTypes?: IAliasedType[]; -} - -export interface IModel extends IConcept { - namespace: string; - sourceUri?: string; - concertoVersion?: string; - imports?: IImport[]; - declarations?: IDeclaration[]; - decorators?: IDecorator[]; -} - -export interface IModels extends IConcept { - models: IModel[]; -} - -export interface IScalarDeclaration extends IDeclaration { -} - -export type ScalarDeclarationUnion = IBooleanScalar | -IIntegerScalar | -ILongScalar | -IDoubleScalar | -IStringScalar | -IDateTimeScalar; - -export interface IBooleanScalar extends IScalarDeclaration { - defaultValue?: boolean; -} - -export interface IIntegerScalar extends IScalarDeclaration { - defaultValue?: number; - validator?: IIntegerDomainValidator; -} - -export interface ILongScalar extends IScalarDeclaration { - defaultValue?: number; - validator?: ILongDomainValidator; -} - -export interface IDoubleScalar extends IScalarDeclaration { - defaultValue?: number; - validator?: IDoubleDomainValidator; -} - -export interface IStringScalar extends IScalarDeclaration { - defaultValue?: string; - validator?: IStringRegexValidator; - lengthValidator?: IStringLengthValidator; -} - -export interface IDateTimeScalar extends IScalarDeclaration { - defaultValue?: string; -} - diff --git a/packages/markdown-common/types/model/index.d.ts b/packages/markdown-common/types/model/index.d.ts deleted file mode 100644 index f5d48bff..00000000 --- a/packages/markdown-common/types/model/index.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from './concerto-base'; -export * from './concerto-metamodel'; -export * from './commonmark'; -export * from './ciceromark'; -export * from './templatemark'; diff --git a/packages/markdown-common/types/model/templatemark.d.ts b/packages/markdown-common/types/model/templatemark.d.ts deleted file mode 100644 index f8c77352..00000000 --- a/packages/markdown-common/types/model/templatemark.d.ts +++ /dev/null @@ -1,122 +0,0 @@ -// Generated code for namespace: org.accordproject.templatemark@0.5.0 - -// imports - -// Warning: Beware of circular dependencies when modifying these imports - -// Warning: Beware of circular dependencies when modifying these imports - -// Warning: Beware of circular dependencies when modifying these imports -import {IChild,INode} from './commonmark'; -import {IDecorator} from './concerto-metamodel'; -import {IConcept} from './concerto-base'; - -// interfaces -export enum CodeType { - TYPESCRIPT = 'TYPESCRIPT', - ES_2020 = 'ES_2020', -} - -export interface ICode extends IConcept { - type: CodeType; - contents: string; -} - -export interface IElementDefinition extends IChild { - name: string; - elementType?: string; - decorators?: IDecorator[]; -} - -export type ElementDefinitionUnion = IVariableDefinition | -IFormulaDefinition | -IBlockDefinition; - -export interface IVariableDefinition extends IElementDefinition { - identifiedBy?: string; -} - -export type VariableDefinitionUnion = IFormattedVariableDefinition | -IEnumVariableDefinition; - -export interface IFormattedVariableDefinition extends IVariableDefinition { - format: string; -} - -export interface IEnumVariableDefinition extends IVariableDefinition { - enumValues: string[]; -} - -export interface IFormulaDefinition extends IElementDefinition { - dependencies?: string[]; - code: ICode; -} - -export interface IBlockDefinition extends IElementDefinition { -} - -export type BlockDefinitionUnion = IClauseDefinition | -IContractDefinition | -IWithDefinition | -IConditionalDefinition | -IOptionalDefinition | -IJoinDefinition | -IListBlockDefinition | -IForeachBlockDefinition | -IWithBlockDefinition | -IConditionalBlockDefinition | -IOptionalBlockDefinition; - -export interface IClauseDefinition extends IBlockDefinition { - condition?: ICode; -} - -export interface IContractDefinition extends IBlockDefinition { -} - -export interface IWithDefinition extends IBlockDefinition { -} - -export interface IConditionalDefinition extends IBlockDefinition { - whenTrue: IChild[]; - whenFalse: IChild[]; - condition?: ICode; - dependencies?: string[]; -} - -export interface IOptionalDefinition extends IBlockDefinition { - whenSome: IChild[]; - whenNone: IChild[]; -} - -export interface IJoinDefinition extends IBlockDefinition { - separator?: string; - locale?: string; - type?: string; - style?: string; -} - -export interface IListBlockDefinition extends IBlockDefinition { - type: string; - tight: string; - start?: string; - delimiter?: string; -} - -export interface IForeachBlockDefinition extends IBlockDefinition { -} - -export interface IWithBlockDefinition extends IBlockDefinition { -} - -export interface IConditionalBlockDefinition extends IBlockDefinition { - whenTrue: IChild[]; - whenFalse: IChild[]; - condition?: ICode; -} - -export interface IOptionalBlockDefinition extends IBlockDefinition { - whenSome: IChild[]; - whenNone: IChild[]; -} - diff --git a/packages/markdown-html/.babelrc b/packages/markdown-html/.babelrc deleted file mode 100644 index ffd7a3c1..00000000 --- a/packages/markdown-html/.babelrc +++ /dev/null @@ -1,21 +0,0 @@ -{ - "presets": [ - [ - "@babel/preset-env", - { - "targets": { - "node": "6.10", - "esmodules": true - } - } - ] - ], - "env": { - "production": { - "plugins": ["@babel/plugin-proposal-object-rest-spread"] - }, - "development": { - "plugins": ["istanbul","@babel/plugin-proposal-object-rest-spread"] - } - } -} \ No newline at end of file diff --git a/packages/markdown-html/.eslintrc.cjs b/packages/markdown-html/.eslintrc.cjs new file mode 100644 index 00000000..c4ec30f5 --- /dev/null +++ b/packages/markdown-html/.eslintrc.cjs @@ -0,0 +1,37 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +module.exports = { + root: true, + env: { es2022: true, node: true, jest: true }, + extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'], + parser: '@typescript-eslint/parser', + parserOptions: { ecmaVersion: 2022, sourceType: 'module' }, + plugins: ['@typescript-eslint'], + ignorePatterns: ['node_modules/', 'lib/', 'umd/', 'coverage/', '**/*.snap'], + rules: { + 'indent': ['error', 4, { 'SwitchCase': 1 }], + 'quotes': ['error', 'single', { 'avoidEscape': true, 'allowTemplateLiterals': true }], + 'semi': ['error', 'always'], + 'no-console': 'warn', + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-var-requires': 'off', + '@typescript-eslint/no-unused-vars': ['error', { 'args': 'none', 'ignoreRestSiblings': true, 'caughtErrors': 'none' }], + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/no-empty-function': 'off', + '@typescript-eslint/ban-ts-comment': 'off', + '@typescript-eslint/no-this-alias': 'off', + 'no-unused-vars': 'off', + }, +}; diff --git a/packages/markdown-html/.eslintrc.yml b/packages/markdown-html/.eslintrc.yml deleted file mode 100644 index ec0c5d88..00000000 --- a/packages/markdown-html/.eslintrc.yml +++ /dev/null @@ -1,47 +0,0 @@ -env: - es6: true - node: true - mocha: true -extends: 'eslint:recommended' -parserOptions: - ecmaVersion: 12 - sourceType: 'script' -rules: - indent: - - error - - 4 - linebreak-style: - - warn - - unix - quotes: - - error - - single - semi: - - error - - always - no-unused-vars: - - error - - args: none - no-console: warn - curly: error - eqeqeq: error - no-throw-literal: error - strict: error - no-var: error - dot-notation: error - no-tabs: error - no-trailing-spaces: error - # no-use-before-define: error - no-useless-call: error - no-with: error - operator-linebreak: error - require-jsdoc: - - error - - require: - ClassDeclaration: true - MethodDefinition: true - FunctionDeclaration: true - valid-jsdoc: - - error - - requireReturn: false - yoda: error diff --git a/packages/markdown-html/README.md b/packages/markdown-html/README.md index 3948dcb8..e1440475 100644 --- a/packages/markdown-html/README.md +++ b/packages/markdown-html/README.md @@ -1,34 +1,55 @@ # HTML Transformer -Use `HtmlTransformer` to transform a CiceroMark DOM to/from an HTML String. +Converts a CiceroMark DOM to/from an HTML string. ## Installation ``` -npm install @accordproject/markdown-html --save +npm install @accordproject/markdown-html ``` ## Usage -``` javascript -const CiceroMarkTransformer = require('@accordproject/markdown-cicero').CiceroMarkTransformer; -const HtmlTransformer = require('@accordproject/markdown-html').HtmlTransformer; -htmlTransformer = new HtmlTransformer(); -ciceroTransformer = new CiceroMarkTransformer(); -const json = ciceroTransformer.fromMarkdown(markdownText, 'json'); -const html = htmlTransformer.toHtml(json); -``` +```ts +import { CiceroMarkTransformer } from '@accordproject/markdown-cicero'; +import { HtmlTransformer } from '@accordproject/markdown-html'; -## Using in web apps with webpack +const ciceroTransformer = new CiceroMarkTransformer(); +const htmlTransformer = new HtmlTransformer(); -If using this module in a web app with webpack, you must add the following to your webpack config in the `plugins` array. +// markdown → CiceroMark DOM → HTML +const dom = ciceroTransformer.fromMarkdown('# Hello\n\nWorld.'); +const html = htmlTransformer.toHtml(dom); +// HTML → CiceroMark DOM +const roundTripped = htmlTransformer.toCiceroMark(html); ``` -new webpack.IgnorePlugin(/jsdom$/) + +In CommonJS: + +```js +const { CiceroMarkTransformer } = require('@accordproject/markdown-cicero'); +const { HtmlTransformer } = require('@accordproject/markdown-html'); ``` +## Using in web apps with webpack + +This package depends on [`jsdom`](https://github.com/jsdom/jsdom) for HTML parsing in Node, but uses the browser's built-in `DOMParser` at runtime when available. To prevent webpack from bundling `jsdom`, add an `IgnorePlugin` for it: + +```js +// webpack.config.js (webpack 5) +const webpack = require('webpack'); + +module.exports = { + // ... + plugins: [ + new webpack.IgnorePlugin({ + resourceRegExp: /^\.$/, + contextRegExp: /jsdom$/, + }), + ], +}; +``` ## License Accord Project source code files are made available under the Apache License, Version 2.0 (Apache-2.0), located in the LICENSE file. Accord Project documentation files are made available under the Creative Commons Attribution 4.0 International License (CC-BY-4.0), available at http://creativecommons.org/licenses/by/4.0/. - -© 2017-2019 Clause, Inc. diff --git a/packages/markdown-html/jest.config.js b/packages/markdown-html/jest.config.js index 30b57cb7..c8c592ed 100644 --- a/packages/markdown-html/jest.config.js +++ b/packages/markdown-html/jest.config.js @@ -13,186 +13,18 @@ */ 'use strict'; -// For a detailed explanation regarding each configuration property, visit: -// https://jestjs.io/docs/en/configuration.html +/** @type {import('jest').Config} */ module.exports = { - // All imported modules in your tests should be mocked automatically - // automock: false, - - // Stop running tests after `n` failures - // bail: 0, - - // Respect "browser" field in package.json when resolving modules - // browser: false, - - // The directory where Jest should store its cached dependency information - // cacheDirectory: "/private/var/folders/tv/4ljndl3s2jg90nxd8h7f3bgr0000gn/T/jest_dx", - - // Automatically clear mock calls and instances between every test + preset: 'ts-jest', + testEnvironment: 'node', clearMocks: true, - - // Indicates whether the coverage information should be collected while executing the test - // collectCoverage: false, - - // An array of glob patterns indicating a set of files for which coverage information should be collected - collectCoverageFrom: [ 'src/**/*.js' ], - - // The directory where Jest should output its coverage files + testMatch: ['/src/**/*.test.ts'], + collectCoverageFrom: ['src/**/*.ts', '!src/**/*.test.ts', '!src/**/*.d.ts'], coverageDirectory: 'coverage', - - // An array of regexp pattern strings used to skip coverage collection - coveragePathIgnorePatterns: [ - '/src/', - '/node_modules/' - ], - - // A list of reporter names that Jest uses when writing coverage reports - coverageReporters: [ - 'json', - 'text', - 'lcov', - 'html' - ], - - // An object that configures minimum threshold enforcement for coverage results - // coverageThreshold: null, - - // A path to a custom dependency extractor - // dependencyExtractor: null, - - // Make calling deprecated APIs throw helpful error messages - // errorOnDeprecated: false, - - // Force coverage collection from ignored files using an array of glob patterns - // forceCoverageMatch: [], - - // A path to a module which exports an async function that is triggered once before all test suites - // globalSetup: null, - - // A path to a module which exports an async function that is triggered once after all test suites - // globalTeardown: null, - - // A set of global variables that need to be available in all test environments - // globals: {}, - - // An array of directory names to be searched recursively up from the requiring module's location - // moduleDirectories: [ - // "node_modules" - // ], - - // An array of file extensions your modules use - // moduleFileExtensions: [ - // "js", - // "json", - // "jsx", - // "ts", - // "tsx", - // "node" - // ], - - // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader - // modulePathIgnorePatterns: [], - - // Activates notifications for test results - // notify: false, - - // An enum that specifies notification mode. Requires { notify: true } - // notifyMode: "failure-change", - - // A preset that is used as a base for Jest's configuration - // preset: null, - - // Run tests from one or more projects - // projects: null, - - // Use this configuration option to add custom reporters to Jest - // reporters: undefined, - - // Automatically reset mock state between every test - // resetMocks: false, - - // Reset the module registry before running each individual test - // resetModules: false, - - // A path to a custom resolver - // resolver: null, - - // Automatically restore mock state between every test - // restoreMocks: false, - - // The root directory that Jest should scan for tests and modules within - // rootDir: null, - - // A list of paths to directories that Jest should use to search for files in - // roots: [ - // "" - // ], - - // Allows you to use a custom runner instead of Jest's default test runner - // runner: "jest-runner", - - // The paths to modules that run some code to configure or set up the testing environment before each test - // setupFiles: [], - - // A list of paths to modules that run some code to configure or set up the testing framework before each test - // setupFilesAfterEnv: [], - - // A list of paths to snapshot serializer modules Jest should use for snapshot testing - // snapshotSerializers: [], - - // The test environment that will be used for testing - testEnvironment: 'node', - - // Options that will be passed to the testEnvironment - // testEnvironmentOptions: {}, - - // Adds a location field to test results - // testLocationInResults: false, - - // The glob patterns Jest uses to detect test files - // testMatch: [ - // "**/__tests__/**/*.[jt]s?(x)", - // "**/?(*.)+(spec|test).[tj]s?(x)" - // ], - - // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped - // testPathIgnorePatterns: [ - // "/node_modules/" - // ], - - // The regexp pattern or array of patterns that Jest uses to detect test files - // testRegex: [], - - // This option allows the use of a custom results processor - // testResultsProcessor: null, - - // This option allows use of a custom test runner - // testRunner: "jasmine2", - - // This option sets the URL for the jsdom environment. It is reflected in properties such as location.href - // testURL: "http://localhost", - - // Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout" - // timers: "real", - - // A map from regular expressions to paths to transformers - // transform: null, - - // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation - // transformIgnorePatterns: [ - // "/node_modules/" - // ], - - // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them - // unmockedModulePathPatterns: undefined, - - // Indicates whether each individual test should be reported during the run - // verbose: null, - - // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode - // watchPathIgnorePatterns: [], - - // Whether to use watchman for file crawling - // watchman: true, + coveragePathIgnorePatterns: ['/node_modules/'], + coverageReporters: ['json', 'text', 'lcov', 'html'], + transform: { + '^.+\\.tsx?$': ['ts-jest', { tsconfig: '/tsconfig.test.json' }], + }, }; diff --git a/packages/markdown-html/jsdoc.json b/packages/markdown-html/jsdoc.json deleted file mode 100644 index 66f38e56..00000000 --- a/packages/markdown-html/jsdoc.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "tags": { - "allowUnknownTags": true, - "dictionaries": ["jsdoc", "closure"] - }, - "source": { - "include": [ - "./src", - "./index.js" - ], - "includePattern": ".+\\.js(doc|x)?$" - }, - "plugins": ["plugins/markdown"], - "templates": { - "logoFile": "", - "cleverLinks": false, - "monospaceLinks": false, - "dateFormat": "ddd MMM Do YYYY", - "outputSourceFiles": true, - "outputSourcePath": true, - "systemName": "Accord Project Cicero SDK", - "footer": "", - "copyright": "Released under the Apache License v2.0", - "navType": "vertical", - "theme": "spacelab", - "linenums": true, - "collapseSymbols": false, - "inverseNav": true, - "protocol": "html://", - "methodHeadingReturns": false - }, - "markdown": { - "parser": "gfm", - "hardwrap": true - } -} \ No newline at end of file diff --git a/packages/markdown-html/package.json b/packages/markdown-html/package.json index b847fd65..fa503821 100644 --- a/packages/markdown-html/package.json +++ b/packages/markdown-html/package.json @@ -1,6 +1,6 @@ { "name": "@accordproject/markdown-html", - "version": "0.16.25", + "version": "1.0.0", "description": "Transform CiceroDOM to HTML", "engines": { "node": ">=22", @@ -10,32 +10,28 @@ "access": "public" }, "files": [ - "bin", "lib", - "types", "umd" ], - "main": "index.js", + "main": "lib/index.js", "browser": "umd/markdown-html.js", + "types": "lib/index.d.ts", + "typings": "lib/index.d.ts", "scripts": { "webpack": "webpack --config webpack.config.js --mode production", - "build": "babel src -d lib --copy-files && npm run build:types", - "build:types": "tsc", - "build:dist": "NODE_ENV=production babel src -d lib --copy-files", - "build:watch": "babel src -d lib --copy-files --watch", - "prepublishOnly": "npm run build:dist && npm run webpack", - "prepare": "npm run build", + "build": "tsc -p tsconfig.json", + "build:dist": "npm run build && npm run webpack", + "prepublishOnly": "npm run build:dist", "pretest": "npm run lint && npm run build", - "lint": "eslint .", + "lint": "eslint . --ext .ts", "postlint": "npm run licchk", "licchk": "license-check-and-add", - "test": "jest --timeOut=10000 --silent", + "test": "jest --silent", + "test:noisy": "jest", "test:updateSnapshot": "jest --updateSnapshot --silent", - "test:cov": "npm run lint && jest --timeOut=10000 --coverage --silent", - "jsdoc": "jsdoc -c jsdoc.json package.json", - "typescript": "jsdoc -t node_modules/tsd-jsdoc/dist -r ./src/" + "test:cov": "npm run lint && npm run build && jest --coverage --silent", + "clean": "rimraf lib umd" }, - "typings": "types/index.d.ts", "repository": { "type": "git", "url": "git+https://github.com/accordproject/markdown-transform.git", @@ -54,33 +50,34 @@ }, "homepage": "https://github.com/accordproject/markdown-transform#readme", "devDependencies": { - "@babel/cli": "7.25.9", - "@babel/core": "7.26.0", - "@babel/preset-env": "7.26.0", - "ajv": "^8.17.1", - "babel-loader": "^8.4.1", - "babel-plugin-istanbul": "7.0.0", + "@types/jest": "^29.5.12", + "@types/jsdom": "^21.1.7", + "@types/node": "^20.11.30", + "@typescript-eslint/eslint-plugin": "^7.4.0", + "@typescript-eslint/parser": "^7.4.0", "browserify-zlib": "0.2.0", "buffer": "^6.0.3", "crypto-browserify": "3.12.1", "eslint": "8.57.1", "https-browserify": "1.0.0", "jest": "^29.7.0", - "jsdoc": "^4.0.4", "license-check-and-add": "2.3.6", "raw-loader": "^4.0.2", + "rimraf": "^5.0.5", "stream-browserify": "3.0.0", "stream-http": "3.2.0", - "tsd-jsdoc": "^2.5.0", + "ts-jest": "^29.1.2", + "ts-loader": "^9.5.1", + "typescript": "^5.9.3", "vm-browserify": "^1.1.2", "webpack": "5.104.1", - "webpack-cli": "5.1.4", - "typescript": "^5.9.3" + "webpack-cli": "5.1.4" }, "dependencies": { "@accordproject/markdown-cicero": "*", "@accordproject/markdown-common": "*", "jsdom": "^25.0.1", + "process": "^0.11.10", "type-of": "^2.0.1" }, "license-check-and-add-config": { @@ -88,12 +85,9 @@ "license": "header.txt", "exact_paths_method": "EXCLUDE", "exact_paths": [ - "externalModels/.npmignore", - "externalModels/.gitignore", "coverage", - "index.d.ts", + "__snapshots__", "./system", - "./lib", "LICENSE", "node_modules", ".nyc-output", @@ -110,7 +104,7 @@ ], "insert_license": false, "license_formats": { - "js|njk|pegjs|cto|acl|qry": { + "ts|tsx|js|njk|pegjs|cto|acl|qry": { "prepend": "/*", "append": " */", "eachLine": { @@ -126,28 +120,5 @@ "file": "header.md" } } - }, - "nyc": { - "produce-source-map": "true", - "sourceMap": "inline", - "reporter": [ - "lcov", - "text", - "text-summary", - "html", - "json" - ], - "include": [ - "src/**/*.js" - ], - "exclude": [ - "scripts/**/*.js" - ], - "all": true, - "check-coverage": true, - "statements": 88, - "branches": 76, - "functions": 84, - "lines": 88 } } diff --git a/packages/markdown-html/src/HtmlTransformer.test.js b/packages/markdown-html/src/HtmlTransformer.test.ts similarity index 70% rename from packages/markdown-html/src/HtmlTransformer.test.js rename to packages/markdown-html/src/HtmlTransformer.test.ts index 23b18302..388cee66 100644 --- a/packages/markdown-html/src/HtmlTransformer.test.js +++ b/packages/markdown-html/src/HtmlTransformer.test.ts @@ -12,46 +12,30 @@ * limitations under the License. */ -// @ts-nocheck -/* eslint-disable no-undef */ -'use strict'; - -const fs = require('fs'); -const CiceroMarkTransformer = require('@accordproject/markdown-cicero').CiceroMarkTransformer; -const { CommonMarkModel } = require('@accordproject/markdown-common'); -const HtmlTransformer = require('./HtmlTransformer'); - -let htmlTransformer = null; -let ciceroTransformer = null; - -/** - * Prepare the text for parsing (normalizes new lines, etc) - * @param {string} input - the text for the clause - * @return {string} - the normalized text for the clause - */ -function normalizeNLs(input) { - // we replace all \r and \n with \n - let text = input.replace(/\r/gm,''); - return text; +import * as fs from 'fs'; +import { CiceroMarkTransformer } from '@accordproject/markdown-cicero'; +import { CommonMarkModel } from '@accordproject/markdown-common'; +import { HtmlTransformer } from './HtmlTransformer'; + +let htmlTransformer: HtmlTransformer; +let ciceroTransformer: CiceroMarkTransformer; + +function normalizeNLs(input: string): string { + return input.replace(/\r/gm, ''); } -// @ts-ignore beforeAll(() => { htmlTransformer = new HtmlTransformer(); ciceroTransformer = new CiceroMarkTransformer(); }); -/** - * Get the name and contents of all markdown test files - * @returns {*} an array of name/contents tuples - */ -function getMarkdownFiles() { - const result = []; +function getMarkdownFiles(): [string, string][] { + const result: [string, string][] = []; const files = fs.readdirSync(__dirname + '/../test/data/markdown'); - files.forEach(function(file) { - if(file.endsWith('.md')) { - let contents = fs.readFileSync(__dirname + '/../test/data/markdown/' + file, 'utf8'); + files.forEach(function (file) { + if (file.endsWith('.md')) { + const contents = fs.readFileSync(__dirname + '/../test/data/markdown/' + file, 'utf8'); result.push([file, contents]); } }); @@ -60,22 +44,22 @@ function getMarkdownFiles() { } describe('markdown <-> html', () => { - getMarkdownFiles().forEach(([file, markdownText], i) => { + getMarkdownFiles().forEach(([file, markdownText]) => { it(`converts ${file} to html`, () => { - const json = ciceroTransformer.fromMarkdown(markdownText, 'json'); - expect(json).toMatchSnapshot(); // (1) + const json = ciceroTransformer.fromMarkdown(markdownText); + expect(json).toMatchSnapshot(); const html = htmlTransformer.toHtml(json); - expect(html).toMatchSnapshot(); // (2) - const ciceroMarkDom = htmlTransformer.toCiceroMark(html, 'json'); + expect(html).toMatchSnapshot(); + const ciceroMarkDom = htmlTransformer.toCiceroMark(html); expect(ciceroMarkDom).toEqual(json); }); }); it('converts unwrapped
  • to html', () => { - const ciceroMarkDom = htmlTransformer.toCiceroMark('

    Hello

  • list item
  • World.

    ', 'json'); - expect(ciceroMarkDom).toMatchSnapshot(); // (1) + const ciceroMarkDom = htmlTransformer.toCiceroMark('

    Hello

  • list item
  • World.

    '); + expect(ciceroMarkDom).toMatchSnapshot(); const md = ciceroTransformer.toMarkdown(ciceroMarkDom); - expect(md).toMatchSnapshot(); // (2) + expect(md).toMatchSnapshot(); }); }); @@ -101,14 +85,9 @@ describe('html table deserialization', () => { nodes: [ { $class: `${commonmarkNamespace}.Strong`, - nodes: [ - { - $class: `${commonmarkNamespace}.Text`, - text: 'Employee Details' - } - ] - } - ] + nodes: [{ $class: `${commonmarkNamespace}.Text`, text: 'Employee Details' }], + }, + ], }); expect(ciceroMarkDom.nodes[1].$class).toBe(`${commonmarkNamespace}.Table`); }); @@ -129,26 +108,23 @@ describe('html table deserialization', () => { expect(ciceroMarkDom.nodes).toHaveLength(2); const strong = ciceroMarkDom.nodes[0].nodes[0]; expect(strong.$class).toBe(`${commonmarkNamespace}.Strong`); - // every child of Strong must be inline (no Paragraph leaked in) - strong.nodes.forEach(child => { + strong.nodes.forEach((child: any) => { expect(child.$class).not.toBe(`${commonmarkNamespace}.Paragraph`); }); expect(strong.nodes).toEqual([ { $class: `${commonmarkNamespace}.Text`, text: 'See ' }, { $class: `${commonmarkNamespace}.Emph`, - nodes: [{ $class: `${commonmarkNamespace}.Text`, text: 'also' }] - } + nodes: [{ $class: `${commonmarkNamespace}.Text`, text: 'also' }], + }, ]); expect(ciceroMarkDom.nodes[1].$class).toBe(`${commonmarkNamespace}.Table`); }); it('does not leak a caption node when it appears outside a table', () => { const ciceroMarkDom = htmlTransformer.toCiceroMark('Orphan caption'); - - // the old caption rule produced a node with no $class; ensure none leak - ciceroMarkDom.nodes.forEach(n => expect(typeof n.$class).toBe('string')); - expect(ciceroMarkDom.nodes.some(n => n.type === 'caption')).toBe(false); + ciceroMarkDom.nodes.forEach((n: any) => expect(typeof n.$class).toBe('string')); + expect(ciceroMarkDom.nodes.some((n: any) => n.type === 'caption')).toBe(false); }); it('normalizes whitespace in table cells', () => { @@ -166,12 +142,7 @@ describe('html table deserialization', () => { `); const cellNodes = ciceroMarkDom.nodes[0].nodes[0].nodes[0].nodes[0].nodes; - expect(cellNodes).toEqual([ - { - $class: `${commonmarkNamespace}.Text`, - text: 'First Second' - } - ]); + expect(cellNodes).toEqual([{ $class: `${commonmarkNamespace}.Text`, text: 'First Second' }]); }); it('promotes tbody rows with header cells to table head when thead is missing', () => { @@ -187,9 +158,9 @@ describe('html table deserialization', () => { const table = ciceroMarkDom.nodes[0]; expect(table.nodes).toHaveLength(2); expect(table.nodes[0].$class).toBe(`${commonmarkNamespace}.TableHead`); - expect(table.nodes[0].nodes[0].nodes.map(cell => cell.$class)).toEqual([ + expect(table.nodes[0].nodes[0].nodes.map((cell: any) => cell.$class)).toEqual([ + `${commonmarkNamespace}.HeaderCell`, `${commonmarkNamespace}.HeaderCell`, - `${commonmarkNamespace}.HeaderCell` ]); expect(table.nodes[1].$class).toBe(`${commonmarkNamespace}.TableBody`); expect(table.nodes[1].nodes).toHaveLength(1); @@ -212,17 +183,13 @@ describe('html table deserialization', () => { }); }); -/** - * Get the name and contents of all ciceromark test files - * @returns {*} an array of name/contents tuples - */ -function getCiceroMarkFiles() { - const result = []; +function getCiceroMarkFiles(): [string, string][] { + const result: [string, string][] = []; const files = fs.readdirSync(__dirname + '/../test/data/ciceromark'); - files.forEach(function(file) { - if(file.endsWith('.json')) { - let contents = normalizeNLs(fs.readFileSync(__dirname + '/../test/data/ciceromark/' + file, 'utf8')); + files.forEach(function (file) { + if (file.endsWith('.json')) { + const contents = normalizeNLs(fs.readFileSync(__dirname + '/../test/data/ciceromark/' + file, 'utf8')); result.push([file, contents]); } }); @@ -231,26 +198,20 @@ function getCiceroMarkFiles() { } describe('ciceromark <-> html', () => { - getCiceroMarkFiles().forEach( ([file, jsonText], index) => { + getCiceroMarkFiles().forEach(([file, jsonText]) => { it(`converts ${file} to and from CiceroMark`, () => { const value = JSON.parse(jsonText); const html = htmlTransformer.toHtml(value); - // check no changes to html - expect(html).toMatchSnapshot(); // (1) + expect(html).toMatchSnapshot(); - // load expected html - const expectedHtml = normalizeNLs(fs.readFileSync(__dirname + '/../test/data/ciceromark/' + file.replace(/.json$/,'.html'), 'utf8')); - expect(expectedHtml).toMatchSnapshot(); // (2) + const expectedHtml = normalizeNLs(fs.readFileSync(__dirname + '/../test/data/ciceromark/' + file.replace(/.json$/, '.html'), 'utf8')); + expect(expectedHtml).toMatchSnapshot(); - // convert the expected html and compare const expectedCiceroMarkValue = htmlTransformer.toCiceroMark(expectedHtml); - expect(expectedCiceroMarkValue).toMatchSnapshot(); // (3) + expect(expectedCiceroMarkValue).toMatchSnapshot(); - // check that html created from ciceromark and from the expected html is the same expect(html).toEqual(expectedHtml); - - // check roundtrip expect(expectedCiceroMarkValue).toEqual(value); }); }); @@ -268,9 +229,9 @@ describe('renderVariableValue - relationship variables', () => { 'value': '"resource:org.accordproject.organization@0.2.0.Organization#Party A"', 'identifiedBy': 'identifier', 'name': 'buyer', - 'elementType': 'org.accordproject.organization@0.2.0.Organization' - }] - }] + 'elementType': 'org.accordproject.organization@0.2.0.Organization', + }], + }], }; const html = htmlTransformer.toHtml(ciceroMarkJson); expect(html).toContain('>Party A<'); @@ -287,9 +248,9 @@ describe('renderVariableValue - relationship variables', () => { '$class': 'org.accordproject.ciceromark@0.6.0.Variable', 'value': '"Widgets"', 'name': 'deliverable', - 'elementType': 'String' - }] - }] + 'elementType': 'String', + }], + }], }; const html = htmlTransformer.toHtml(ciceroMarkJson); expect(html).toContain('>"Widgets"<'); @@ -306,9 +267,9 @@ describe('renderVariableValue - relationship variables', () => { 'value': 'resource:org.accordproject.organization@0.2.0.Organization#Party B', 'identifiedBy': 'identifier', 'name': 'seller', - 'elementType': 'org.accordproject.organization@0.2.0.Organization' - }] - }] + 'elementType': 'org.accordproject.organization@0.2.0.Organization', + }], + }], }; const html = htmlTransformer.toHtml(ciceroMarkJson); expect(html).toContain('>Party B<'); diff --git a/packages/markdown-html/src/HtmlTransformer.js b/packages/markdown-html/src/HtmlTransformer.ts similarity index 58% rename from packages/markdown-html/src/HtmlTransformer.js rename to packages/markdown-html/src/HtmlTransformer.ts index 56681de7..608174e9 100644 --- a/packages/markdown-html/src/HtmlTransformer.js +++ b/packages/markdown-html/src/HtmlTransformer.ts @@ -12,42 +12,31 @@ * limitations under the License. */ -'use strict'; - -/** @typedef {import('@accordproject/markdown-common/types/model/commonmark').IDocument} IDocument */ -/** @typedef {import('@accordproject/concerto-core').Typed} Typed */ -/** @typedef {IDocument|Typed} HtmlInput */ - -const ToHtmlStringVisitor = require('./ToHtmlStringVisitor'); -const ToCiceroMarkVisitor = require('./ToCiceroMarkVisitor'); -const CiceroMarkTransformer = require('@accordproject/markdown-cicero').CiceroMarkTransformer; +import { ToHtmlStringVisitor } from './ToHtmlStringVisitor'; +import { ToCiceroMarkVisitor } from './ToCiceroMarkVisitor'; +import { CiceroMarkTransformer } from '@accordproject/markdown-cicero'; /** * Converts a CiceroMark or CommonMark DOM to HTML */ -class HtmlTransformer { +export class HtmlTransformer { + options: any; + ciceroMarkTransformer: CiceroMarkTransformer; - /** - * Construct the parser. - * @param {object} [options] configuration options - */ - constructor(options = {}) { + constructor(options: any = {}) { this.options = options; this.ciceroMarkTransformer = new CiceroMarkTransformer(); } /** * Converts a CiceroMark DOM to an html string - * @param {HtmlInput} input - CiceroMark DOM object - * @returns {string} the html string */ - toHtml(input) { - - if(!input.getType) { + toHtml(input: any): string { + if (!input.getType) { input = this.ciceroMarkTransformer.getSerializer().fromJSON(input); } - const parameters = {}; + const parameters: any = {}; parameters.result = ''; parameters.first = true; parameters.indent = 0; @@ -58,13 +47,11 @@ class HtmlTransformer { /** * Converts an html string to a CiceroMark DOM - * @param {string} input - html string - * @returns {IDocument} CiceroMark DOM */ - toCiceroMark(input) { + toCiceroMark(input: string): any { const visitor = new ToCiceroMarkVisitor(this.options); return visitor.toCiceroMark(input); } } -module.exports = HtmlTransformer; +export default HtmlTransformer; diff --git a/packages/markdown-html/src/ToCiceroMarkVisitor.js b/packages/markdown-html/src/ToCiceroMarkVisitor.ts similarity index 52% rename from packages/markdown-html/src/ToCiceroMarkVisitor.js rename to packages/markdown-html/src/ToCiceroMarkVisitor.ts index 809a3d98..43aa5548 100644 --- a/packages/markdown-html/src/ToCiceroMarkVisitor.js +++ b/packages/markdown-html/src/ToCiceroMarkVisitor.ts @@ -12,78 +12,65 @@ * limitations under the License. */ -'use strict'; -const { CommonMarkModel } = require('@accordproject/markdown-common'); -const jsdom = typeof DOMParser === 'undefined' ? require('jsdom') : null; +import { CommonMarkModel } from '@accordproject/markdown-common'; +import defaultRules, { Rule } from './rules'; + +// eslint-disable-next-line @typescript-eslint/no-var-requires const typeOf = require('type-of'); -const defaultRules = require('./rules'); -const JSDOM = jsdom ? jsdom.JSDOM : null; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const jsdom: any = typeof DOMParser === 'undefined' ? require('jsdom') : null; +const JSDOM: any = jsdom ? jsdom.JSDOM : null; /** * Converts an html string to a CiceroMark DOM - * */ -class ToCiceroMarkVisitor { +export class ToCiceroMarkVisitor { + options: any; + rules: Rule[]; - /** - * Construct the parser. - * @param {object} [options] configuration options - */ - constructor(options = {}) { - let { - rules = [], - } = options; + constructor(options: any = {}) { + const { rules = [] } = options; this.options = options; this.rules = [...rules, ...defaultRules]; } /** * Filter out cruft newline nodes inserted by the DOM parser. - * - * @param {Object} element DOM element - * @return {Boolean} true if node is not a new line */ - cruftNewline(element) { + cruftNewline(element: any): boolean { return !(element.nodeName === '#text' && element.nodeValue === '\n'); } /** * Deserialize a DOM element. - * - * @param {Object} element DOM element - * @param {boolean} ignoreSpace override - * @return {Any} node */ - deserializeElement(element, ignoreSpace) { - let node; + deserializeElement(element: any, ignoreSpace?: boolean): any { + let node: any; - //console.log('tagName', element.tagName); if (!element.tagName) { element.tagName = ''; } - const next = (elements, ignoreSpace) => { + const next = (elements: any, ignoreSpace?: boolean): any => { if (Object.prototype.toString.call(elements) === '[object NodeList]') { elements = Array.from(elements); } switch (typeOf(elements)) { - case 'array': - return this.deserializeElements(elements, ignoreSpace); - case 'object': - return this.deserializeElement(elements, ignoreSpace); - case 'null': - case 'undefined': - return; - default: - throw new Error( - `The \`next\` argument was called with invalid children: "${elements}".` - ); + case 'array': + return this.deserializeElements(elements, ignoreSpace); + case 'object': + return this.deserializeElement(elements, ignoreSpace); + case 'null': + case 'undefined': + return; + default: + throw new Error(`The \`next\` argument was called with invalid children: "${elements}".`); } }; for (const rule of this.rules) { - if (!rule.deserialize) {continue;} + if (!rule.deserialize) { continue; } const ret = rule.deserialize(element, next, ignoreSpace); const type = typeOf(ret); @@ -93,17 +80,13 @@ class ToCiceroMarkVisitor { type !== 'null' && type !== 'undefined' ) { - throw new Error( - `A rule returned an invalid deserialized representation: "${node}".` - ); + throw new Error(`A rule returned an invalid deserialized representation: "${node}".`); } if (ret === undefined) { continue; } else if (ret === null) { return null; - // } else if (ret.object === 'mark') { - // node = this.deserializeMark(ret); // will we need this?? } else { node = ret; } @@ -124,26 +107,20 @@ class ToCiceroMarkVisitor { /** * Deserialize an array of DOM elements. - * - * @param {Array} elements DOM elements - * @param {boolean} ignoreSpace override - * @return {Array} array of nodes */ - deserializeElements(elements = [], ignoreSpace) { - let nodes = []; + deserializeElements(elements: any[] = [], ignoreSpace?: boolean): any[] { + let nodes: any[] = []; - elements.filter(this.cruftNewline).forEach(element => { - // console.log('element -- ', element); + elements.filter(this.cruftNewline).forEach((element) => { const node = this.deserializeElement(element, ignoreSpace); - // console.log('node -- ', node); switch (typeOf(node)) { - case 'array': - nodes = nodes.concat(node); - break; - case 'object': - nodes.push(node); - break; + case 'array': + nodes = nodes.concat(node); + break; + case 'object': + nodes.push(node); + break; } }); @@ -152,14 +129,9 @@ class ToCiceroMarkVisitor { /** * Converts an html string to a CiceroMark DOM - * @param {string} input - html string - * @param {string} [format] result format, defaults to 'concerto'. Pass - * 'json' to return the JSON data. - * @returns {*} CiceroMark DOM */ - toCiceroMark(input, format='concerto') { - let fragment; - // eslint-disable-next-line no-undef + toCiceroMark(input: string, _format = 'concerto'): any { + let fragment: any; if (typeof DOMParser === 'undefined') { fragment = JSDOM.fragment(input); } else { @@ -167,15 +139,13 @@ class ToCiceroMarkVisitor { fragment = new DOMParser().parseFromString(input, 'text/html'); } const children = Array.from(fragment.childNodes); - // console.log('children -- ', children); const nodes = this.deserializeElements(children, true); - // console.log('nodes', nodes); return { - '$class': `${CommonMarkModel.NAMESPACE}.${'Document'}`, + '$class': `${CommonMarkModel.NAMESPACE}.Document`, nodes, xmlns: 'http://commonmark.org/xml/1.0', }; } } -module.exports = ToCiceroMarkVisitor; \ No newline at end of file +export default ToCiceroMarkVisitor; diff --git a/packages/markdown-html/src/ToHtmlStringVisitor.js b/packages/markdown-html/src/ToHtmlStringVisitor.js deleted file mode 100644 index be5f8f2f..00000000 --- a/packages/markdown-html/src/ToHtmlStringVisitor.js +++ /dev/null @@ -1,264 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -/** - * Returns the display value for a variable node. - * For relationship types (identifiedBy is set), strips the resource URI scheme - * (resource:namespace#identifier) and returns just the identifier. - * @param {object} thing - the variable node - * @returns {string} the display value - */ -function renderVariableValue(thing) { - if (thing.identifiedBy && typeof thing.value === 'string') { - const unquoted = thing.value.replace(/^"(.*)"$/s, '$1'); - if (unquoted.startsWith('resource:') && unquoted.includes('#')) { - return unquoted.split('#').pop(); - } - } - return thing.value; -} - -// const CiceroMarkTransformer = require('@accordproject/markdown-cicero').CiceroMarkTransformer; - -/** - * Converts a commonmark model instance to an html string. - * - */ -class ToHtmlStringVisitor { - - /** - * Construct the visitor - * @param {*} [options] configuration options - */ - constructor(options) { - this.options = options; - } - - /** - * Visits a sub-tree and return the html - * @param {*} visitor the visitor to use - * @param {*} thing the node to visit - * @param {*} [parameters] optional parameters - * @returns {string} the html for the sub tree - */ - static visitChildren(visitor, thing, parameters) { - if(!parameters) { - parameters = {}; - parameters.result = ''; - parameters.first = false; - parameters.indent = 0; - } - - if(thing.nodes) { - thing.nodes.forEach(node => { - node.accept(visitor, parameters); - }); - } - - return parameters.result; - } - - /** - * Visit a node - * @param {*} thing the object being visited - * @param {*} parameters the parameters - */ - visit(thing, parameters) { - - switch(thing.getType()) { - case 'Clause': { - let attributes = `class="clause" name="${thing.name}"`; - if (thing.elementType) { - attributes += ` elementType="${thing.elementType}"`; - } - if (thing.src) { - attributes += ` src="${thing.src}"`; - } - parameters.result += `
    \n${ToHtmlStringVisitor.visitChildren(this, thing)}
    \n`; - } - break; - case 'Variable': { - let attributes = `class="variable" name="${thing.name}"`; - if (thing.elementType) { - attributes += ` elementType="${thing.elementType}"`; - } - if (thing.identifiedBy) { - attributes += ` identifiedBy="${thing.identifiedBy}"`; - } - parameters.result += `${renderVariableValue(thing)}`; - - } - break; - case 'FormattedVariable': { - let attributes = `class="variable" name="${thing.name}" format="${thing.format}"`; - if (thing.elementType) { - attributes += ` elementType="${thing.elementType}"`; - } - if (thing.identifiedBy) { - attributes += ` identifiedBy="${thing.identifiedBy}"`; - } - parameters.result += `${renderVariableValue(thing)}`; - - } - break; - case 'EnumVariable': { - const enumValues = encodeURIComponent(JSON.stringify(thing.enumValues)); - let attributes = `class="variable" name="${thing.name}" enumValues="${enumValues}"`; - if (thing.elementType) { - attributes += ` elementType="${thing.elementType}"`; - } - if (thing.identifiedBy) { - attributes += ` identifiedBy="${thing.identifiedBy}"`; - } - parameters.result += `${renderVariableValue(thing)}`; - - } - break; - case 'Conditional': - parameters.result += `${thing.nodes[0] ? thing.nodes[0].text : ''}`; - break; - case 'Optional': - parameters.result += `${thing.nodes[0] ? thing.nodes[0].text : ''}`; - break; - case 'Formula': { - let attributes = `class="formula" name="${thing.name}"`; - if (thing.code) { - attributes += ` code="${encodeURIComponent(thing.code)}"`; - } - if (thing.dependencies) { - attributes += ` dependencies="${encodeURIComponent(JSON.stringify(thing.dependencies))}"`; - } - parameters.result += `${thing.value}`; - } - break; - case 'CodeBlock': { - const info = thing.info; - if (info) { - parameters.result += `
    ${thing.text}
    \n`; - } else { - parameters.result += `
    ${thing.text}
    \n`; - } - } - break; - case 'Code': - parameters.result += `${thing.text}`; - break; - case 'HtmlInline': - parameters.result += `${thing.text}`; - break; - case 'Emph': - parameters.result += `${ToHtmlStringVisitor.visitChildren(this, thing)}`; - break; - case 'Strong': - parameters.result += `${ToHtmlStringVisitor.visitChildren(this, thing)}`; - break; - case 'BlockQuote': { - parameters.result += `
    ${ToHtmlStringVisitor.visitChildren(this, thing)}
    \n`; - } - break; - case 'Heading': { - const level = parseInt(thing.level); - parameters.result += `${ToHtmlStringVisitor.visitChildren(this, thing)}\n`; - } - break; - case 'ThematicBreak': - parameters.result += '\n
    \n'; - break; - case 'Linebreak': - parameters.result += '
    '; - break; - case 'Softbreak': - parameters.result += '\n'; - break; - case 'Link': - parameters.result += `${ToHtmlStringVisitor.visitChildren(this, thing)}`; - break; - case 'Image': - parameters.result += ``; - break; - case 'Paragraph': - parameters.result += `

    ${ToHtmlStringVisitor.visitChildren(this, thing)}

    \n`; - break; - case 'HtmlBlock':{ - parameters.result += `
    ${thing.text}
    \n`; - break; - } - case 'Text': - parameters.result += `${thing.text}`; - break; - case 'List': { - // Always start with a new line - parameters.result += '\n'; - const { delimiter, start, tight} = thing; - if(thing.type === 'ordered') { - parameters.result += `
      `; - } - else { - parameters.result += `
        `; - } - - thing.nodes.forEach(item => { - parameters.result += `\n
      • ${ToHtmlStringVisitor.visitChildren(this, item)}
      • `; - }); - - if(thing.type === 'ordered') { - parameters.result += '
    '; - } - else { - parameters.result += ''; - } - } - break; - case 'Item': - parameters.result += `
  • ${ToHtmlStringVisitor.visitChildren(this, thing)}
  • \n`; - break; - - case 'Table': - parameters.result += '\n'; - parameters.result += `${ToHtmlStringVisitor.visitChildren(this, thing)}\n
    \n`; - break; - - case 'TableHead': - parameters.result += `\n${ToHtmlStringVisitor.visitChildren(this,thing)}\n`; - break; - - case 'TableBody': - parameters.result += `\n${ToHtmlStringVisitor.visitChildren(this,thing)}\n`; - break; - - case 'TableRow': - parameters.result += `\n\n${ToHtmlStringVisitor.visitChildren(this,thing)}`; - break; - - case 'TableCell': - parameters.result += `${ToHtmlStringVisitor.visitChildren(this,thing)}\n`; - break; - - case 'HeaderCell': - parameters.result += `${ToHtmlStringVisitor.visitChildren(this,thing)}\n`; - break; - - case 'Document': - parameters.result += `\n\n\n
    \n${ToHtmlStringVisitor.visitChildren(this, thing)}
    \n\n`; - break; - default: - throw new Error(`Unhandled type ${thing.getType()}`); - } - parameters.first = false; - } -} - -module.exports = ToHtmlStringVisitor; \ No newline at end of file diff --git a/packages/markdown-html/src/ToHtmlStringVisitor.ts b/packages/markdown-html/src/ToHtmlStringVisitor.ts new file mode 100644 index 00000000..627cecf2 --- /dev/null +++ b/packages/markdown-html/src/ToHtmlStringVisitor.ts @@ -0,0 +1,201 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Returns the display value for a variable node. + * For relationship types (identifiedBy is set), strips the resource URI scheme + * (resource:namespace#identifier) and returns just the identifier. + */ +function renderVariableValue(thing: any): string { + if (thing.identifiedBy && typeof thing.value === 'string') { + const unquoted = thing.value.replace(/^"(.*)"$/s, '$1'); + if (unquoted.startsWith('resource:') && unquoted.includes('#')) { + return unquoted.split('#').pop(); + } + } + return thing.value; +} + +/** + * Converts a commonmark model instance to an html string. + */ +export class ToHtmlStringVisitor { + options: any; + + constructor(options?: any) { + this.options = options; + } + + static visitChildren(visitor: ToHtmlStringVisitor, thing: any, parameters?: any): string { + if (!parameters) { + parameters = { result: '', first: false, indent: 0 }; + } + if (thing.nodes) { + thing.nodes.forEach((node: any) => { + node.accept(visitor, parameters); + }); + } + return parameters.result; + } + + visit(thing: any, parameters: any): void { + switch (thing.getType()) { + case 'Clause': { + let attributes = `class="clause" name="${thing.name}"`; + if (thing.elementType) { attributes += ` elementType="${thing.elementType}"`; } + if (thing.src) { attributes += ` src="${thing.src}"`; } + parameters.result += `
    \n${ToHtmlStringVisitor.visitChildren(this, thing)}
    \n`; + break; + } + case 'Variable': { + let attributes = `class="variable" name="${thing.name}"`; + if (thing.elementType) { attributes += ` elementType="${thing.elementType}"`; } + if (thing.identifiedBy) { attributes += ` identifiedBy="${thing.identifiedBy}"`; } + parameters.result += `${renderVariableValue(thing)}`; + break; + } + case 'FormattedVariable': { + let attributes = `class="variable" name="${thing.name}" format="${thing.format}"`; + if (thing.elementType) { attributes += ` elementType="${thing.elementType}"`; } + if (thing.identifiedBy) { attributes += ` identifiedBy="${thing.identifiedBy}"`; } + parameters.result += `${renderVariableValue(thing)}`; + break; + } + case 'EnumVariable': { + const enumValues = encodeURIComponent(JSON.stringify(thing.enumValues)); + let attributes = `class="variable" name="${thing.name}" enumValues="${enumValues}"`; + if (thing.elementType) { attributes += ` elementType="${thing.elementType}"`; } + if (thing.identifiedBy) { attributes += ` identifiedBy="${thing.identifiedBy}"`; } + parameters.result += `${renderVariableValue(thing)}`; + break; + } + case 'Conditional': + parameters.result += `${thing.nodes[0] ? thing.nodes[0].text : ''}`; + break; + case 'Optional': + parameters.result += `${thing.nodes[0] ? thing.nodes[0].text : ''}`; + break; + case 'Formula': { + let attributes = `class="formula" name="${thing.name}"`; + if (thing.code) { attributes += ` code="${encodeURIComponent(thing.code)}"`; } + if (thing.dependencies) { attributes += ` dependencies="${encodeURIComponent(JSON.stringify(thing.dependencies))}"`; } + parameters.result += `${thing.value}`; + break; + } + case 'CodeBlock': { + const info = thing.info; + if (info) { + parameters.result += `
    ${thing.text}
    \n`; + } else { + parameters.result += `
    ${thing.text}
    \n`; + } + break; + } + case 'Code': + parameters.result += `${thing.text}`; + break; + case 'HtmlInline': + parameters.result += `${thing.text}`; + break; + case 'Emph': + parameters.result += `${ToHtmlStringVisitor.visitChildren(this, thing)}`; + break; + case 'Strong': + parameters.result += `${ToHtmlStringVisitor.visitChildren(this, thing)}`; + break; + case 'BlockQuote': + parameters.result += `
    ${ToHtmlStringVisitor.visitChildren(this, thing)}
    \n`; + break; + case 'Heading': { + const level = parseInt(thing.level); + parameters.result += `${ToHtmlStringVisitor.visitChildren(this, thing)}\n`; + break; + } + case 'ThematicBreak': + parameters.result += '\n
    \n'; + break; + case 'Linebreak': + parameters.result += '
    '; + break; + case 'Softbreak': + parameters.result += '\n'; + break; + case 'Link': + parameters.result += `${ToHtmlStringVisitor.visitChildren(this, thing)}`; + break; + case 'Image': + parameters.result += ``; + break; + case 'Paragraph': + parameters.result += `

    ${ToHtmlStringVisitor.visitChildren(this, thing)}

    \n`; + break; + case 'HtmlBlock': + parameters.result += `
    ${thing.text}
    \n`; + break; + case 'Text': + parameters.result += `${thing.text}`; + break; + case 'List': { + parameters.result += '\n'; + const { delimiter, start, tight } = thing; + if (thing.type === 'ordered') { + parameters.result += `
      `; + } else { + parameters.result += `
        `; + } + + thing.nodes.forEach((item: any) => { + parameters.result += `\n
      • ${ToHtmlStringVisitor.visitChildren(this, item)}
      • `; + }); + + if (thing.type === 'ordered') { + parameters.result += '
    '; + } else { + parameters.result += ''; + } + break; + } + case 'Item': + parameters.result += `
  • ${ToHtmlStringVisitor.visitChildren(this, thing)}
  • \n`; + break; + case 'Table': + parameters.result += '\n'; + parameters.result += `${ToHtmlStringVisitor.visitChildren(this, thing)}\n
    \n`; + break; + case 'TableHead': + parameters.result += `\n${ToHtmlStringVisitor.visitChildren(this, thing)}\n`; + break; + case 'TableBody': + parameters.result += `\n${ToHtmlStringVisitor.visitChildren(this, thing)}\n`; + break; + case 'TableRow': + parameters.result += `\n\n${ToHtmlStringVisitor.visitChildren(this, thing)}`; + break; + case 'TableCell': + parameters.result += `${ToHtmlStringVisitor.visitChildren(this, thing)}\n`; + break; + case 'HeaderCell': + parameters.result += `${ToHtmlStringVisitor.visitChildren(this, thing)}\n`; + break; + case 'Document': + parameters.result += `\n\n\n
    \n${ToHtmlStringVisitor.visitChildren(this, thing)}
    \n\n`; + break; + default: + throw new Error(`Unhandled type ${thing.getType()}`); + } + parameters.first = false; + } +} + +export default ToHtmlStringVisitor; diff --git a/packages/markdown-html/src/__snapshots__/HtmlTransformer.test.js.snap b/packages/markdown-html/src/__snapshots__/HtmlTransformer.test.ts.snap similarity index 100% rename from packages/markdown-html/src/__snapshots__/HtmlTransformer.test.js.snap rename to packages/markdown-html/src/__snapshots__/HtmlTransformer.test.ts.snap diff --git a/packages/markdown-html/src/helpers.js b/packages/markdown-html/src/helpers.js deleted file mode 100644 index 767735a9..00000000 --- a/packages/markdown-html/src/helpers.js +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -/** - * Determine whether a node's text content is entirely whitespace. - * - * @param {object} node A node implementing the |CharacterData| interface - * (i.e. a |Text|, |Comment|, or |CDATASection| node - * @return {boolean} True if all of the text content of |nod| is whitespace, - * otherwise false. - */ -function isAllWhitespace( node ) -{ - return !(/[^\t\n\r ]/.test(node.textContent)); -} - - -/** - * Determine if a node should be ignored by the iterator functions. - * - * @param {object} node An object implementing the DOM1 |Node| interface. - * @param {boolean} ignoreSpace override - * @return {boolean} true if the node is: - * 1) A |Text| node that is all whitespace - * 2) A |Comment| node - * and otherwise false. - */ -function isIgnorable(node, ignoreSpace) -{ - return (ignoreSpace && // Is ignoring space allowed in this context - ((node.nodeType === 8) || // A comment node - (node.nodeType === 3) && isAllWhitespace(node))); // a text node, all ws -} - -module.exports = { - isAllWhitespace, - isIgnorable, -}; \ No newline at end of file diff --git a/packages/markdown-html/src/helpers.ts b/packages/markdown-html/src/helpers.ts new file mode 100644 index 00000000..ae424cc4 --- /dev/null +++ b/packages/markdown-html/src/helpers.ts @@ -0,0 +1,31 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Determine whether a node's text content is entirely whitespace. + */ +export function isAllWhitespace(node: any): boolean { + return !(/[^\t\n\r ]/.test(node.textContent)); +} + +/** + * Determine if a node should be ignored by the iterator functions. + */ +export function isIgnorable(node: any, ignoreSpace: boolean): boolean { + return ( + ignoreSpace && + (node.nodeType === 8 || + (node.nodeType === 3 && isAllWhitespace(node))) + ); +} diff --git a/packages/markdown-html/index.js b/packages/markdown-html/src/index.ts similarity index 71% rename from packages/markdown-html/index.js rename to packages/markdown-html/src/index.ts index b9023f4a..186fbe21 100644 --- a/packages/markdown-html/index.js +++ b/packages/markdown-html/src/index.ts @@ -12,13 +12,9 @@ * limitations under the License. */ -'use strict'; +import { HtmlTransformer } from './HtmlTransformer'; +import { ToHtmlStringVisitor } from './ToHtmlStringVisitor'; -/** - * Export HTML transformer - * @module markdown-transform - */ - -module.exports.HtmlTransformer = require('./lib/HtmlTransformer'); -module.exports.ToHtmlStringVisitor = require('./lib/ToHtmlStringVisitor'); +export { HtmlTransformer, ToHtmlStringVisitor }; +export default { HtmlTransformer, ToHtmlStringVisitor }; diff --git a/packages/markdown-html/src/rules.js b/packages/markdown-html/src/rules.ts similarity index 59% rename from packages/markdown-html/src/rules.js rename to packages/markdown-html/src/rules.ts index 08547977..f28665d0 100644 --- a/packages/markdown-html/src/rules.js +++ b/packages/markdown-html/src/rules.ts @@ -12,47 +12,43 @@ * limitations under the License. */ -'use strict'; -const { CommonMarkUtils, CiceroMarkModel, CommonMarkModel } = require('@accordproject/markdown-common'); -const { isIgnorable } = require('./helpers'); +import { CommonMarkUtils, CiceroMarkModel, CommonMarkModel } from '@accordproject/markdown-common'; +import { isIgnorable } from './helpers'; -/** - * A rule to deserialize text nodes. - * @type {Object} - */ -const TEXT_RULE = { +export interface Rule { + deserialize(el: any, next: (children: any, ignoreSpace?: boolean) => any, ignoreSpace?: boolean): any; +} + +const TEXT_RULE: Rule = { deserialize(el, next, ignoreSpace) { - // text nodes will be of type 3 - if (el.nodeType === 3 && !isIgnorable(el, ignoreSpace)) { - const textArray = el.nodeValue.split('\n'); - const textNodes = textArray.map(text => { + if (el.nodeType === 3 && !isIgnorable(el, !!ignoreSpace)) { + const textArray: string[] = el.nodeValue.split('\n'); + const textNodes = textArray.map((text) => { if (text) { return { - '$class': `${CommonMarkModel.NAMESPACE}.${'Text'}`, + '$class': `${CommonMarkModel.NAMESPACE}.Text`, text, }; } }); - const result = [...textNodes].map((node, i) => i < textNodes.length - 1 ? [node, { '$class': `${CommonMarkModel.NAMESPACE}.${'Softbreak'}` }] : [node]).reduce((a, b) => a.concat(b)).filter(n => !!n); + const result = [...textNodes] + .map((node, i) => i < textNodes.length - 1 ? [node, { '$class': `${CommonMarkModel.NAMESPACE}.Softbreak` }] : [node]) + .reduce((a, b) => a.concat(b)) + .filter((n) => !!n); return result; } - } + }, }; -// make sure these are getting set as attributes when we create the html -/** - * A rule to deserialize list nodes. - * @type {Object} - */ -const LIST_RULE = { +const LIST_RULE: Rule = { deserialize(el, next, ignoreSpace) { if (el.tagName && el.tagName.toLowerCase() === 'ul') { return { '$class': `${CommonMarkModel.NAMESPACE}.List`, type: 'bullet', tight: el.getAttribute('tight') ? el.getAttribute('tight') : 'true', - nodes: next(el.childNodes, ignoreSpace) + nodes: next(el.childNodes, ignoreSpace), }; } if (el.tagName && el.tagName.toLowerCase() === 'ol') { @@ -62,83 +58,61 @@ const LIST_RULE = { delimiter: el.getAttribute('delimiter'), start: el.getAttribute('start'), tight: el.getAttribute('tight') ? el.getAttribute('tight') : 'true', - nodes: next(el.childNodes, ignoreSpace) + nodes: next(el.childNodes, ignoreSpace), }; } if (el.tagName && el.tagName.toLowerCase() === 'li') { return { '$class': `${CommonMarkModel.NAMESPACE}.Item`, - nodes: next(el.childNodes) + nodes: next(el.childNodes), }; } - } + }, }; -/** - * A rule to deserialize linebreak nodes. - * @type {Object} - */ -const LINEBREAK_RULE = { - deserialize(el, next, ignoreSpace) { +const LINEBREAK_RULE: Rule = { + deserialize(el) { if (el.tagName && el.tagName.toLowerCase() === 'br') { - return { - '$class': `${CommonMarkModel.NAMESPACE}.Linebreak` - }; + return { '$class': `${CommonMarkModel.NAMESPACE}.Linebreak` }; } - } + }, }; -/** - * A rule to deserialize paragraph nodes. - * @type {Object} - */ -const PARAGRAPH_RULE = { - deserialize(el, next, ignoreSpace) { +const PARAGRAPH_RULE: Rule = { + deserialize(el, next) { if (el.tagName && el.tagName.toLowerCase() === 'p') { return { '$class': `${CommonMarkModel.NAMESPACE}.Paragraph`, - nodes: next(el.childNodes, false) + nodes: next(el.childNodes, false), }; } - } + }, }; -/** - * A rule to deserialize strong (bold) nodes. - * @type {Object} - */ -const STRONG_RULE = { +const STRONG_RULE: Rule = { deserialize(el, next, ignoreSpace) { if (el.tagName && el.tagName.toLowerCase() === 'strong') { return { '$class': `${CommonMarkModel.NAMESPACE}.Strong`, - nodes: next(el.childNodes, ignoreSpace) + nodes: next(el.childNodes, ignoreSpace), }; } - } + }, }; -/** - * A rule to deserialize emph (italic) nodes. - * @type {Object} - */ -const EMPH_RULE = { +const EMPH_RULE: Rule = { deserialize(el, next, ignoreSpace) { if (el.tagName && el.tagName.toLowerCase() === 'em') { return { '$class': `${CommonMarkModel.NAMESPACE}.Emph`, - nodes: next(el.childNodes, ignoreSpace) + nodes: next(el.childNodes, ignoreSpace), }; } - } + }, }; -/** - * A rule to deserialize link nodes. - * @type {Object} - */ -const LINK_RULE = { +const LINK_RULE: Rule = { deserialize(el, next, ignoreSpace) { if (el.tagName && el.tagName.toLowerCase() === 'a') { return { @@ -148,14 +122,10 @@ const LINK_RULE = { title: el.getAttribute('title') ? el.getAttribute('title') : '', }; } - } + }, }; -/** - * A rule to deserialize link nodes. - * @type {Object} - */ -const IMAGE_RULE = { +const IMAGE_RULE: Rule = { deserialize(el, next, ignoreSpace) { if (el.tagName && el.tagName.toLowerCase() === 'img') { return { @@ -165,38 +135,21 @@ const IMAGE_RULE = { title: el.getAttribute('title') ? el.getAttribute('title') : '', }; } - } + }, }; -/** - * A rule to deserialize heading nodes (all levels). - * @type {Object} - */ -const HEADING_RULE = { - deserialize(el, next, ignoreSpace) { +const HEADING_RULE: Rule = { + deserialize(el, next) { if (el.tagName) { - let level; + let level: string | null; switch (el.tagName.toLowerCase()) { - case 'h1': - level = '1'; - break; - case 'h2': - level = '2'; - break; - case 'h3': - level = '3'; - break; - case 'h4': - level = '4'; - break; - case 'h5': - level = '5'; - break; - case 'h6': - level = '6'; - break; - default: - level = null; + case 'h1': level = '1'; break; + case 'h2': level = '2'; break; + case 'h3': level = '3'; break; + case 'h4': level = '4'; break; + case 'h5': level = '5'; break; + case 'h6': level = '6'; break; + default: level = null; } if (level) { return { @@ -206,49 +159,22 @@ const HEADING_RULE = { }; } } - } + }, }; -/** - * A rule to deserialize thematic break nodes. - * @type {Object} - */ -const THEMATIC_BREAK_RULE = { - deserialize(el, next, ignoreSpace) { +const THEMATIC_BREAK_RULE: Rule = { + deserialize(el) { if (el.tagName && el.tagName.toLowerCase() === 'hr') { - return { - '$class': `${CommonMarkModel.NAMESPACE}.ThematicBreak`, - }; + return { '$class': `${CommonMarkModel.NAMESPACE}.ThematicBreak` }; } - } + }, }; -/** - * A rule to deserialize html block nodes. - * @type {Object} - */ -// Look at common mark dingus and see how they are mapping html blocks -// TODO: figure out how to handle custom html blocks (could be anything?) -// const HTML_BLOCK_RULE = { -// deserialize(el, next, ignoreSpace) { -// if (el.tagName ) { -// return { -// '$class': `${CommonMarkModel.NAMESPACE}.HtmlBlock`, -// }; -// } -// } -// }; - -/** - * A rule to deserialize code block nodes. - * @type {Object} - */ -const CODE_BLOCK_RULE = { - deserialize(el, next, ignoreSpace) { +const CODE_BLOCK_RULE: Rule = { + deserialize(el) { if (el.tagName && el.tagName.toLowerCase() === 'pre' && el.getAttribute('class') === 'code_block') { const children = el.childNodes; - if (children.length === 1 && children[0].tagName.toLowerCase() === 'code') - { + if (children.length === 1 && children[0].tagName.toLowerCase() === 'code') { const info = children[0].getAttribute('data-ciceromark'); if (info) { const decodedInfo = decodeURIComponent(info); @@ -259,7 +185,7 @@ const CODE_BLOCK_RULE = { info: decodedInfo, tag, }; - } else { + } else { return { '$class': `${CommonMarkModel.NAMESPACE}.CodeBlock`, text: children[0].textContent, @@ -267,53 +193,39 @@ const CODE_BLOCK_RULE = { } } } - } + }, }; -/** - * A rule to deserialize inline code nodes. - * @type {Object} - */ -const INLINE_CODE_RULE = { - deserialize(el, next, ignoreSpace) { +const INLINE_CODE_RULE: Rule = { + deserialize(el) { if (el.tagName && el.tagName.toLowerCase() === 'code') { - { - return { - '$class': `${CommonMarkModel.NAMESPACE}.Code`, - text: el.textContent, - }; - } + return { + '$class': `${CommonMarkModel.NAMESPACE}.Code`, + text: el.textContent, + }; } - } + }, }; -/** - * A rule to deserialize block quote nodes. - * @type {Object} - */ -const BLOCK_QUOTE_RULE = { +const BLOCK_QUOTE_RULE: Rule = { deserialize(el, next, ignoreSpace) { if (el.tagName && el.tagName.toLowerCase() === 'blockquote') { return { '$class': `${CommonMarkModel.NAMESPACE}.BlockQuote`, - nodes: next(el.childNodes, ignoreSpace) + nodes: next(el.childNodes, ignoreSpace), }; } - } + }, }; -/** - * A rule to deserialize clause nodes. - * @type {Object} - */ -const CLAUSE_RULE = { - deserialize(el, next, ignoreSpace) { +const CLAUSE_RULE: Rule = { + deserialize(el, next) { const tag = el.tagName; if (tag && tag.toLowerCase() === 'div' && el.getAttribute('class') === 'clause') { - const clause = { + const clause: any = { '$class': `${CiceroMarkModel.NAMESPACE}.Clause`, name: el.getAttribute('name'), - nodes: next(el.childNodes, false) + nodes: next(el.childNodes, false), }; if (el.getAttribute('elementType')) { clause.elementType = el.getAttribute('elementType'); @@ -323,24 +235,20 @@ const CLAUSE_RULE = { } return clause; } - } + }, }; -/** - * A rule to deserialize variable nodes. - * @type {Object} - */ -const VARIABLE_RULE = { - deserialize(el, next, ignoreSpace) { +const VARIABLE_RULE: Rule = { + deserialize(el) { const { tagName } = el; if (tagName && tagName.toLowerCase() === 'span' && el.getAttribute('class') === 'variable') { - let variable; + let variable: any; if (el.getAttribute('format')) { variable = { '$class': `${CiceroMarkModel.NAMESPACE}.FormattedVariable`, name: el.getAttribute('name'), value: el.textContent, - format: el.getAttribute('format') + format: el.getAttribute('format'), }; } else if (el.getAttribute('enumValues')) { variable = { @@ -364,15 +272,11 @@ const VARIABLE_RULE = { } return variable; } - } + }, }; -/** - * A rule to deserialize conditional nodes. - * @type {Object} - */ -const CONDITIONAL_RULE = { - deserialize(el, next, ignoreSpace) { +const CONDITIONAL_RULE: Rule = { + deserialize(el) { const { tagName } = el; if (tagName && tagName.toLowerCase() === 'span' && el.getAttribute('class') === 'conditional') { const text = el.textContent; @@ -382,29 +286,16 @@ const CONDITIONAL_RULE = { '$class': `${CiceroMarkModel.NAMESPACE}.Conditional`, name: el.getAttribute('name'), isTrue: text === whenTrueText, - whenTrue: whenTrueText ? [{ - '$class': `${CommonMarkModel.NAMESPACE}.Text`, - text: whenTrueText, - }] : [], - whenFalse: whenFalseText ? [{ - '$class': `${CommonMarkModel.NAMESPACE}.Text`, - text: whenFalseText, - }] : [], - nodes: [{ - '$class': `${CommonMarkModel.NAMESPACE}.Text`, - text: text, - }], + whenTrue: whenTrueText ? [{ '$class': `${CommonMarkModel.NAMESPACE}.Text`, text: whenTrueText }] : [], + whenFalse: whenFalseText ? [{ '$class': `${CommonMarkModel.NAMESPACE}.Text`, text: whenFalseText }] : [], + nodes: [{ '$class': `${CommonMarkModel.NAMESPACE}.Text`, text: text }], }; } - } + }, }; -/** - * A rule to deserialize optional nodes. - * @type {Object} - */ -const OPTIONAL_RULE = { - deserialize(el, next, ignoreSpace) { +const OPTIONAL_RULE: Rule = { + deserialize(el) { const { tagName } = el; if (tagName && tagName.toLowerCase() === 'span' && el.getAttribute('class') === 'optional') { const text = el.textContent; @@ -414,32 +305,19 @@ const OPTIONAL_RULE = { '$class': `${CiceroMarkModel.NAMESPACE}.Optional`, name: el.getAttribute('name'), hasSome: text === whenSomeText, - whenSome: whenSomeText ? [{ - '$class': `${CommonMarkModel.NAMESPACE}.Text`, - text: whenSomeText, - }] : [], - whenNone: whenNoneText ? [{ - '$class': `${CommonMarkModel.NAMESPACE}.Text`, - text: whenNoneText, - }] : [], - nodes: [{ - '$class': `${CommonMarkModel.NAMESPACE}.Text`, - text: text, - }], + whenSome: whenSomeText ? [{ '$class': `${CommonMarkModel.NAMESPACE}.Text`, text: whenSomeText }] : [], + whenNone: whenNoneText ? [{ '$class': `${CommonMarkModel.NAMESPACE}.Text`, text: whenNoneText }] : [], + nodes: [{ '$class': `${CommonMarkModel.NAMESPACE}.Text`, text: text }], }; } - } + }, }; -/** - * A rule to deserialize formulas - * @type {Object} - */ -const FORMULA_RULE = { - deserialize(el, next, ignoreSpace) { +const FORMULA_RULE: Rule = { + deserialize(el) { const { tagName } = el; if (tagName && tagName.toLowerCase() === 'span' && el.getAttribute('class') === 'formula') { - const formula = { + const formula: any = { '$class': `${CiceroMarkModel.NAMESPACE}.Formula`, name: el.getAttribute('name'), value: el.textContent, @@ -452,40 +330,29 @@ const FORMULA_RULE = { } return formula; } - } + }, }; -/** - * A rule to deserialize html inline nodes. - * @type {Object} - */ -const HTML_INLINE_RULE = { - deserialize(el, next, ignoreSpace) { +const HTML_INLINE_RULE: Rule = { + deserialize(el) { const { tagName } = el; if (tagName && tagName.toLowerCase() === 'span' && el.getAttribute('class') === 'html_inline') { - { - const text = el.innerHTML; - const tag = CommonMarkUtils.parseHtmlBlock(text); - return { - '$class': `${CommonMarkModel.NAMESPACE}.HtmlInline`, - text: text, - tag, - }; - } + const text = el.innerHTML; + const tag = CommonMarkUtils.parseHtmlBlock(text); + return { + '$class': `${CommonMarkModel.NAMESPACE}.HtmlInline`, + text: text, + tag, + }; } - } + }, }; -/** - * A rule to deserialize html block nodes. - * @type {Object} - */ -const HTML_BLOCK_RULE = { - deserialize(el, next, ignoreSpace) { +const HTML_BLOCK_RULE: Rule = { + deserialize(el) { if (el.tagName && el.tagName.toLowerCase() === 'pre' && el.getAttribute('class') === 'html_block') { const children = el.childNodes; - if (children.length === 1 && children[0].tagName.toLowerCase() === 'code') - { + if (children.length === 1 && children[0].tagName.toLowerCase() === 'code') { const text = children[0].innerHTML; const tag = CommonMarkUtils.parseHtmlBlock(text); return { @@ -495,34 +362,19 @@ const HTML_BLOCK_RULE = { }; } } - } + }, }; -// CommonMark inline node classes. A may contain flow content -// (paragraphs, lists, etc.), but a Strong node may only contain inline -// children, so caption content is flattened to these classes before it is -// wrapped in Strong. const INLINE_CLASSES = [ 'Text', 'Emph', 'Strong', 'Code', 'Link', 'Image', - 'Softbreak', 'Linebreak', 'HtmlInline' -].map(name => `${CommonMarkModel.NAMESPACE}.${name}`); - -/** - * Flatten caption content to inline-only nodes so it can be safely wrapped in - * a Strong node. Block-level wrappers (e.g. Paragraph) are unwrapped to their - * inline children; nodes with no inline content are dropped. - * @param {Array} nodes - the list of nodes - * @returns {Array} - the inline-only list of nodes - */ -function toInlineNodes(nodes) { - if (!nodes) { - return []; - } + 'Softbreak', 'Linebreak', 'HtmlInline', +].map((name) => `${CommonMarkModel.NAMESPACE}.${name}`); + +function toInlineNodes(nodes: any): any[] { + if (!nodes) { return []; } const list = Array.isArray(nodes) ? nodes : [nodes]; - return list.reduce((acc, node) => { - if (!node) { - return acc; - } + return list.reduce((acc, node) => { + if (!node) { return acc; } if (INLINE_CLASSES.includes(node.$class)) { acc.push(node); } else if (node.nodes) { @@ -532,25 +384,16 @@ function toInlineNodes(nodes) { }, []); } -/** - * Clean table cell nodes by removing Softbreaks and normalizing whitespace. - * @param {Array} nodes - the list of nodes - * @returns {Array} - the cleaned list of nodes - */ -function cleanTableNodes(nodes) { +function cleanTableNodes(nodes: any): any[] { const NS = CommonMarkModel.NAMESPACE; const TEXT = `${NS}.Text`; const SOFT = `${NS}.Softbreak`; - if (!nodes) { - return []; - } - nodes = Array.isArray(nodes) ? nodes : [nodes]; + if (!nodes) { return []; } + const arr = Array.isArray(nodes) ? nodes : [nodes]; - const merged = nodes.reduce((acc, node) => { - if (!node) { - return acc; - } + const merged = arr.reduce((acc, node) => { + if (!node) { return acc; } let newNode = { ...node }; if (newNode.nodes) { @@ -571,8 +414,7 @@ function cleanTableNodes(nodes) { return acc; }, []); - // Normalize whitespace inside Text nodes - merged.forEach(n => { + merged.forEach((n) => { if (n.$class === TEXT) { n.text = n.text.replace(/\s+/g, ' '); } @@ -585,34 +427,33 @@ function cleanTableNodes(nodes) { merged[merged.length - 1].text = merged[merged.length - 1].text.replace(/\s+$/, ''); } - return merged.filter(n => n.$class !== TEXT || n.text.length > 0); + return merged.filter((n) => n.$class !== TEXT || n.text.length > 0); } - -const TABLE_RULE = { +const TABLE_RULE: Rule = { deserialize(el, next, ignoreSpace) { if (el.tagName && el.tagName.toLowerCase() === 'table') { const children = next(el.childNodes, ignoreSpace); - let tableNodes = children.filter(node => + let tableNodes = children.filter((node: any) => node.$class === `${CommonMarkModel.NAMESPACE}.TableHead` || node.$class === `${CommonMarkModel.NAMESPACE}.TableBody` ); - let head = tableNodes.find(n => n.$class === `${CommonMarkModel.NAMESPACE}.TableHead`); - const body = tableNodes.find(n => n.$class === `${CommonMarkModel.NAMESPACE}.TableBody`); + let head = tableNodes.find((n: any) => n.$class === `${CommonMarkModel.NAMESPACE}.TableHead`); + const body = tableNodes.find((n: any) => n.$class === `${CommonMarkModel.NAMESPACE}.TableBody`); if (!head && body && body.nodes && body.nodes.length > 0) { const firstRow = body.nodes[0]; - const hasHeaderCells = firstRow.nodes && firstRow.nodes.some(n => n.$class === `${CommonMarkModel.NAMESPACE}.HeaderCell`); + const hasHeaderCells = firstRow.nodes && firstRow.nodes.some((n: any) => n.$class === `${CommonMarkModel.NAMESPACE}.HeaderCell`); if (hasHeaderCells) { head = { $class: `${CommonMarkModel.NAMESPACE}.TableHead`, - nodes: [firstRow] + nodes: [firstRow], }; const newBody = { $class: `${CommonMarkModel.NAMESPACE}.TableBody`, - nodes: body.nodes.slice(1) + nodes: body.nodes.slice(1), }; tableNodes = [head, newBody]; } @@ -623,26 +464,17 @@ const TABLE_RULE = { nodes: tableNodes, }; - // A is handled here (rather than via its own rule) so the - // caption node never leaks into the output when it appears outside - // of a table. Its content is flattened to inline-only nodes so the - // Strong wrapper stays valid CommonMark, and the bolded caption is - // emitted as its own Paragraph block before the table - block-level - // spacing is left to the Markdown serializer. const captionElement = Array.from(el.childNodes).find( - child => child.tagName && child.tagName.toLowerCase() === 'caption' + (child: any) => child.tagName && child.tagName.toLowerCase() === 'caption' ); if (captionElement) { - const captionNodes = cleanTableNodes(toInlineNodes(next(captionElement.childNodes, ignoreSpace))); + const captionNodes = cleanTableNodes(toInlineNodes(next((captionElement as any).childNodes, ignoreSpace))); if (captionNodes.length > 0) { const captionParagraph = { $class: `${CommonMarkModel.NAMESPACE}.Paragraph`, nodes: [ - { - $class: `${CommonMarkModel.NAMESPACE}.Strong`, - nodes: captionNodes - } - ] + { $class: `${CommonMarkModel.NAMESPACE}.Strong`, nodes: captionNodes }, + ], }; return [captionParagraph, table]; } @@ -654,21 +486,21 @@ const TABLE_RULE = { const nodes = next(el.childNodes); return { $class: `${CommonMarkModel.NAMESPACE}.TableHead`, - nodes: nodes.filter(n => n.$class === `${CommonMarkModel.NAMESPACE}.TableRow`), + nodes: nodes.filter((n: any) => n.$class === `${CommonMarkModel.NAMESPACE}.TableRow`), }; } if (el.tagName && el.tagName.toLowerCase() === 'tbody') { const nodes = next(el.childNodes); return { $class: `${CommonMarkModel.NAMESPACE}.TableBody`, - nodes: nodes.filter(n => n.$class === `${CommonMarkModel.NAMESPACE}.TableRow`), + nodes: nodes.filter((n: any) => n.$class === `${CommonMarkModel.NAMESPACE}.TableRow`), }; } if (el.tagName && el.tagName.toLowerCase() === 'tr') { const nodes = next(el.childNodes); return { $class: `${CommonMarkModel.NAMESPACE}.TableRow`, - nodes: nodes.filter(n => n.$class === `${CommonMarkModel.NAMESPACE}.HeaderCell` || n.$class === `${CommonMarkModel.NAMESPACE}.TableCell`), + nodes: nodes.filter((n: any) => n.$class === `${CommonMarkModel.NAMESPACE}.HeaderCell` || n.$class === `${CommonMarkModel.NAMESPACE}.TableCell`), }; } if (el.tagName && el.tagName.toLowerCase() === 'th') { @@ -686,7 +518,7 @@ const TABLE_RULE = { }, }; -const rules = [ +const rules: Rule[] = [ LIST_RULE, PARAGRAPH_RULE, STRONG_RULE, @@ -707,8 +539,7 @@ const rules = [ HTML_INLINE_RULE, HTML_BLOCK_RULE, IMAGE_RULE, - TABLE_RULE + TABLE_RULE, ]; - -module.exports = rules; +export default rules; diff --git a/packages/markdown-html/tsconfig.json b/packages/markdown-html/tsconfig.json index 92add5b4..fb3e9f82 100644 --- a/packages/markdown-html/tsconfig.json +++ b/packages/markdown-html/tsconfig.json @@ -1,11 +1,11 @@ { + "extends": "../../tsconfig.base.json", "compilerOptions": { - "allowJs": true, + "rootDir": "src", + "outDir": "lib", "declaration": true, - "emitDeclarationOnly": true, - "outDir": "types", - "strict": false + "sourceMap": true }, - "include": ["index.js", "lib/**/*.js"], - "exclude": ["**/*.test.js"] + "include": ["src/**/*.ts"], + "exclude": ["src/**/*.test.ts", "lib", "node_modules"] } diff --git a/packages/markdown-html/tsconfig.test.json b/packages/markdown-html/tsconfig.test.json new file mode 100644 index 00000000..58810225 --- /dev/null +++ b/packages/markdown-html/tsconfig.test.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "rootDir": ".", + "noEmit": true + }, + "include": ["src/**/*.ts"], + "exclude": ["lib", "node_modules"] +} diff --git a/packages/markdown-html/types/index.d.ts b/packages/markdown-html/types/index.d.ts deleted file mode 100644 index c7fd65c9..00000000 --- a/packages/markdown-html/types/index.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export const HtmlTransformer: typeof import("./lib/HtmlTransformer"); -export const ToHtmlStringVisitor: typeof import("./lib/ToHtmlStringVisitor"); diff --git a/packages/markdown-html/types/lib/HtmlTransformer.d.ts b/packages/markdown-html/types/lib/HtmlTransformer.d.ts deleted file mode 100644 index e19d5f2d..00000000 --- a/packages/markdown-html/types/lib/HtmlTransformer.d.ts +++ /dev/null @@ -1,31 +0,0 @@ -export = HtmlTransformer; -/** - * Converts a CiceroMark or CommonMark DOM to HTML - */ -declare class HtmlTransformer { - /** - * Construct the parser. - * @param {object} [options] configuration options - */ - constructor(...args: any[]); - options: any; - ciceroMarkTransformer: import("@accordproject/markdown-cicero/types/lib/CiceroMarkTransformer"); - /** - * Converts a CiceroMark DOM to an html string - * @param {HtmlInput} input - CiceroMark DOM object - * @returns {string} the html string - */ - toHtml(input: HtmlInput): string; - /** - * Converts an html string to a CiceroMark DOM - * @param {string} input - html string - * @returns {IDocument} CiceroMark DOM - */ - toCiceroMark(input: string): IDocument; -} -declare namespace HtmlTransformer { - export { IDocument, Typed, HtmlInput }; -} -type IDocument = import("@accordproject/markdown-common/types/model/commonmark").IDocument; -type Typed = import("@accordproject/concerto-core").Typed; -type HtmlInput = IDocument | Typed; diff --git a/packages/markdown-html/types/lib/ToCiceroMarkVisitor.d.ts b/packages/markdown-html/types/lib/ToCiceroMarkVisitor.d.ts deleted file mode 100644 index 5677284a..00000000 --- a/packages/markdown-html/types/lib/ToCiceroMarkVisitor.d.ts +++ /dev/null @@ -1,45 +0,0 @@ -export = ToCiceroMarkVisitor; -/** - * Converts an html string to a CiceroMark DOM - * - */ -declare class ToCiceroMarkVisitor { - /** - * Construct the parser. - * @param {object} [options] configuration options - */ - constructor(...args: any[]); - options: any; - rules: any[]; - /** - * Filter out cruft newline nodes inserted by the DOM parser. - * - * @param {Object} element DOM element - * @return {Boolean} true if node is not a new line - */ - cruftNewline(element: any): boolean; - /** - * Deserialize a DOM element. - * - * @param {Object} element DOM element - * @param {boolean} ignoreSpace override - * @return {Any} node - */ - deserializeElement(element: any, ignoreSpace: boolean): Any; - /** - * Deserialize an array of DOM elements. - * - * @param {Array} elements DOM elements - * @param {boolean} ignoreSpace override - * @return {Array} array of nodes - */ - deserializeElements(...args: any[]): any[]; - /** - * Converts an html string to a CiceroMark DOM - * @param {string} input - html string - * @param {string} [format] result format, defaults to 'concerto'. Pass - * 'json' to return the JSON data. - * @returns {*} CiceroMark DOM - */ - toCiceroMark(input: string, ...args: any[]): any; -} diff --git a/packages/markdown-html/types/lib/ToHtmlStringVisitor.d.ts b/packages/markdown-html/types/lib/ToHtmlStringVisitor.d.ts deleted file mode 100644 index fa7524bd..00000000 --- a/packages/markdown-html/types/lib/ToHtmlStringVisitor.d.ts +++ /dev/null @@ -1,23 +0,0 @@ -export = ToHtmlStringVisitor; -declare class ToHtmlStringVisitor { - /** - * Visits a sub-tree and return the html - * @param {*} visitor the visitor to use - * @param {*} thing the node to visit - * @param {*} [parameters] optional parameters - * @returns {string} the html for the sub tree - */ - static visitChildren(visitor: any, thing: any, parameters?: any): string; - /** - * Construct the visitor - * @param {*} [options] configuration options - */ - constructor(options?: any); - options: any; - /** - * Visit a node - * @param {*} thing the object being visited - * @param {*} parameters the parameters - */ - visit(thing: any, parameters: any): void; -} diff --git a/packages/markdown-html/types/lib/helpers.d.ts b/packages/markdown-html/types/lib/helpers.d.ts deleted file mode 100644 index 063de283..00000000 --- a/packages/markdown-html/types/lib/helpers.d.ts +++ /dev/null @@ -1,12 +0,0 @@ -export function isAllWhitespace(node: any): boolean; -/** - * Determine if a node should be ignored by the iterator functions. - * - * @param {object} node An object implementing the DOM1 |Node| interface. - * @param {boolean} ignoreSpace override - * @return {boolean} true if the node is: - * 1) A |Text| node that is all whitespace - * 2) A |Comment| node - * and otherwise false. - */ -export function isIgnorable(node: object, ignoreSpace: boolean): boolean; diff --git a/packages/markdown-html/types/lib/rules.d.ts b/packages/markdown-html/types/lib/rules.d.ts deleted file mode 100644 index 3e9de56b..00000000 --- a/packages/markdown-html/types/lib/rules.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export = rules; -declare var rules: any[]; diff --git a/packages/markdown-html/webpack.config.js b/packages/markdown-html/webpack.config.js index 25a4e129..f1b5fe98 100644 --- a/packages/markdown-html/webpack.config.js +++ b/packages/markdown-html/webpack.config.js @@ -14,65 +14,36 @@ 'use strict'; -let path = require('path'); +const path = require('path'); const webpack = require('webpack'); const packageJson = require('./package.json'); module.exports = { - entry: { - client: [ - './index.js' - ] - }, + entry: { client: ['./src/index.ts'] }, output: { path: path.join(__dirname, 'umd'), filename: 'markdown-html.js', - library: { - name: 'markdown-html', - type: 'umd', - }, + library: { name: 'markdown-html', type: 'umd' }, umdNamedDefine: true, }, plugins: [ - new webpack.BannerPlugin(`Markdown Transform v${packageJson.version} - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License.`), - new webpack.DefinePlugin({ - 'process.env': { - 'NODE_ENV': JSON.stringify('production') - } - }), + new webpack.BannerPlugin(`Markdown Transform v${packageJson.version}`), + new webpack.DefinePlugin({ 'process.env': { 'NODE_ENV': JSON.stringify('production') } }), new webpack.ProvidePlugin({ - Buffer: ['buffer', 'Buffer'] + Buffer: ['buffer', 'Buffer'], + // webpack 5 no longer polyfills `process` automatically. + process: 'process/browser', }), - new webpack.IgnorePlugin({ - resourceRegExp: /^\.$/, - contextRegExp: /jsdom$/, - }) + new webpack.IgnorePlugin({ resourceRegExp: /^\.$/, contextRegExp: /jsdom$/ }), ], - module: { - rules: [ - { - test: /\.js$/, - include: [path.join(__dirname, 'src')], - use: ['babel-loader'] - }, - { - test: /\.ne$/, - use:['raw-loader'] - } - ] - }, resolve: { + extensions: ['.ts', '.js'], + // jsdom is only required when DOMParser is unavailable (i.e. Node). + // In the browser UMD bundle the require is unreachable, so replace + // it with an empty module so webpack doesn't pull in ~4 MB of jsdom. + alias: { jsdom: false }, fallback: { - 'assert' : false, + 'assert': false, 'fs': false, 'tls': false, 'net': false, @@ -88,6 +59,19 @@ module.exports = { 'zlib': require.resolve('browserify-zlib'), 'buffer': require.resolve('buffer/'), 'vm': require.resolve('vm-browserify'), - } - } -}; \ No newline at end of file + }, + }, + module: { + rules: [ + { + test: /\.ts$/, + include: [path.join(__dirname, 'src')], + exclude: /\.test\.ts$/, + use: [{ + loader: 'ts-loader', + options: { transpileOnly: true, configFile: path.join(__dirname, 'tsconfig.json') }, + }], + }, + ], + }, +}; diff --git a/packages/markdown-it-cicero/.eslintrc.cjs b/packages/markdown-it-cicero/.eslintrc.cjs new file mode 100644 index 00000000..098308d9 --- /dev/null +++ b/packages/markdown-it-cicero/.eslintrc.cjs @@ -0,0 +1,51 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +module.exports = { + root: true, + env: { + es2022: true, + node: true, + jest: true, + }, + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + ], + parser: '@typescript-eslint/parser', + parserOptions: { + ecmaVersion: 2022, + sourceType: 'module', + }, + plugins: ['@typescript-eslint'], + ignorePatterns: [ + 'node_modules/', + 'lib/', + 'coverage/', + ], + rules: { + 'indent': ['error', 4, { 'SwitchCase': 1 }], + 'quotes': ['error', 'single', { 'avoidEscape': true, 'allowTemplateLiterals': true }], + 'semi': ['error', 'always'], + 'no-console': 'warn', + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-var-requires': 'off', + '@typescript-eslint/no-unused-vars': ['error', { 'args': 'none', 'ignoreRestSiblings': true, 'caughtErrors': 'none' }], + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/no-empty-function': 'off', + '@typescript-eslint/ban-ts-comment': 'off', + '@typescript-eslint/no-this-alias': 'off', + 'no-unused-vars': 'off', + }, +}; diff --git a/packages/markdown-it-cicero/.eslintrc.yml b/packages/markdown-it-cicero/.eslintrc.yml deleted file mode 100644 index ec0c5d88..00000000 --- a/packages/markdown-it-cicero/.eslintrc.yml +++ /dev/null @@ -1,47 +0,0 @@ -env: - es6: true - node: true - mocha: true -extends: 'eslint:recommended' -parserOptions: - ecmaVersion: 12 - sourceType: 'script' -rules: - indent: - - error - - 4 - linebreak-style: - - warn - - unix - quotes: - - error - - single - semi: - - error - - always - no-unused-vars: - - error - - args: none - no-console: warn - curly: error - eqeqeq: error - no-throw-literal: error - strict: error - no-var: error - dot-notation: error - no-tabs: error - no-trailing-spaces: error - # no-use-before-define: error - no-useless-call: error - no-with: error - operator-linebreak: error - require-jsdoc: - - error - - require: - ClassDeclaration: true - MethodDefinition: true - FunctionDeclaration: true - valid-jsdoc: - - error - - requireReturn: false - yoda: error diff --git a/packages/markdown-it-cicero/.gitignore b/packages/markdown-it-cicero/.gitignore index a6cf488d..4bd86293 100644 --- a/packages/markdown-it-cicero/.gitignore +++ b/packages/markdown-it-cicero/.gitignore @@ -15,6 +15,7 @@ pids /out /dist /umd +/lib # Directory for instrumented libs generated by jscoverage/JSCover lib-cov diff --git a/packages/markdown-it-cicero/README.md b/packages/markdown-it-cicero/README.md index fca087f7..f356b889 100644 --- a/packages/markdown-it-cicero/README.md +++ b/packages/markdown-it-cicero/README.md @@ -1,21 +1,42 @@ # Markdown-it Plugin for CiceroMark -This package extends CommonMark for CiceroMark to introduce three new DOM nodes: -1. Clause -2. Variable -3. ComputedVariable +A [`markdown-it`](https://github.com/markdown-it/markdown-it) plugin that adds support for the two CiceroMark-specific markdown constructs: + +| Syntax | What it parses to | +|--------------------------------|--------------------------| +| `{{#clause NAME}}…{{/clause}}` | A block-level `clause` (block_clause_open / block_clause_close tokens) | +| `{{%TS expression%}}` | An inline `formula` | + +The plugin emits markdown-it tokens; see [`@accordproject/markdown-cicero`](../markdown-cicero) for the transformer that turns those tokens into a CiceroMark DOM. ## Installation -```sh +``` npm install @accordproject/markdown-it-cicero ``` ## Usage -In progress... +```ts +import MarkdownIt from 'markdown-it'; +import MarkdownItCicero from '@accordproject/markdown-it-cicero'; + +const md = new MarkdownIt({ html: true }).use(MarkdownItCicero); + +const tokens = md.parse( + '{{#clause sample}}Total is {{%amount * rate%}}{{/clause}}', + {} +); +``` + +In CommonJS: + +```js +const MarkdownIt = require('markdown-it'); +const MarkdownItCicero = require('@accordproject/markdown-it-cicero'); + +const md = new MarkdownIt({ html: true }).use(MarkdownItCicero); +``` ## License Accord Project source code files are made available under the Apache License, Version 2.0 (Apache-2.0), located in the LICENSE file. Accord Project documentation files are made available under the Creative Commons Attribution 4.0 International License (CC-BY-4.0), available at http://creativecommons.org/licenses/by/4.0/. - -© 2017-2019 Clause, Inc. diff --git a/packages/markdown-it-cicero/jest.config.js b/packages/markdown-it-cicero/jest.config.js new file mode 100644 index 00000000..c8c592ed --- /dev/null +++ b/packages/markdown-it-cicero/jest.config.js @@ -0,0 +1,30 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict'; + +/** @type {import('jest').Config} */ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + clearMocks: true, + testMatch: ['/src/**/*.test.ts'], + collectCoverageFrom: ['src/**/*.ts', '!src/**/*.test.ts', '!src/**/*.d.ts'], + coverageDirectory: 'coverage', + coveragePathIgnorePatterns: ['/node_modules/'], + coverageReporters: ['json', 'text', 'lcov', 'html'], + transform: { + '^.+\\.tsx?$': ['ts-jest', { tsconfig: '/tsconfig.test.json' }], + }, +}; diff --git a/packages/markdown-it-cicero/jsdoc.json b/packages/markdown-it-cicero/jsdoc.json deleted file mode 100644 index 66f38e56..00000000 --- a/packages/markdown-it-cicero/jsdoc.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "tags": { - "allowUnknownTags": true, - "dictionaries": ["jsdoc", "closure"] - }, - "source": { - "include": [ - "./src", - "./index.js" - ], - "includePattern": ".+\\.js(doc|x)?$" - }, - "plugins": ["plugins/markdown"], - "templates": { - "logoFile": "", - "cleverLinks": false, - "monospaceLinks": false, - "dateFormat": "ddd MMM Do YYYY", - "outputSourceFiles": true, - "outputSourcePath": true, - "systemName": "Accord Project Cicero SDK", - "footer": "", - "copyright": "Released under the Apache License v2.0", - "navType": "vertical", - "theme": "spacelab", - "linenums": true, - "collapseSymbols": false, - "inverseNav": true, - "protocol": "html://", - "methodHeadingReturns": false - }, - "markdown": { - "parser": "gfm", - "hardwrap": true - } -} \ No newline at end of file diff --git a/packages/markdown-it-cicero/lib/cicero_block.js b/packages/markdown-it-cicero/lib/cicero_block.js deleted file mode 100644 index c9bf5e7c..00000000 --- a/packages/markdown-it-cicero/lib/cicero_block.js +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -const matchOpenBlock = require('./cicero_re').matchOpenBlock; -const matchCloseBlock = require('./cicero_re').matchCloseBlock; - -function cicero_block(state, startLine, endLine, silent) { - let block_name, - block_open, - match, - attrs; - - let pos, nextLine, markup, token, - old_parent, old_line_max, - auto_closed = false, - start = state.bMarks[startLine] + state.tShift[startLine], - max = state.eMarks[startLine], - stack = []; - - // Check out the first three characters quickly, - // this should filter out most of non-containers - // - if (0x7B/* { */ !== state.src.charCodeAt(start)) { return false; } - if (0x7B/* { */ !== state.src.charCodeAt(start+1)) { return false; } - if (0x23/* # */ !== state.src.charCodeAt(start+2)) { return false; } - - match = matchOpenBlock(state.src.slice(start),stack); - if (!match) { return false; } - - // make sure tail has spaces only - pos = start + match.matched[0].length; - pos = state.skipSpaces(pos); - - if (pos < max) { return false; } - - block_open = match.tag; - attrs = match.attrs; - - block_name = block_open; - markup = ''; - - // Since start is found, we can report success here in validation mode - // - if (silent) { return true; } - - // Search for the end of the block - // - nextLine = startLine; - - for (;;) { - nextLine++; - if (nextLine >= endLine) { - // unclosed block should be autoclosed by end of document. - // also block seems to be autoclosed by end of parent - break; - } - - start = state.bMarks[nextLine] + state.tShift[nextLine]; - max = state.eMarks[nextLine]; - - if (start < max && state.sCount[nextLine] < state.blkIndent) { - // non-empty line with negative indent should stop the block: - // - ``` - // test - break; - } - - // Check out the first three character quickly, - // this should filter out most of non-containers - // - if (0x7B/* { */ !== state.src.charCodeAt(start)) { continue; } - if (0x7B/* { */ !== state.src.charCodeAt(start+1)) { continue; } - if (0x2F/* / */ !== state.src.charCodeAt(start+2) && 0x23/* # */ !== state.src.charCodeAt(start+2)) { continue; } - - // Handles nested blocks - if (0x23/* # */ === state.src.charCodeAt(start+2)) { - match = matchOpenBlock(state.src.slice(start),stack); - continue; - } - - if (state.sCount[nextLine] - state.blkIndent >= 4) { - // closing fence should be indented less than 4 spaces - continue; - } - - match = matchCloseBlock(state.src.slice(start),block_open,stack); - if (!match) { continue; } - - // make sure tail has spaces only - pos = start + match.matched[0].length; - pos = state.skipSpaces(pos); - - if (pos < max) { continue; } - - // found! - auto_closed = true; - break; - } - - old_parent = state.parentType; - old_line_max = state.lineMax; - state.parentType = 'block'; - - // this will prevent lazy continuations from ever going past our end marker - state.lineMax = nextLine; - - token = state.push('block_' + block_name + '_open', 'div', 1); - token.markup = markup; - token.block = true; - token.info = ''; - token.map = [ startLine, nextLine ]; - - token.attrs = attrs; - - state.md.block.tokenize(state, startLine + 1, nextLine); - - token = state.push('block_' + block_name + '_close', 'div', -1); - token.markup = state.src.slice(start, pos); - token.block = true; - - state.parentType = old_parent; - state.lineMax = old_line_max; - state.line = nextLine + (auto_closed ? 1 : 0); - - return true; -} - -module.exports = cicero_block; diff --git a/packages/markdown-it-cicero/lib/cicero_re.js b/packages/markdown-it-cicero/lib/cicero_re.js deleted file mode 100644 index 71b93599..00000000 --- a/packages/markdown-it-cicero/lib/cicero_re.js +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// Regexps to match cicero elements - -'use strict'; - -const names = require('./names.json'); - -const string = '"([^"]*)"'; -const identifier = '([a-zA-Z_][a-zA-Z0-9_]*)'; -const name = '(?:\\s+([A-Za-z0-9_\-]+))'; -var attribute = '(?:\\s+' + identifier + '(?:\\s*=\\s*' + string + ')?)'; - -const format = '(:?\\s+as\\s*'+ string + '\\s*)?'; -const variable = '{{\\s*' + identifier + format + '\\s*}}'; - -const open_block = '{{#\\s*' + identifier + name + attribute + '*\\s*}}'; -const close_block = '{{/\\s*' + identifier + '\\s*}}'; -const formula = '{{%([^%]*)%}}'; - -const VARIABLE_RE = new RegExp('^(?:' + variable + ')'); -const OPEN_BLOCK_RE = new RegExp('^(?:' + open_block + ')'); -const CLOSE_BLOCK_RE = new RegExp('^(?:' + close_block + ')'); -const FORMULA_RE = new RegExp('^(?:' + formula + ')'); - -/** - * Extract attributes from opening blocks - * @param {string[]} match - * @return {*[]} attributes - */ -function getBlockAttributes(match) { - const result = []; - // name is always present in the block - result.push([ 'name', match[2] ]) - // those are block attributes - for(let i = 3; i < match.length; i = i+2) { - if (match[i]) { - result.push([ match[i], match[i+1] ]); - } - } - return result; -} - -/** - * Match opening blocks - * @param {string} text - the text - * @param {Array} stack - the block stack - * @return {*} open tag - */ -function matchOpenBlock(text,stack) { - var match = text.match(OPEN_BLOCK_RE); - if (!match) { return null; } - var block_open = match[1]; - if (!names.blocks.includes(block_open)) { return null; } - stack.unshift(block_open); - return { tag: block_open, attrs: getBlockAttributes(match), matched: match }; -} -/** - * Match closing blocks - * @param {string} text - the text - * @param {string} block_open - the opening block name - * @param {Array} stack - the block stack - * @return {*} close tag - */ -function matchCloseBlock(text,block_open,stack) { - var match = text.match(CLOSE_BLOCK_RE); - if (!match) { - return null; - } - var block_close = match[1]; - // Handle proper nesting - if (stack[0] === block_close) { - stack.shift() - } - // Handle stack depleted - if (stack.length > 0) { - return null; - } else { - return { tag: block_close, matched: match }; - } -} - -module.exports.VARIABLE_RE = VARIABLE_RE; -module.exports.OPEN_BLOCK_RE = OPEN_BLOCK_RE; -module.exports.CLOSE_BLOCK_RE = CLOSE_BLOCK_RE; -module.exports.FORMULA_RE = FORMULA_RE; -module.exports.matchOpenBlock = matchOpenBlock; -module.exports.matchCloseBlock = matchCloseBlock; -module.exports.getBlockAttributes = getBlockAttributes; diff --git a/packages/markdown-it-cicero/package.json b/packages/markdown-it-cicero/package.json index 77da336e..2c6a216a 100644 --- a/packages/markdown-it-cicero/package.json +++ b/packages/markdown-it-cicero/package.json @@ -1,6 +1,6 @@ { "name": "@accordproject/markdown-it-cicero", - "version": "0.16.25", + "version": "1.0.0", "description": "Plugin to introduce cicero blocks and inlines for the markdown-it markdown parser", "engines": { "node": ">=22", @@ -10,24 +10,22 @@ "access": "public" }, "files": [ - "bin", - "lib", - "types", - "umd" + "lib" ], - "main": "index.js", + "main": "lib/index.js", + "types": "lib/index.d.ts", + "typings": "lib/index.d.ts", "scripts": { - "pretest": "npm run lint", - "lint": "eslint .", + "pretest": "npm run lint && npm run build", + "lint": "eslint . --ext .ts", "postlint": "npm run licchk", "licchk": "license-check-and-add", - "test": "mocha", - "test:cov": "npm run lint && nyc mocha", - "jsdoc": "jsdoc -c jsdoc.json package.json", - "build": "npm run build:types", - "build:types": "tsc" + "test": "jest --silent", + "test:noisy": "jest", + "test:cov": "npm run lint && npm run build && jest --coverage --silent", + "build": "tsc -p tsconfig.json", + "clean": "rimraf lib" }, - "typings": "types/index.d.ts", "repository": { "type": "git", "url": "git+https://github.com/accordproject/markdown-transform.git", @@ -47,29 +45,27 @@ }, "homepage": "https://github.com/accordproject/markdown-transform", "devDependencies": { - "chai": "4.3.6", - "chai-as-promised": "7.1.1", - "chai-string": "^1.5.0", - "chai-things": "0.2.0", + "@types/jest": "^29.5.12", + "@types/markdown-it": "^14.1.2", + "@types/node": "^20.11.30", + "@typescript-eslint/eslint-plugin": "^7.4.0", + "@typescript-eslint/parser": "^7.4.0", "eslint": "8.57.1", - "jsdoc": "^4.0.4", + "jest": "^29.7.0", "license-check-and-add": "2.3.6", - "mocha": "10.8.2", - "nyc": "17.1.0", + "rimraf": "^5.0.5", + "ts-jest": "^29.1.2", "typescript": "^5.9.3" }, "dependencies": { "markdown-it": "^14.1.0" }, "license-check-and-add-config": { - "folder": "./lib", + "folder": "./src", "license": "header.txt", "exact_paths_method": "EXCLUDE", "exact_paths": [ - "externalModels/.npmignore", - "externalModels/.gitignore", "coverage", - "index.d.ts", "./system", "LICENSE", "node_modules", @@ -83,11 +79,12 @@ ".yaml", ".zip", ".tgz", - ".snap" + ".snap", + ".json" ], "insert_license": false, "license_formats": { - "js|njk|pegjs|cto|acl|qry": { + "ts|tsx|js|njk|pegjs|cto|acl|qry": { "prepend": "/*", "append": " */", "eachLine": { @@ -103,28 +100,5 @@ "file": "header.md" } } - }, - "nyc": { - "produce-source-map": "true", - "sourceMap": "inline", - "reporter": [ - "lcov", - "text", - "text-summary", - "html", - "json" - ], - "include": [ - "lib/**/*.js" - ], - "exclude": [ - "scripts/**/*.js" - ], - "all": true, - "check-coverage": true, - "statements": 87, - "branches": 76, - "functions": 84, - "lines": 87 } } diff --git a/packages/markdown-it-cicero/src/cicero_block.ts b/packages/markdown-it-cicero/src/cicero_block.ts new file mode 100644 index 00000000..ef7f9849 --- /dev/null +++ b/packages/markdown-it-cicero/src/cicero_block.ts @@ -0,0 +1,113 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { matchOpenBlock, matchCloseBlock } from './cicero_re'; + +export function cicero_block(state: any, startLine: number, endLine: number, silent: boolean): boolean { + let match; + let pos: number; + let nextLine: number; + let token: any; + let auto_closed = false; + let start = state.bMarks[startLine] + state.tShift[startLine]; + let max = state.eMarks[startLine]; + const stack: string[] = []; + + if (0x7B !== state.src.charCodeAt(start)) { return false; } + if (0x7B !== state.src.charCodeAt(start + 1)) { return false; } + if (0x23 !== state.src.charCodeAt(start + 2)) { return false; } + + match = matchOpenBlock(state.src.slice(start), stack); + if (!match) { return false; } + + pos = start + match.matched[0].length; + pos = state.skipSpaces(pos); + + if (pos < max) { return false; } + + const block_open = match.tag; + const attrs = match.attrs; + const block_name = block_open; + const markup = ''; + + if (silent) { return true; } + + nextLine = startLine; + + for (; ;) { + nextLine++; + if (nextLine >= endLine) { + break; + } + + start = state.bMarks[nextLine] + state.tShift[nextLine]; + max = state.eMarks[nextLine]; + + if (start < max && state.sCount[nextLine] < state.blkIndent) { + break; + } + + if (0x7B !== state.src.charCodeAt(start)) { continue; } + if (0x7B !== state.src.charCodeAt(start + 1)) { continue; } + if (0x2F !== state.src.charCodeAt(start + 2) && 0x23 !== state.src.charCodeAt(start + 2)) { continue; } + + if (0x23 === state.src.charCodeAt(start + 2)) { + match = matchOpenBlock(state.src.slice(start), stack); + continue; + } + + if (state.sCount[nextLine] - state.blkIndent >= 4) { + continue; + } + + match = matchCloseBlock(state.src.slice(start), block_open, stack); + if (!match) { continue; } + + pos = start + match.matched[0].length; + pos = state.skipSpaces(pos); + + if (pos < max) { continue; } + + auto_closed = true; + break; + } + + const old_parent = state.parentType; + const old_line_max = state.lineMax; + state.parentType = 'block'; + + state.lineMax = nextLine; + + token = state.push('block_' + block_name + '_open', 'div', 1); + token.markup = markup; + token.block = true; + token.info = ''; + token.map = [startLine, nextLine]; + + token.attrs = attrs; + + state.md.block.tokenize(state, startLine + 1, nextLine); + + token = state.push('block_' + block_name + '_close', 'div', -1); + token.markup = state.src.slice(start, pos); + token.block = true; + + state.parentType = old_parent; + state.lineMax = old_line_max; + state.line = nextLine + (auto_closed ? 1 : 0); + + return true; +} + +export default cicero_block; diff --git a/packages/markdown-it-cicero/lib/cicero_block_render.js b/packages/markdown-it-cicero/src/cicero_block_render.ts similarity index 77% rename from packages/markdown-it-cicero/lib/cicero_block_render.js rename to packages/markdown-it-cicero/src/cicero_block_render.ts index d71fb73f..521cdba6 100644 --- a/packages/markdown-it-cicero/lib/cicero_block_render.js +++ b/packages/markdown-it-cicero/src/cicero_block_render.ts @@ -12,18 +12,13 @@ * limitations under the License. */ -'use strict'; - -function cicero_block_render(name) { - return function renderDefault(tokens, idx, _options, env, slf) { - - // add a class to the opening tag +export function cicero_block_render(name: string) { + return function renderDefault(tokens: any[], idx: number, _options: any, env: any, slf: any): string { if (tokens[idx].nesting === 1) { tokens[idx].attrJoin('class', name + '_block'); } - return slf.renderToken(tokens, idx, _options, env, slf); - } + }; } -module.exports = cicero_block_render; +export default cicero_block_render; diff --git a/packages/markdown-it-cicero/lib/cicero_inline.js b/packages/markdown-it-cicero/src/cicero_inline.ts similarity index 58% rename from packages/markdown-it-cicero/lib/cicero_inline.js rename to packages/markdown-it-cicero/src/cicero_inline.ts index d895777a..f4ddaa64 100644 --- a/packages/markdown-it-cicero/lib/cicero_inline.js +++ b/packages/markdown-it-cicero/src/cicero_inline.ts @@ -12,42 +12,36 @@ * limitations under the License. */ -'use strict'; - // Regexps to match cicero elements -const VARIABLE_RE = require('./cicero_re').VARIABLE_RE; -const OPEN_BLOCK_RE = require('./cicero_re').OPEN_BLOCK_RE; -const CLOSE_BLOCK_RE = require('./cicero_re').CLOSE_BLOCK_RE; -const FORMULA_RE = require('./cicero_re').FORMULA_RE; +import { FORMULA_RE } from './cicero_re'; -function cicero_inline(state, silent) { - let ch, match, max, token, - pos = state.pos; +export function cicero_inline(state: any, silent: boolean): boolean { + let ch: number; + let match; + let token: any; + const pos = state.pos; + const max = state.posMax; - // Check start - max = state.posMax; - if (state.src.charCodeAt(pos) !== 0x7B/* { */ || + if (state.src.charCodeAt(pos) !== 0x7B || pos + 2 >= max) { return false; } - // Quick fail on second char ch = state.src.charCodeAt(pos + 1); - if (ch !== 0x7B/* { */) { + if (ch !== 0x7B) { return false; } - // Quick dispatch on third char ch = state.src.charCodeAt(pos + 2); - if (ch === 0x25/* % */) { + if (ch === 0x25) { match = state.src.slice(pos).match(FORMULA_RE); if (!match) { return false; } if (!silent) { - token = state.push('formula', 'formula', 0); + token = state.push('formula', 'formula', 0); token.content = match[1]; - token.attrs = [ [ 'name', 'formula' ] ]; + token.attrs = [['name', 'formula']]; } state.pos += match[0].length; @@ -57,4 +51,4 @@ function cicero_inline(state, silent) { } } -module.exports = cicero_inline; +export default cicero_inline; diff --git a/packages/markdown-it-cicero/src/cicero_re.ts b/packages/markdown-it-cicero/src/cicero_re.ts new file mode 100644 index 00000000..b99b9a81 --- /dev/null +++ b/packages/markdown-it-cicero/src/cicero_re.ts @@ -0,0 +1,79 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Regexps to match cicero elements + +import names from './names.json'; + +const string = '"([^"]*)"'; +const identifier = '([a-zA-Z_][a-zA-Z0-9_]*)'; +const name = '(?:\\s+([A-Za-z0-9_\\-]+))'; +const attribute = '(?:\\s+' + identifier + '(?:\\s*=\\s*' + string + ')?)'; + +const format = '(:?\\s+as\\s*' + string + '\\s*)?'; +const variable = '{{\\s*' + identifier + format + '\\s*}}'; + +const open_block = '{{#\\s*' + identifier + name + attribute + '*\\s*}}'; +const close_block = '{{/\\s*' + identifier + '\\s*}}'; +const formula = '{{%([^%]*)%}}'; + +export const VARIABLE_RE = new RegExp('^(?:' + variable + ')'); +export const OPEN_BLOCK_RE = new RegExp('^(?:' + open_block + ')'); +export const CLOSE_BLOCK_RE = new RegExp('^(?:' + close_block + ')'); +export const FORMULA_RE = new RegExp('^(?:' + formula + ')'); + +/** + * Extract attributes from opening blocks + */ +export function getBlockAttributes(match: RegExpMatchArray): [string, string][] { + const result: [string, string][] = []; + result.push(['name', match[2]]); + for (let i = 3; i < match.length; i = i + 2) { + if (match[i]) { + result.push([match[i], match[i + 1]]); + } + } + return result; +} + +/** + * Match opening blocks + */ +export function matchOpenBlock(text: string, stack: string[]): { tag: string; attrs: [string, string][]; matched: RegExpMatchArray } | null { + const match = text.match(OPEN_BLOCK_RE); + if (!match) { return null; } + const block_open = match[1]; + if (!names.blocks.includes(block_open)) { return null; } + stack.unshift(block_open); + return { tag: block_open, attrs: getBlockAttributes(match), matched: match }; +} + +/** + * Match closing blocks + */ +export function matchCloseBlock(text: string, _block_open: string, stack: string[]): { tag: string; matched: RegExpMatchArray } | null { + const match = text.match(CLOSE_BLOCK_RE); + if (!match) { + return null; + } + const block_close = match[1]; + if (stack[0] === block_close) { + stack.shift(); + } + if (stack.length > 0) { + return null; + } else { + return { tag: block_close, matched: match }; + } +} diff --git a/packages/markdown-it-cicero/src/index.test.ts b/packages/markdown-it-cicero/src/index.test.ts new file mode 100644 index 00000000..735dece8 --- /dev/null +++ b/packages/markdown-it-cicero/src/index.test.ts @@ -0,0 +1,53 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as fs from 'fs'; +import * as path from 'path'; +import MarkdownIt from 'markdown-it'; +import MarkdownItCicero = require('./index'); + +const mdit = new MarkdownIt({ html: true }).use(MarkdownItCicero); + +const tests = [ + 'clause1', 'clause2', 'clause3', 'clause4', 'clause5', 'clause6', + 'nestedclause1', 'nestedclause2', + 'notclause1', 'notclause2', 'notclause3', 'notclause4', + 'autoclose1', 'autoclose2', 'autoclose3', + 'all', 'none', +]; + +const dataDir = path.join(__dirname, '..', 'test', 'data'); + +describe('#markdown-it-cicero', () => { + for (const name of tests) { + const markdown = fs.readFileSync(path.join(dataDir, name + '.tem.md'), 'utf8'); + const json = JSON.parse(fs.readFileSync(path.join(dataDir, name + '.json'), 'utf8')); + const html = fs.readFileSync(path.join(dataDir, name + '.html'), 'utf8'); + + describe(`#parse (${name})`, () => { + it('should parse to a token stream', () => { + const tokens = mdit.parse(markdown, {}); + const result = JSON.parse(JSON.stringify(tokens)); + expect(result).toEqual(json); + }); + }); + + describe(`#render (${name})`, () => { + it('should render to HTML', () => { + const result = mdit.render(markdown, {}); + expect(result).toEqual(html.replace(/\r/gm, '')); + }); + }); + } +}); diff --git a/packages/markdown-it-cicero/lib/index.js b/packages/markdown-it-cicero/src/index.ts similarity index 66% rename from packages/markdown-it-cicero/lib/index.js rename to packages/markdown-it-cicero/src/index.ts index 8b2bc6e3..8e53bec3 100644 --- a/packages/markdown-it-cicero/lib/index.js +++ b/packages/markdown-it-cicero/src/index.ts @@ -12,27 +12,25 @@ * limitations under the License. */ -'use strict'; +import { cicero_inline } from './cicero_inline'; +import { cicero_block } from './cicero_block'; +import { cicero_block_render } from './cicero_block_render'; -const cicero_inline = require('./cicero_inline'); -const cicero_block = require('./cicero_block'); -const cicero_block_render = require('./cicero_block_render'); - -const formula_inline = function (tokens, idx /*, options, env */) { - const token = tokens[idx]; - return `${token.content}`; +const formula_inline = function (tokens: any[], idx: number): string { + const token = tokens[idx]; + return `${token.content}`; }; -function cicero_plugin(md) { +function cicero_plugin(md: any): void { md.renderer.rules['formula'] = formula_inline; md.inline.ruler.before('emphasis', 'cicero', cicero_inline); md.block.ruler.before('fence', 'cicero_block', cicero_block, { - alt: [ 'paragraph', 'reference', 'blockquote', 'list' ] + alt: ['paragraph', 'reference', 'blockquote', 'list'], }); md.renderer.rules['block_clause_open'] = cicero_block_render('clause'); md.renderer.rules['block_clause_close'] = cicero_block_render('clause'); } -module.exports = cicero_plugin; +export = cicero_plugin; diff --git a/packages/markdown-it-cicero/lib/names.json b/packages/markdown-it-cicero/src/names.json similarity index 59% rename from packages/markdown-it-cicero/lib/names.json rename to packages/markdown-it-cicero/src/names.json index e13f4d2d..7da461d0 100644 --- a/packages/markdown-it-cicero/lib/names.json +++ b/packages/markdown-it-cicero/src/names.json @@ -1,2 +1,2 @@ { "blocks": [ "clause" ], - "inlines": [] } \ No newline at end of file + "inlines": [] } diff --git a/packages/markdown-it-cicero/tsconfig.json b/packages/markdown-it-cicero/tsconfig.json index d2e666ea..9d6252e2 100644 --- a/packages/markdown-it-cicero/tsconfig.json +++ b/packages/markdown-it-cicero/tsconfig.json @@ -1,10 +1,11 @@ { + "extends": "../../tsconfig.base.json", "compilerOptions": { - "allowJs": true, + "rootDir": "src", + "outDir": "lib", "declaration": true, - "emitDeclarationOnly": true, - "outDir": "types", - "strict": false + "sourceMap": true }, - "include": ["index.js", "lib/**/*.js"] + "include": ["src/**/*.ts", "src/**/*.json"], + "exclude": ["src/**/*.test.ts", "lib", "node_modules"] } diff --git a/packages/markdown-it-cicero/tsconfig.test.json b/packages/markdown-it-cicero/tsconfig.test.json new file mode 100644 index 00000000..2e8ed18d --- /dev/null +++ b/packages/markdown-it-cicero/tsconfig.test.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "rootDir": ".", + "noEmit": true + }, + "include": ["src/**/*.ts", "src/**/*.json"], + "exclude": ["lib", "node_modules"] +} diff --git a/packages/markdown-it-cicero/types/index.d.ts b/packages/markdown-it-cicero/types/index.d.ts deleted file mode 100644 index 02bbffd9..00000000 --- a/packages/markdown-it-cicero/types/index.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const _exports: typeof import("./lib"); -export = _exports; diff --git a/packages/markdown-it-cicero/types/lib/cicero_block.d.ts b/packages/markdown-it-cicero/types/lib/cicero_block.d.ts deleted file mode 100644 index b6453b44..00000000 --- a/packages/markdown-it-cicero/types/lib/cicero_block.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export = cicero_block; -declare function cicero_block(state: any, startLine: any, endLine: any, silent: any): boolean; diff --git a/packages/markdown-it-cicero/types/lib/cicero_block_render.d.ts b/packages/markdown-it-cicero/types/lib/cicero_block_render.d.ts deleted file mode 100644 index f8353da0..00000000 --- a/packages/markdown-it-cicero/types/lib/cicero_block_render.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export = cicero_block_render; -declare function cicero_block_render(name: any): (tokens: any, idx: any, _options: any, env: any, slf: any) => any; diff --git a/packages/markdown-it-cicero/types/lib/cicero_inline.d.ts b/packages/markdown-it-cicero/types/lib/cicero_inline.d.ts deleted file mode 100644 index dcc1bd77..00000000 --- a/packages/markdown-it-cicero/types/lib/cicero_inline.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export = cicero_inline; -declare function cicero_inline(state: any, silent: any): boolean; diff --git a/packages/markdown-it-cicero/types/lib/cicero_re.d.ts b/packages/markdown-it-cicero/types/lib/cicero_re.d.ts deleted file mode 100644 index 45bc224b..00000000 --- a/packages/markdown-it-cicero/types/lib/cicero_re.d.ts +++ /dev/null @@ -1,25 +0,0 @@ -export const VARIABLE_RE: RegExp; -export const OPEN_BLOCK_RE: RegExp; -export const CLOSE_BLOCK_RE: RegExp; -export const FORMULA_RE: RegExp; -/** - * Match opening blocks - * @param {string} text - the text - * @param {Array} stack - the block stack - * @return {*} open tag - */ -export function matchOpenBlock(text: string, stack: Array): any; -/** - * Match closing blocks - * @param {string} text - the text - * @param {string} block_open - the opening block name - * @param {Array} stack - the block stack - * @return {*} close tag - */ -export function matchCloseBlock(text: string, block_open: string, stack: Array): any; -/** - * Extract attributes from opening blocks - * @param {string[]} match - * @return {*[]} attributes - */ -export function getBlockAttributes(match: string[]): any[]; diff --git a/packages/markdown-it-cicero/types/lib/index.d.ts b/packages/markdown-it-cicero/types/lib/index.d.ts deleted file mode 100644 index fb209288..00000000 --- a/packages/markdown-it-cicero/types/lib/index.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export = cicero_plugin; -declare function cicero_plugin(md: any): void; diff --git a/packages/markdown-it-template/.eslintrc.cjs b/packages/markdown-it-template/.eslintrc.cjs new file mode 100644 index 00000000..098308d9 --- /dev/null +++ b/packages/markdown-it-template/.eslintrc.cjs @@ -0,0 +1,51 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +module.exports = { + root: true, + env: { + es2022: true, + node: true, + jest: true, + }, + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + ], + parser: '@typescript-eslint/parser', + parserOptions: { + ecmaVersion: 2022, + sourceType: 'module', + }, + plugins: ['@typescript-eslint'], + ignorePatterns: [ + 'node_modules/', + 'lib/', + 'coverage/', + ], + rules: { + 'indent': ['error', 4, { 'SwitchCase': 1 }], + 'quotes': ['error', 'single', { 'avoidEscape': true, 'allowTemplateLiterals': true }], + 'semi': ['error', 'always'], + 'no-console': 'warn', + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-var-requires': 'off', + '@typescript-eslint/no-unused-vars': ['error', { 'args': 'none', 'ignoreRestSiblings': true, 'caughtErrors': 'none' }], + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/no-empty-function': 'off', + '@typescript-eslint/ban-ts-comment': 'off', + '@typescript-eslint/no-this-alias': 'off', + 'no-unused-vars': 'off', + }, +}; diff --git a/packages/markdown-it-template/.eslintrc.yml b/packages/markdown-it-template/.eslintrc.yml deleted file mode 100644 index ec0c5d88..00000000 --- a/packages/markdown-it-template/.eslintrc.yml +++ /dev/null @@ -1,47 +0,0 @@ -env: - es6: true - node: true - mocha: true -extends: 'eslint:recommended' -parserOptions: - ecmaVersion: 12 - sourceType: 'script' -rules: - indent: - - error - - 4 - linebreak-style: - - warn - - unix - quotes: - - error - - single - semi: - - error - - always - no-unused-vars: - - error - - args: none - no-console: warn - curly: error - eqeqeq: error - no-throw-literal: error - strict: error - no-var: error - dot-notation: error - no-tabs: error - no-trailing-spaces: error - # no-use-before-define: error - no-useless-call: error - no-with: error - operator-linebreak: error - require-jsdoc: - - error - - require: - ClassDeclaration: true - MethodDefinition: true - FunctionDeclaration: true - valid-jsdoc: - - error - - requireReturn: false - yoda: error diff --git a/packages/markdown-it-template/.gitignore b/packages/markdown-it-template/.gitignore index a6cf488d..4bd86293 100644 --- a/packages/markdown-it-template/.gitignore +++ b/packages/markdown-it-template/.gitignore @@ -15,6 +15,7 @@ pids /out /dist /umd +/lib # Directory for instrumented libs generated by jscoverage/JSCover lib-cov diff --git a/packages/markdown-it-template/README.md b/packages/markdown-it-template/README.md index 8175869e..bce87bfe 100644 --- a/packages/markdown-it-template/README.md +++ b/packages/markdown-it-template/README.md @@ -1,21 +1,59 @@ # Markdown-it Plugin for TemplateMark -This package extends CommonMark for TemplateMark to introduce three new DOM nodes: -1. Clause -2. Variable -3. ComputedVariable +A [`markdown-it`](https://github.com/markdown-it/markdown-it) plugin that adds support for TemplateMark syntax — block-level template definitions (clauses, ordered/unordered list templates) and inline template constructs (variables, conditionals, optionals, with, join, formulas). + +The plugin emits markdown-it tokens; see [`@accordproject/markdown-template`](../markdown-template) for the higher-level transformer that turns those tokens into a TemplateMark DOM. ## Installation -```sh -npm install @accordproject/markdown-it-template --save +``` +npm install @accordproject/markdown-it-template ``` ## Usage -In progress... +```ts +import MarkdownIt from 'markdown-it'; +import MarkdownItTemplate from '@accordproject/markdown-it-template'; + +const md = new MarkdownIt({ html: true }).use(MarkdownItTemplate); + +const tokens = md.parse( + 'Hello {{name}}, {{#if hasGift}}enjoy{{else}}sorry{{/if}}!', + {} +); +``` + +In CommonJS: + +```js +const MarkdownIt = require('markdown-it'); +const MarkdownItTemplate = require('@accordproject/markdown-it-template'); + +const md = new MarkdownIt({ html: true }).use(MarkdownItTemplate); +``` + +## Syntax supported + +Block constructs (each on its own paragraph): + +| Open | Close | TemplateMark node | +|-------------------------------|----------------|---------------------------------| +| `{{#clause NAME}}` | `{{/clause}}` | `ClauseDefinition` | +| `{{#ulist NAME}}` | `{{/ulist}}` | `ListBlockDefinition` (bullet) | +| `{{#olist NAME}}` | `{{/olist}}` | `ListBlockDefinition` (ordered) | + +Inline constructs: + +| Syntax | TemplateMark node | +|---------------------------------------|--------------------------------| +| `{{name}}` or `{{name as "format"}}` | `VariableDefinition` / `FormattedVariableDefinition` | +| `{{this}}` or `{{this as "format"}}` | `VariableDefinition` for the current scope | +| `{{%TS code%}}` | `FormulaDefinition` | +| `{{#if NAME}}…{{else}}…{{/if}}` | `ConditionalDefinition` | +| `{{#optional NAME}}…{{/optional}}` | `OptionalDefinition` | +| `{{#with NAME}}…{{/with}}` | `WithDefinition` | +| `{{#join NAME ...}}…{{/join}}` | `JoinDefinition` | ## License Accord Project source code files are made available under the Apache License, Version 2.0 (Apache-2.0), located in the LICENSE file. Accord Project documentation files are made available under the Creative Commons Attribution 4.0 International License (CC-BY-4.0), available at http://creativecommons.org/licenses/by/4.0/. - -© 2017-2019 Clause, Inc. diff --git a/packages/markdown-it-template/jest.config.js b/packages/markdown-it-template/jest.config.js new file mode 100644 index 00000000..c8c592ed --- /dev/null +++ b/packages/markdown-it-template/jest.config.js @@ -0,0 +1,30 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict'; + +/** @type {import('jest').Config} */ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + clearMocks: true, + testMatch: ['/src/**/*.test.ts'], + collectCoverageFrom: ['src/**/*.ts', '!src/**/*.test.ts', '!src/**/*.d.ts'], + coverageDirectory: 'coverage', + coveragePathIgnorePatterns: ['/node_modules/'], + coverageReporters: ['json', 'text', 'lcov', 'html'], + transform: { + '^.+\\.tsx?$': ['ts-jest', { tsconfig: '/tsconfig.test.json' }], + }, +}; diff --git a/packages/markdown-it-template/jsdoc.json b/packages/markdown-it-template/jsdoc.json deleted file mode 100644 index 66f38e56..00000000 --- a/packages/markdown-it-template/jsdoc.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "tags": { - "allowUnknownTags": true, - "dictionaries": ["jsdoc", "closure"] - }, - "source": { - "include": [ - "./src", - "./index.js" - ], - "includePattern": ".+\\.js(doc|x)?$" - }, - "plugins": ["plugins/markdown"], - "templates": { - "logoFile": "", - "cleverLinks": false, - "monospaceLinks": false, - "dateFormat": "ddd MMM Do YYYY", - "outputSourceFiles": true, - "outputSourcePath": true, - "systemName": "Accord Project Cicero SDK", - "footer": "", - "copyright": "Released under the Apache License v2.0", - "navType": "vertical", - "theme": "spacelab", - "linenums": true, - "collapseSymbols": false, - "inverseNav": true, - "protocol": "html://", - "methodHeadingReturns": false - }, - "markdown": { - "parser": "gfm", - "hardwrap": true - } -} \ No newline at end of file diff --git a/packages/markdown-it-template/lib/names.json b/packages/markdown-it-template/lib/names.json deleted file mode 100644 index d0e68a0d..00000000 --- a/packages/markdown-it-template/lib/names.json +++ /dev/null @@ -1,2 +0,0 @@ -{ "blocks": [ "clause", "ulist", "olist" ], - "inlines": [ "if", "optional", "with", "join" ] } \ No newline at end of file diff --git a/packages/markdown-it-template/lib/template_block.js b/packages/markdown-it-template/lib/template_block.js deleted file mode 100644 index 7198c36b..00000000 --- a/packages/markdown-it-template/lib/template_block.js +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -const matchOpenBlock = require('./template_re').matchOpenBlock; -const matchCloseBlock = require('./template_re').matchCloseBlock; - -function template_block(state, startLine, endLine, silent) { - let block_name, - block_open, - match, - attrs; - - let pos, nextLine, markup, token, - old_parent, old_line_max, - auto_closed = false, - start = state.bMarks[startLine] + state.tShift[startLine], - max = state.eMarks[startLine], - stack = []; - - // Check out the first three characters quickly, - // this should filter out most of non-containers - // - if (0x7B/* { */ !== state.src.charCodeAt(start)) { return false; } - if (0x7B/* { */ !== state.src.charCodeAt(start+1)) { return false; } - if (0x23/* # */ !== state.src.charCodeAt(start+2)) { return false; } - - match = matchOpenBlock(state.src.slice(start),stack); - if (!match) { return false; } - - // make sure tail has spaces only - pos = start + match.matched[0].length; - pos = state.skipSpaces(pos); - - if (pos < max) { return false; } - - block_open = match.tag; - attrs = match.attrs; - - block_name = block_open; - markup = ''; - - // Since start is found, we can report success here in validation mode - // - if (silent) { return true; } - - // Search for the end of the block - // - nextLine = startLine; - - for (;;) { - nextLine++; - if (nextLine >= endLine) { - // unclosed block should be autoclosed by end of document. - // also block seems to be autoclosed by end of parent - break; - } - - start = state.bMarks[nextLine] + state.tShift[nextLine]; - max = state.eMarks[nextLine]; - - if (start < max && state.sCount[nextLine] < state.blkIndent) { - // non-empty line with negative indent should stop the block: - // - ``` - // test - break; - } - - // Check out the first three character quickly, - // this should filter out most of non-containers - // - if (0x7B/* { */ !== state.src.charCodeAt(start)) { continue; } - if (0x7B/* { */ !== state.src.charCodeAt(start+1)) { continue; } - if (0x2F/* / */ !== state.src.charCodeAt(start+2) && 0x23/* # */ !== state.src.charCodeAt(start+2)) { continue; } - - // Handles nested blocks - if (0x23/* # */ === state.src.charCodeAt(start+2)) { - match = matchOpenBlock(state.src.slice(start),stack); - continue; - } - - if (state.sCount[nextLine] - state.blkIndent >= 4) { - // closing fence should be indented less than 4 spaces - continue; - } - - match = matchCloseBlock(state.src.slice(start),block_open,stack); - if (!match) { continue; } - - // make sure tail has spaces only - pos = start + match.matched[0].length; - pos = state.skipSpaces(pos); - - if (pos < max) { continue; } - - // found! - auto_closed = true; - break; - } - - old_parent = state.parentType; - old_line_max = state.lineMax; - state.parentType = 'block'; - - // this will prevent lazy continuations from ever going past our end marker - state.lineMax = nextLine; - - token = state.push('block_' + block_name + '_open', 'div', 1); - token.markup = markup; - token.block = true; - token.info = ''; - token.map = [ startLine, nextLine ]; - - token.attrs = attrs; - - state.md.block.tokenize(state, startLine + 1, nextLine); - - token = state.push('block_' + block_name + '_close', 'div', -1); - token.markup = state.src.slice(start, pos); - token.block = true; - - state.parentType = old_parent; - state.lineMax = old_line_max; - state.line = nextLine + (auto_closed ? 1 : 0); - - return true; -} - -module.exports = template_block; diff --git a/packages/markdown-it-template/package.json b/packages/markdown-it-template/package.json index 297cffc4..0d016d21 100644 --- a/packages/markdown-it-template/package.json +++ b/packages/markdown-it-template/package.json @@ -1,6 +1,6 @@ { "name": "@accordproject/markdown-it-template", - "version": "0.16.25", + "version": "1.0.0", "description": "Plugin to introduce template blocks and inlines for the markdown-it markdown parser", "engines": { "node": ">=22", @@ -10,24 +10,22 @@ "access": "public" }, "files": [ - "bin", - "lib", - "types", - "umd" + "lib" ], - "main": "index.js", + "main": "lib/index.js", + "types": "lib/index.d.ts", + "typings": "lib/index.d.ts", "scripts": { - "pretest": "npm run lint", - "lint": "eslint .", + "pretest": "npm run lint && npm run build", + "lint": "eslint . --ext .ts", "postlint": "npm run licchk", "licchk": "license-check-and-add", - "test": "mocha", - "test:cov": "npm run lint && nyc mocha", - "jsdoc": "jsdoc -c jsdoc.json package.json", - "build": "npm run build:types", - "build:types": "tsc" + "test": "jest --silent", + "test:noisy": "jest", + "test:cov": "npm run lint && npm run build && jest --coverage --silent", + "build": "tsc -p tsconfig.json", + "clean": "rimraf lib" }, - "typings": "types/index.d.ts", "repository": { "type": "git", "url": "git+https://github.com/accordproject/markdown-transform.git", @@ -47,29 +45,27 @@ }, "homepage": "https://github.com/accordproject/markdown-transform", "devDependencies": { - "chai": "4.3.6", - "chai-as-promised": "7.1.1", - "chai-string": "^1.5.0", - "chai-things": "0.2.0", + "@types/jest": "^29.5.12", + "@types/markdown-it": "^14.1.2", + "@types/node": "^20.11.30", + "@typescript-eslint/eslint-plugin": "^7.4.0", + "@typescript-eslint/parser": "^7.4.0", "eslint": "8.57.1", - "jsdoc": "^4.0.4", + "jest": "^29.7.0", "license-check-and-add": "2.3.6", - "mocha": "10.8.2", - "nyc": "17.1.0", + "rimraf": "^5.0.5", + "ts-jest": "^29.1.2", "typescript": "^5.9.3" }, "dependencies": { "markdown-it": "^14.1.0" }, "license-check-and-add-config": { - "folder": "./lib", + "folder": "./src", "license": "header.txt", "exact_paths_method": "EXCLUDE", "exact_paths": [ - "externalModels/.npmignore", - "externalModels/.gitignore", "coverage", - "index.d.ts", "./system", "LICENSE", "node_modules", @@ -83,11 +79,12 @@ ".yaml", ".zip", ".tgz", - ".snap" + ".snap", + ".json" ], "insert_license": false, "license_formats": { - "js|njk|pegjs|cto|acl|qry": { + "ts|tsx|js|njk|pegjs|cto|acl|qry": { "prepend": "/*", "append": " */", "eachLine": { @@ -103,28 +100,5 @@ "file": "header.md" } } - }, - "nyc": { - "produce-source-map": "true", - "sourceMap": "inline", - "reporter": [ - "lcov", - "text", - "text-summary", - "html", - "json" - ], - "include": [ - "lib/**/*.js" - ], - "exclude": [ - "scripts/**/*.js" - ], - "all": true, - "check-coverage": true, - "statements": 87, - "branches": 76, - "functions": 84, - "lines": 87 } } diff --git a/packages/markdown-it-template/src/index.test.ts b/packages/markdown-it-template/src/index.test.ts new file mode 100644 index 00000000..6081b7ea --- /dev/null +++ b/packages/markdown-it-template/src/index.test.ts @@ -0,0 +1,53 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as fs from 'fs'; +import * as path from 'path'; +import MarkdownIt from 'markdown-it'; +import MarkdownItTemplate = require('./index'); + +const mdit = new MarkdownIt({ html: true }).use(MarkdownItTemplate); + +const tests = [ + 'inline1', + 'clause1', 'clause2', 'clause3', 'clause4', 'clause5', 'clause6', + 'notclause1', 'notclause2', 'notclause3', 'notclause4', 'notclause5', + 'autoclose1', 'autoclose2', 'autoclose3', + 'all', 'none', +]; + +const dataDir = path.join(__dirname, '..', 'test', 'data'); + +describe('#markdown-it-template', () => { + for (const name of tests) { + const markdown = fs.readFileSync(path.join(dataDir, name + '.tem.md'), 'utf8'); + const json = JSON.parse(fs.readFileSync(path.join(dataDir, name + '.json'), 'utf8')); + const html = fs.readFileSync(path.join(dataDir, name + '.html'), 'utf8'); + + describe(`#parse (${name})`, () => { + it('should parse to a token stream', () => { + const tokens = mdit.parse(markdown, {}); + const result = JSON.parse(JSON.stringify(tokens)); + expect(result).toEqual(json); + }); + }); + + describe(`#render (${name})`, () => { + it('should render to HTML', () => { + const result = mdit.render(markdown, {}); + expect(result).toEqual(html.replace(/\r/gm, '')); + }); + }); + } +}); diff --git a/packages/markdown-it-template/lib/index.js b/packages/markdown-it-template/src/index.ts similarity index 63% rename from packages/markdown-it-template/lib/index.js rename to packages/markdown-it-template/src/index.ts index 6802864e..7611906e 100644 --- a/packages/markdown-it-template/lib/index.js +++ b/packages/markdown-it-template/src/index.ts @@ -12,22 +12,15 @@ * limitations under the License. */ -'use strict'; - -const template_inline = require('./template_inline'); -const template_inline_render = require('./template_inline_render'); -const template_block = require('./template_block'); -const template_block_render = require('./template_block_render'); +import { template_inline } from './template_inline'; +import { template_inline_render } from './template_inline_render'; +import { template_block } from './template_block'; +import { template_block_render } from './template_block_render'; /** * Get an attribute value - * - * @param {*} attrs open ordered list attributes - * @param {string} name attribute name - * @param {*} def a default value - * @returns {string} the initial index */ -function getAttr(attrs,name,def) { +function getAttr(attrs: any[], name: string, def: any): string | null { const startAttrs = attrs.filter((x) => x[0] === name); if (startAttrs[0]) { return '' + startAttrs[0][1]; @@ -36,31 +29,31 @@ function getAttr(attrs,name,def) { } } -const variable_inline = function (tokens, idx /*, options, env */) { - const token = tokens[idx]; - const name = getAttr(token.attrs,'name',null); - const format = getAttr(token.attrs,'format',null); - let attrs = `name="${name}"`; - if(format) { - attrs += ` format="${format}"`; - } - return `${name}`; +const variable_inline = function (tokens: any[], idx: number /*, options, env */): string { + const token = tokens[idx]; + const name = getAttr(token.attrs, 'name', null); + const format = getAttr(token.attrs, 'format', null); + let attrs = `name="${name}"`; + if (format) { + attrs += ` format="${format}"`; + } + return `${name}`; }; -const this_inline = function (tokens, idx /*, options, env */) { - return `this`; +const this_inline = function (): string { + return `this`; }; -const else_inline = function (tokens, idx /*, options, env */) { - return ``; +const else_inline = function (): string { + return ``; }; -const formula_inline = function (tokens, idx /*, options, env */) { - const token = tokens[idx]; - return `${token.content}`; +const formula_inline = function (tokens: any[], idx: number): string { + const token = tokens[idx]; + return `${token.content}`; }; -function template_plugin(md) { +function template_plugin(md: any): void { md.inline.ruler.before('emphasis', 'template', template_inline); md.renderer.rules['inline_block_if_open'] = template_inline_render('if'); md.renderer.rules['inline_block_if_close'] = template_inline_render('if'); @@ -76,7 +69,7 @@ function template_plugin(md) { md.renderer.rules['formula'] = formula_inline; md.block.ruler.before('fence', 'template_block', template_block, { - alt: [ 'paragraph', 'reference', 'blockquote', 'list' ] + alt: ['paragraph', 'reference', 'blockquote', 'list'], }); md.renderer.rules['block_clause_open'] = template_block_render('clause'); md.renderer.rules['block_clause_close'] = template_block_render('clause'); @@ -86,4 +79,4 @@ function template_plugin(md) { md.renderer.rules['block_olist_close'] = template_block_render('olist'); } -module.exports = template_plugin; +export = template_plugin; diff --git a/packages/markdown-it-template/src/names.json b/packages/markdown-it-template/src/names.json new file mode 100644 index 00000000..51fda3ff --- /dev/null +++ b/packages/markdown-it-template/src/names.json @@ -0,0 +1,2 @@ +{ "blocks": [ "clause", "ulist", "olist" ], + "inlines": [ "if", "optional", "with", "join" ] } diff --git a/packages/markdown-it-template/src/template_block.ts b/packages/markdown-it-template/src/template_block.ts new file mode 100644 index 00000000..63007a31 --- /dev/null +++ b/packages/markdown-it-template/src/template_block.ts @@ -0,0 +1,113 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { matchOpenBlock, matchCloseBlock } from './template_re'; + +export function template_block(state: any, startLine: number, endLine: number, silent: boolean): boolean { + let match; + let pos: number; + let nextLine: number; + let token: any; + let auto_closed = false; + let start = state.bMarks[startLine] + state.tShift[startLine]; + let max = state.eMarks[startLine]; + const stack: string[] = []; + + if (0x7B !== state.src.charCodeAt(start)) { return false; } + if (0x7B !== state.src.charCodeAt(start + 1)) { return false; } + if (0x23 !== state.src.charCodeAt(start + 2)) { return false; } + + match = matchOpenBlock(state.src.slice(start), stack); + if (!match) { return false; } + + pos = start + match.matched[0].length; + pos = state.skipSpaces(pos); + + if (pos < max) { return false; } + + const block_open = match.tag; + const attrs = match.attrs; + const block_name = block_open; + const markup = ''; + + if (silent) { return true; } + + nextLine = startLine; + + for (; ;) { + nextLine++; + if (nextLine >= endLine) { + break; + } + + start = state.bMarks[nextLine] + state.tShift[nextLine]; + max = state.eMarks[nextLine]; + + if (start < max && state.sCount[nextLine] < state.blkIndent) { + break; + } + + if (0x7B !== state.src.charCodeAt(start)) { continue; } + if (0x7B !== state.src.charCodeAt(start + 1)) { continue; } + if (0x2F !== state.src.charCodeAt(start + 2) && 0x23 !== state.src.charCodeAt(start + 2)) { continue; } + + if (0x23 === state.src.charCodeAt(start + 2)) { + match = matchOpenBlock(state.src.slice(start), stack); + continue; + } + + if (state.sCount[nextLine] - state.blkIndent >= 4) { + continue; + } + + match = matchCloseBlock(state.src.slice(start), block_open, stack); + if (!match) { continue; } + + pos = start + match.matched[0].length; + pos = state.skipSpaces(pos); + + if (pos < max) { continue; } + + auto_closed = true; + break; + } + + const old_parent = state.parentType; + const old_line_max = state.lineMax; + state.parentType = 'block'; + + state.lineMax = nextLine; + + token = state.push('block_' + block_name + '_open', 'div', 1); + token.markup = markup; + token.block = true; + token.info = ''; + token.map = [startLine, nextLine]; + + token.attrs = attrs; + + state.md.block.tokenize(state, startLine + 1, nextLine); + + token = state.push('block_' + block_name + '_close', 'div', -1); + token.markup = state.src.slice(start, pos); + token.block = true; + + state.parentType = old_parent; + state.lineMax = old_line_max; + state.line = nextLine + (auto_closed ? 1 : 0); + + return true; +} + +export default template_block; diff --git a/packages/markdown-it-template/lib/template_block_render.js b/packages/markdown-it-template/src/template_block_render.ts similarity index 77% rename from packages/markdown-it-template/lib/template_block_render.js rename to packages/markdown-it-template/src/template_block_render.ts index 3ccf7283..f83a2df5 100644 --- a/packages/markdown-it-template/lib/template_block_render.js +++ b/packages/markdown-it-template/src/template_block_render.ts @@ -12,18 +12,13 @@ * limitations under the License. */ -'use strict'; - -function template_block_render(name) { - return function renderDefault(tokens, idx, _options, env, slf) { - - // add a class to the opening tag +export function template_block_render(name: string) { + return function renderDefault(tokens: any[], idx: number, _options: any, env: any, slf: any): string { if (tokens[idx].nesting === 1) { tokens[idx].attrJoin('class', name + '_block'); } - return slf.renderToken(tokens, idx, _options, env, slf); - } + }; } -module.exports = template_block_render; +export default template_block_render; diff --git a/packages/markdown-it-template/lib/template_inline.js b/packages/markdown-it-template/src/template_inline.ts similarity index 57% rename from packages/markdown-it-template/lib/template_inline.js rename to packages/markdown-it-template/src/template_inline.ts index f6c4287b..51223861 100644 --- a/packages/markdown-it-template/lib/template_inline.js +++ b/packages/markdown-it-template/src/template_inline.ts @@ -12,43 +12,41 @@ * limitations under the License. */ -'use strict'; - // Regexps to match template elements -const names = require('./names.json'); - -const VARIABLE_RE = require('./template_re').VARIABLE_RE; -const OPEN_BLOCK_RE = require('./template_re').OPEN_BLOCK_RE; -const CLOSE_BLOCK_RE = require('./template_re').CLOSE_BLOCK_RE; -const FORMULA_RE = require('./template_re').FORMULA_RE; -const getBlockAttributes = require('./template_re').getBlockAttributes; +import names from './names.json'; +import { + VARIABLE_RE, + OPEN_BLOCK_RE, + CLOSE_BLOCK_RE, + FORMULA_RE, + getBlockAttributes, +} from './template_re'; -function template_inline(state, silent) { - // Maintain a stack for template block open/close +export function template_inline(state: any, silent: boolean): boolean { if (!state.templates) { state.templates = []; } - let ch, match, max, token, attrs, - pos = state.pos; + let ch: number; + let match; + let token: any; + let attrs: any; + const pos = state.pos; + const max = state.posMax; - // Check start - max = state.posMax; - if (state.src.charCodeAt(pos) !== 0x7B/* { */ || + if (state.src.charCodeAt(pos) !== 0x7B || pos + 2 >= max) { return false; } - // Quick fail on second char ch = state.src.charCodeAt(pos + 1); - if (ch !== 0x7B/* { */) { + if (ch !== 0x7B) { return false; } - // Quick dispatch on third char ch = state.src.charCodeAt(pos + 2); - if (ch === 0x23/* # */) { + if (ch === 0x23) { if (silent) { return false; } match = state.src.slice(pos).match(OPEN_BLOCK_RE); @@ -60,17 +58,16 @@ function template_inline(state, silent) { if (!names.inlines.includes(block)) { return false; } - // Push open block to template stack state.templates.push(block); - token = state.push('inline_block_' + block + '_open', 'span', 1); + token = state.push('inline_block_' + block + '_open', 'span', 1); token.content = match[0]; - token.attrs = attrs; + token.attrs = attrs; state.pos += match[0].length; return true; - } else if (ch === 0x2F/* / */) { + } else if (ch === 0x2F) { if (silent) { return false; } match = state.src.slice(pos).match(CLOSE_BLOCK_RE); @@ -80,27 +77,25 @@ function template_inline(state, silent) { if (!names.inlines.includes(block)) { return false; } - // Check if template close block matches open in stack const top = state.templates.pop(); if (top !== block) { state.templates.push(top); return false; } - token = state.push('inline_block_' + block + '_close', 'span', -1); + token = state.push('inline_block_' + block + '_close', 'span', -1); token.content = match[0]; state.pos += match[0].length; - //state.templates.push(block); return true; - } else if (ch === 0x25/* % */) { + } else if (ch === 0x25) { match = state.src.slice(pos).match(FORMULA_RE); if (!match) { return false; } if (!silent) { - token = state.push('formula', 'formula', 0); + token = state.push('formula', 'formula', 0); token.content = match[1]; - token.attrs = [ ]; + token.attrs = []; } state.pos += match[0].length; @@ -112,22 +107,22 @@ function template_inline(state, silent) { const name = match[1]; const format = match[3]; if (!silent) { - if (name === 'else') { // XXX 'else' is reserved in variable names - token = state.push('inline_block_else', 'else', 0); + if (name === 'else') { + token = state.push('inline_block_else', 'else', 0); token.content = content; - } else if (name === 'this') { // XXX 'this' is reserved in variable names - token = state.push('this', 'this', 0); + } else if (name === 'this') { + token = state.push('this', 'this', 0); token.content = content; - token.attrs = [ ]; + token.attrs = []; if (format) { - token.attrs.push([ 'format', format ]); + token.attrs.push(['format', format]); } } else { - token = state.push('variable', 'variable', 0); + token = state.push('variable', 'variable', 0); token.content = content; - token.attrs = [ [ 'name', name ] ]; + token.attrs = [['name', name]]; if (format) { - token.attrs.push([ 'format', format ]); + token.attrs.push(['format', format]); } } } @@ -136,4 +131,4 @@ function template_inline(state, silent) { } } -module.exports = template_inline; +export default template_inline; diff --git a/packages/markdown-it-template/lib/template_inline_render.js b/packages/markdown-it-template/src/template_inline_render.ts similarity index 77% rename from packages/markdown-it-template/lib/template_inline_render.js rename to packages/markdown-it-template/src/template_inline_render.ts index 2a962a75..5bbc8ce5 100644 --- a/packages/markdown-it-template/lib/template_inline_render.js +++ b/packages/markdown-it-template/src/template_inline_render.ts @@ -12,18 +12,13 @@ * limitations under the License. */ -'use strict'; - -function template_inline_render(name) { - return function renderDefault(tokens, idx, _options, env, slf) { - - // add a class to the opening tag +export function template_inline_render(name: string) { + return function renderDefault(tokens: any[], idx: number, _options: any, env: any, slf: any): string { if (tokens[idx].nesting === 1) { tokens[idx].attrJoin('class', name + '_inline'); } - return slf.renderToken(tokens, idx, _options, env, slf); - } + }; } -module.exports = template_inline_render; +export default template_inline_render; diff --git a/packages/markdown-it-template/lib/template_re.js b/packages/markdown-it-template/src/template_re.ts similarity index 56% rename from packages/markdown-it-template/lib/template_re.js rename to packages/markdown-it-template/src/template_re.ts index a06d8a43..b8c8d1b9 100644 --- a/packages/markdown-it-template/lib/template_re.js +++ b/packages/markdown-it-template/src/template_re.ts @@ -14,36 +14,32 @@ // Regexps to match cicero elements -'use strict'; - -const names = require('./names.json'); +import names from './names.json'; const string = '"([^"]*)"'; const identifier = '([a-zA-Z_][a-zA-Z0-9_]+)'; const name = '(?:\\s+([A-Za-z0-9_-]+))'; const attributes = '(.*?)'; -const format = '(:?\\s+as\\s*'+ string + '\\s*)?'; +const format = '(:?\\s+as\\s*' + string + '\\s*)?'; const variable = '{{\\s*' + identifier + format + '\\s*}}'; const open_block = '{{#\\s*' + identifier + name + attributes + '\\s*}}'; const close_block = '{{/\\s*' + identifier + '\\s*}}'; const formula = '{{%([^%]*)%}}'; -const VARIABLE_RE = new RegExp('^(?:' + variable + ')'); -const OPEN_BLOCK_RE = new RegExp('^(?:' + open_block + ')'); -const CLOSE_BLOCK_RE = new RegExp('^(?:' + close_block + ')'); -const FORMULA_RE = new RegExp('^(?:' + formula + ')'); +export const VARIABLE_RE = new RegExp('^(?:' + variable + ')'); +export const OPEN_BLOCK_RE = new RegExp('^(?:' + open_block + ')'); +export const CLOSE_BLOCK_RE = new RegExp('^(?:' + close_block + ')'); +export const FORMULA_RE = new RegExp('^(?:' + formula + ')'); /** * Parses an argument string into an object - * @param {string} input the argument string to parse - * @returns {[string]} an array of strings, key/value */ -function parseArguments(input) { +function parseArguments(input: string): [string, string][] { const regex = /(\w+)\s*=\s*"([^"]+)"/g; let match; - const result = []; + const result: [string, string][] = []; while ((match = regex.exec(input))) { const argName = match[1]; const argValue = match[2]; @@ -54,19 +50,13 @@ function parseArguments(input) { /** * Extract attributes from opening blocks - * @param {string[]} match the block data - * @return {*[]} attributes */ -function getBlockAttributes(match) { - let result = []; - // name is always present in the block - result.push([ 'name', match[2] ]); - - // the fourth match is all the arguments - // e.g. style="long" locale="en" - if(match[3]) { +export function getBlockAttributes(match: RegExpMatchArray): [string, string][] { + let result: [string, string][] = []; + result.push(['name', match[2]]); + if (match[3]) { const args = parseArguments(match[3]); - if(args && args.length > 0) { + if (args && args.length > 0) { result = result.concat(args); } } @@ -75,11 +65,8 @@ function getBlockAttributes(match) { /** * Match opening blocks - * @param {string} text - the text - * @param {Array} stack - the block stack - * @return {*} open tag */ -function matchOpenBlock(text,stack) { +export function matchOpenBlock(text: string, stack: string[]): { tag: string; attrs: [string, string][]; matched: RegExpMatchArray } | null { const match = text.match(OPEN_BLOCK_RE); if (!match) { return null; } const block_open = match[1]; @@ -87,35 +74,22 @@ function matchOpenBlock(text,stack) { stack.unshift(block_open); return { tag: block_open, attrs: getBlockAttributes(match), matched: match }; } + /** * Match closing blocks - * @param {string} text - the text - * @param {string} block_open - the opening block name - * @param {Array} stack - the block stack - * @return {*} close tag */ -function matchCloseBlock(text,block_open,stack) { +export function matchCloseBlock(text: string, _block_open: string, stack: string[]): { tag: string; matched: RegExpMatchArray } | null { const match = text.match(CLOSE_BLOCK_RE); if (!match) { return null; } const block_close = match[1]; - // Handle proper nesting if (stack[0] === block_close) { stack.shift(); } - // Handle stack depleted if (stack.length > 0) { return null; } else { return { tag: block_close, matched: match }; } } - -module.exports.VARIABLE_RE = VARIABLE_RE; -module.exports.OPEN_BLOCK_RE = OPEN_BLOCK_RE; -module.exports.CLOSE_BLOCK_RE = CLOSE_BLOCK_RE; -module.exports.FORMULA_RE = FORMULA_RE; -module.exports.matchOpenBlock = matchOpenBlock; -module.exports.matchCloseBlock = matchCloseBlock; -module.exports.getBlockAttributes = getBlockAttributes; \ No newline at end of file diff --git a/packages/markdown-it-template/tsconfig.json b/packages/markdown-it-template/tsconfig.json index d2e666ea..9d6252e2 100644 --- a/packages/markdown-it-template/tsconfig.json +++ b/packages/markdown-it-template/tsconfig.json @@ -1,10 +1,11 @@ { + "extends": "../../tsconfig.base.json", "compilerOptions": { - "allowJs": true, + "rootDir": "src", + "outDir": "lib", "declaration": true, - "emitDeclarationOnly": true, - "outDir": "types", - "strict": false + "sourceMap": true }, - "include": ["index.js", "lib/**/*.js"] + "include": ["src/**/*.ts", "src/**/*.json"], + "exclude": ["src/**/*.test.ts", "lib", "node_modules"] } diff --git a/packages/markdown-it-template/tsconfig.test.json b/packages/markdown-it-template/tsconfig.test.json new file mode 100644 index 00000000..2e8ed18d --- /dev/null +++ b/packages/markdown-it-template/tsconfig.test.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "rootDir": ".", + "noEmit": true + }, + "include": ["src/**/*.ts", "src/**/*.json"], + "exclude": ["lib", "node_modules"] +} diff --git a/packages/markdown-it-template/types/index.d.ts b/packages/markdown-it-template/types/index.d.ts deleted file mode 100644 index 02bbffd9..00000000 --- a/packages/markdown-it-template/types/index.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const _exports: typeof import("./lib"); -export = _exports; diff --git a/packages/markdown-it-template/types/lib/index.d.ts b/packages/markdown-it-template/types/lib/index.d.ts deleted file mode 100644 index d1883ed4..00000000 --- a/packages/markdown-it-template/types/lib/index.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export = template_plugin; -declare function template_plugin(md: any): void; diff --git a/packages/markdown-it-template/types/lib/template_block.d.ts b/packages/markdown-it-template/types/lib/template_block.d.ts deleted file mode 100644 index 1430ebd9..00000000 --- a/packages/markdown-it-template/types/lib/template_block.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export = template_block; -declare function template_block(state: any, startLine: any, endLine: any, silent: any): boolean; diff --git a/packages/markdown-it-template/types/lib/template_block_render.d.ts b/packages/markdown-it-template/types/lib/template_block_render.d.ts deleted file mode 100644 index 8f9c0ef2..00000000 --- a/packages/markdown-it-template/types/lib/template_block_render.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export = template_block_render; -declare function template_block_render(name: any): (tokens: any, idx: any, _options: any, env: any, slf: any) => any; diff --git a/packages/markdown-it-template/types/lib/template_inline.d.ts b/packages/markdown-it-template/types/lib/template_inline.d.ts deleted file mode 100644 index de64b387..00000000 --- a/packages/markdown-it-template/types/lib/template_inline.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export = template_inline; -declare function template_inline(state: any, silent: any): boolean; diff --git a/packages/markdown-it-template/types/lib/template_inline_render.d.ts b/packages/markdown-it-template/types/lib/template_inline_render.d.ts deleted file mode 100644 index 2634af48..00000000 --- a/packages/markdown-it-template/types/lib/template_inline_render.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export = template_inline_render; -declare function template_inline_render(name: any): (tokens: any, idx: any, _options: any, env: any, slf: any) => any; diff --git a/packages/markdown-it-template/types/lib/template_re.d.ts b/packages/markdown-it-template/types/lib/template_re.d.ts deleted file mode 100644 index bad332f8..00000000 --- a/packages/markdown-it-template/types/lib/template_re.d.ts +++ /dev/null @@ -1,25 +0,0 @@ -export const VARIABLE_RE: RegExp; -export const OPEN_BLOCK_RE: RegExp; -export const CLOSE_BLOCK_RE: RegExp; -export const FORMULA_RE: RegExp; -/** - * Match opening blocks - * @param {string} text - the text - * @param {Array} stack - the block stack - * @return {*} open tag - */ -export function matchOpenBlock(text: string, stack: Array): any; -/** - * Match closing blocks - * @param {string} text - the text - * @param {string} block_open - the opening block name - * @param {Array} stack - the block stack - * @return {*} close tag - */ -export function matchCloseBlock(text: string, block_open: string, stack: Array): any; -/** - * Extract attributes from opening blocks - * @param {string[]} match the block data - * @return {*[]} attributes - */ -export function getBlockAttributes(match: string[]): any[]; diff --git a/packages/markdown-template/.babelrc b/packages/markdown-template/.babelrc deleted file mode 100644 index ffd7a3c1..00000000 --- a/packages/markdown-template/.babelrc +++ /dev/null @@ -1,21 +0,0 @@ -{ - "presets": [ - [ - "@babel/preset-env", - { - "targets": { - "node": "6.10", - "esmodules": true - } - } - ] - ], - "env": { - "production": { - "plugins": ["@babel/plugin-proposal-object-rest-spread"] - }, - "development": { - "plugins": ["istanbul","@babel/plugin-proposal-object-rest-spread"] - } - } -} \ No newline at end of file diff --git a/packages/markdown-template/.eslintrc.cjs b/packages/markdown-template/.eslintrc.cjs new file mode 100644 index 00000000..464b671d --- /dev/null +++ b/packages/markdown-template/.eslintrc.cjs @@ -0,0 +1,37 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +module.exports = { + root: true, + env: { es2022: true, node: true, jest: true }, + extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'], + parser: '@typescript-eslint/parser', + parserOptions: { ecmaVersion: 2022, sourceType: 'module' }, + plugins: ['@typescript-eslint'], + ignorePatterns: ['node_modules/', 'lib/', 'umd/', 'coverage/'], + rules: { + 'indent': ['error', 4, { 'SwitchCase': 1 }], + 'quotes': ['error', 'single', { 'avoidEscape': true, 'allowTemplateLiterals': true }], + 'semi': ['error', 'always'], + 'no-console': 'warn', + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-var-requires': 'off', + '@typescript-eslint/no-unused-vars': ['error', { 'args': 'none', 'ignoreRestSiblings': true, 'caughtErrors': 'none' }], + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/no-empty-function': 'off', + '@typescript-eslint/ban-ts-comment': 'off', + '@typescript-eslint/no-this-alias': 'off', + 'no-unused-vars': 'off', + }, +}; diff --git a/packages/markdown-template/.eslintrc.yml b/packages/markdown-template/.eslintrc.yml deleted file mode 100644 index ec0c5d88..00000000 --- a/packages/markdown-template/.eslintrc.yml +++ /dev/null @@ -1,47 +0,0 @@ -env: - es6: true - node: true - mocha: true -extends: 'eslint:recommended' -parserOptions: - ecmaVersion: 12 - sourceType: 'script' -rules: - indent: - - error - - 4 - linebreak-style: - - warn - - unix - quotes: - - error - - single - semi: - - error - - always - no-unused-vars: - - error - - args: none - no-console: warn - curly: error - eqeqeq: error - no-throw-literal: error - strict: error - no-var: error - dot-notation: error - no-tabs: error - no-trailing-spaces: error - # no-use-before-define: error - no-useless-call: error - no-with: error - operator-linebreak: error - require-jsdoc: - - error - - require: - ClassDeclaration: true - MethodDefinition: true - FunctionDeclaration: true - valid-jsdoc: - - error - - requireReturn: false - yoda: error diff --git a/packages/markdown-template/README.md b/packages/markdown-template/README.md index 6bdb9357..fac06495 100644 --- a/packages/markdown-template/README.md +++ b/packages/markdown-template/README.md @@ -1,32 +1,66 @@ -# TemplateMark Transform +# TemplateMark Transformer -This package extends CommonMark to introduce Accord Project grammar support with: -1. Clause definitions -2. Variable definitions -3. Formulas +Parses markdown templates (TemplateMark) into a typed DOM that captures clause/contract structure, variable references, conditionals, optionals, list iteration, joins, and embedded TypeScript formulas. -Use `TemplateMarkTransform` to map from CommonMark to TemplateMark DOM nodes. +TemplateMark extends the CommonMark DOM with: + +- `ClauseDefinition`, `ContractDefinition` +- `VariableDefinition`, `FormattedVariableDefinition`, `EnumVariableDefinition` +- `ConditionalDefinition`, `OptionalDefinition` +- `WithDefinition`, `JoinDefinition`, `ListBlockDefinition` +- `FormulaDefinition` + +Schema: [`templatemark@0.5.0`](https://models.accordproject.org/markdown/templatemark@0.5.0.html). ## Installation ``` -npm install @accordproject/markdown-template --save +npm install @accordproject/markdown-template ``` +Peer dependencies: `@accordproject/concerto-core@^4.1.3` and `@accordproject/concerto-cto@^4.1.3`. + ## Usage -``` javascript +A template needs a Concerto model that defines the shape of the data being templated. The model must declare exactly one concept decorated with `@template`. -const TemplateMarkTransformer = require('@accordproject/markdown-template').TemplateMarkTransformer; -const ModelLoader = require('@accordproject/concerto-core').ModelLoader; +```ts +import { TemplateMarkTransformer } from '@accordproject/markdown-template'; +import { ModelManager } from '@accordproject/concerto-core'; -const modelManager = await ModelLoader.loadModelManager(null, parameters.ctoFiles); -const templateMarkTransformer = new TemplateMarkTransformer(); +const model = ` +namespace org.example@1.0.0 +@template +concept Greeting { + o String name +}`; -return templateMarkTransformer.fromMarkdownTemplate({ fileName:parameters.inputFileName, content:input }, modelManager, templateKind, options); +const modelManager = new ModelManager(); +modelManager.addCTOModel(model); + +const transformer = new TemplateMarkTransformer(); +const templateMark = transformer.fromMarkdownTemplate( + { content: 'Hello {{name}}.' }, + modelManager, + 'clause' +); ``` +The returned `templateMark` is a JSON document conforming to the TemplateMark schema. Pass `'contract'` instead of `'clause'` if the template is a contract. + +In CommonJS: + +```js +const { TemplateMarkTransformer } = require('@accordproject/markdown-template'); +``` + +## What this package exports + +- `TemplateMarkTransformer` — markdown_template ↔ TemplateMark DOM +- `templatemarkutil` — lower-level helpers for tokenization and typing +- `datetimeutil`, `util` — date/time and hashing utilities +- `TemplateException` — error type thrown for invalid templates +- `normalizeNLs` — normalize line endings + ## License Accord Project source code files are made available under the Apache License, Version 2.0 (Apache-2.0), located in the LICENSE file. Accord Project documentation files are made available under the Creative Commons Attribution 4.0 International License (CC-BY-4.0), available at http://creativecommons.org/licenses/by/4.0/. - -© 2017-2019 Clause, Inc. diff --git a/packages/markdown-template/index.js b/packages/markdown-template/index.js deleted file mode 100755 index dd4452aa..00000000 --- a/packages/markdown-template/index.js +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -/** - * Export the framework and plugins - * @module markdown-template - */ - -module.exports.util = require('./lib/util'); -module.exports.templatemarkutil = require('./lib/templatemarkutil'); -module.exports.datetimeutil = require('./lib/datetimeutil'); -module.exports.normalizeNLs = require('./lib/normalize').normalizeNLs; -module.exports.TemplateException = require('./lib/templateexception'); -module.exports.TemplateMarkTransformer = require('./lib/TemplateMarkTransformer'); diff --git a/packages/markdown-template/jest.config.js b/packages/markdown-template/jest.config.js index 541ca505..c8c592ed 100644 --- a/packages/markdown-template/jest.config.js +++ b/packages/markdown-template/jest.config.js @@ -13,188 +13,18 @@ */ 'use strict'; -// For a detailed explanation regarding each configuration property, visit: -// https://jestjs.io/docs/en/configuration.html +/** @type {import('jest').Config} */ module.exports = { - // All imported modules in your tests should be mocked automatically - // automock: false, - - // Stop running tests after `n` failures - // bail: 0, - - // Respect "browser" field in package.json when resolving modules - // browser: false, - - // The directory where Jest should store its cached dependency information - // cacheDirectory: "/private/var/folders/tv/4ljndl3s2jg90nxd8h7f3bgr0000gn/T/jest_dx", - - // Automatically clear mock calls and instances between every test + preset: 'ts-jest', + testEnvironment: 'node', clearMocks: true, - - // Indicates whether the coverage information should be collected while executing the test - // collectCoverage: false, - - // An array of glob patterns indicating a set of files for which coverage information should be collected - collectCoverageFrom: [ 'src/**/*.js' ], - - // The directory where Jest should output its coverage files + testMatch: ['/src/**/*.test.ts'], + collectCoverageFrom: ['src/**/*.ts', '!src/**/*.test.ts', '!src/**/*.d.ts'], coverageDirectory: 'coverage', - - // An array of regexp pattern strings used to skip coverage collection - coveragePathIgnorePatterns: [ - '/node_modules/' - ], - - // A list of reporter names that Jest uses when writing coverage reports - coverageReporters: [ - 'json', - 'text', - 'lcov', - 'html' - ], - - // An object that configures minimum threshold enforcement for coverage results - // coverageThreshold: null, - - // A path to a custom dependency extractor - // dependencyExtractor: null, - - // Make calling deprecated APIs throw helpful error messages - // errorOnDeprecated: false, - - // Force coverage collection from ignored files using an array of glob patterns - // forceCoverageMatch: [], - - // A path to a module which exports an async function that is triggered once before all test suites - // globalSetup: null, - - // A path to a module which exports an async function that is triggered once after all test suites - // globalTeardown: null, - - // A set of global variables that need to be available in all test environments - // globals: {}, - - // An array of directory names to be searched recursively up from the requiring module's location - // moduleDirectories: [ - // "node_modules" - // ], - - // An array of file extensions your modules use - // moduleFileExtensions: [ - // "js", - // "json", - // "jsx", - // "ts", - // "tsx", - // "node" - // ], - - // A map from regular expressions to module names that allow to stub out resources with a single module - // moduleNameMapper: {}, - - // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader - // modulePathIgnorePatterns: [], - - // Activates notifications for test results - // notify: false, - - // An enum that specifies notification mode. Requires { notify: true } - // notifyMode: "failure-change", - - // A preset that is used as a base for Jest's configuration - // preset: null, - - // Run tests from one or more projects - // projects: null, - - // Use this configuration option to add custom reporters to Jest - // reporters: undefined, - - // Automatically reset mock state between every test - // resetMocks: false, - - // Reset the module registry before running each individual test - // resetModules: false, - - // A path to a custom resolver - // resolver: null, - - // Automatically restore mock state between every test - // restoreMocks: false, - - // The root directory that Jest should scan for tests and modules within - // rootDir: null, - - // A list of paths to directories that Jest should use to search for files in - // roots: [ - // "" - // ], - - // Allows you to use a custom runner instead of Jest's default test runner - // runner: "jest-runner", - - // The paths to modules that run some code to configure or set up the testing environment before each test - // setupFiles: [], - - // A list of paths to modules that run some code to configure or set up the testing framework before each test - // setupFilesAfterEnv: [], - - // A list of paths to snapshot serializer modules Jest should use for snapshot testing - // snapshotSerializers: [], - - // The test environment that will be used for testing - testEnvironment: 'node', - - // Options that will be passed to the testEnvironment - // testEnvironmentOptions: {}, - - // Adds a location field to test results - // testLocationInResults: false, - - // The glob patterns Jest uses to detect test files - // testMatch: [ - // "**/__tests__/**/*.[jt]s?(x)", - // "**/?(*.)+(spec|test).[tj]s?(x)" - // ], - - // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped - // testPathIgnorePatterns: [ - // "/node_modules/" - // ], - - // The regexp pattern or array of patterns that Jest uses to detect test files - // testRegex: [], - - // This option allows the use of a custom results processor - // testResultsProcessor: null, - - // This option allows use of a custom test runner - // testRunner: "jasmine2", - - // This option sets the URL for the jsdom environment. It is reflected in properties such as location.href - // testURL: "http://localhost", - - // Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout" - // timers: "real", - - // A map from regular expressions to paths to transformers - // transform: null, - - // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation - // transformIgnorePatterns: [ - // "/node_modules/" - // ], - - // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them - // unmockedModulePathPatterns: undefined, - - // Indicates whether each individual test should be reported during the run - // verbose: null, - - // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode - // watchPathIgnorePatterns: [], - - // Whether to use watchman for file crawling - // watchman: true, + coveragePathIgnorePatterns: ['/node_modules/'], + coverageReporters: ['json', 'text', 'lcov', 'html'], + transform: { + '^.+\\.tsx?$': ['ts-jest', { tsconfig: '/tsconfig.test.json' }], + }, }; diff --git a/packages/markdown-template/jsdoc.json b/packages/markdown-template/jsdoc.json deleted file mode 100644 index 66f38e56..00000000 --- a/packages/markdown-template/jsdoc.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "tags": { - "allowUnknownTags": true, - "dictionaries": ["jsdoc", "closure"] - }, - "source": { - "include": [ - "./src", - "./index.js" - ], - "includePattern": ".+\\.js(doc|x)?$" - }, - "plugins": ["plugins/markdown"], - "templates": { - "logoFile": "", - "cleverLinks": false, - "monospaceLinks": false, - "dateFormat": "ddd MMM Do YYYY", - "outputSourceFiles": true, - "outputSourcePath": true, - "systemName": "Accord Project Cicero SDK", - "footer": "", - "copyright": "Released under the Apache License v2.0", - "navType": "vertical", - "theme": "spacelab", - "linenums": true, - "collapseSymbols": false, - "inverseNav": true, - "protocol": "html://", - "methodHeadingReturns": false - }, - "markdown": { - "parser": "gfm", - "hardwrap": true - } -} \ No newline at end of file diff --git a/packages/markdown-template/lib/FormulaVisitor.js b/packages/markdown-template/lib/FormulaVisitor.js deleted file mode 100644 index e4181782..00000000 --- a/packages/markdown-template/lib/FormulaVisitor.js +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -/** - * Converts a CommonMark DOM to a CiceroMark DOM - */ -class FormulaVisitor { - /** - * Visits a sub-tree and return CiceroMark DOM - * @param {*} visitor the visitor to use - * @param {*} thing the node to visit - * @param {*} [parameters] optional parameters - * @param {string} field where the children are - */ - static visitChildren(visitor, thing, parameters) { - var field = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 'nodes'; - if (thing[field]) { - FormulaVisitor.visitNodes(visitor, thing[field], parameters); - } - } - - /** - * Visits a list of nodes and return the CiceroMark DOM - * @param {*} visitor the visitor to use - * @param {*} things the list node to visit - * @param {*} [parameters] optional parameters - */ - static visitNodes(visitor, things, parameters) { - things.forEach(node => { - node.accept(visitor, parameters); - }); - } - - /** - * Calculates the dependencies for TS code - * @param {string} tsCode the TS code to analyze - * @returns {string[]} array of dependencies - */ - static calculateDependencies(tsCode) { - try { - var deps = []; - // TODO!! - return deps; - } catch (err) { - throw new Error("Failed to calculate dependencies in code '".concat(tsCode, "'. Error: ").concat(err)); - } - } - - /** - * Visit a node - * @param {*} thing the object being visited - * @param {*} parameters the parameters - */ - visit(thing, parameters) { - switch (thing.getType()) { - case 'ConditionalDefinition': - { - if (parameters.calculateDependencies) { - if (thing.condition) { - thing.dependencies = FormulaVisitor.calculateDependencies(thing.condition.contents); - } - } else { - parameters.result.push({ - name: thing.name, - code: thing.condition - }); - } - } - break; - case 'FormulaDefinition': - { - if (parameters.calculateDependencies) { - if (thing.code) { - thing.dependencies = FormulaVisitor.calculateDependencies(thing.code.contents); - } - } else { - parameters.result.push({ - name: thing.name, - code: thing.code - }); - } - } - break; - default: - FormulaVisitor.visitChildren(this, thing, parameters); - } - } - - /** - * Calculate dependencies - * @param {*} serializer - the template mark serializer - * @param {object} ast - the template AST - * @param {object} options - options - * @param {number} [options.utcOffset] - UTC Offset for this execution - * @returns {*} the formulas - */ - calculateDependencies(serializer, ast, options) { - var parameters = { - calculateDependencies: true, - variables: [], - result: [] - }; - var input = serializer.fromJSON(ast, options); - input.accept(this, parameters); - return serializer.toJSON(input, options); - } - - /** - * Process formulas and returns the list of those formulas from a TemplateMark DOM - * @param {*} serializer - the template mark serializer - * @param {object} ast - the template AST - * @param {object} options - options - * @param {number} [options.utcOffset] - UTC Offset for this execution - * @returns {*} the formulas - */ - processFormulas(serializer, ast, options) { - var parameters = { - calculateDependencies: false, - variables: [], - result: [] - }; - var input = serializer.fromJSON(ast, options); - input.accept(this, parameters); - return parameters.result; - } -} -module.exports = FormulaVisitor; \ No newline at end of file diff --git a/packages/markdown-template/lib/ModelVisitor.js b/packages/markdown-template/lib/ModelVisitor.js deleted file mode 100644 index e68a8db0..00000000 --- a/packages/markdown-template/lib/ModelVisitor.js +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -var { - CommonMarkModel, - TemplateMarkModel -} = require('@accordproject/markdown-common'); - -/** - * Converts concerto models to TemplateMark - * - * @private - * @class - */ -class ModelVisitor { - /** - * Visitor design pattern - * @param {Object} thing - the object being visited - * @param {Object} parameters - the parameter - * @return {Object} the result of visiting or null - * @private - */ - visit(thing, parameters) { - var _thing$isEnum, _thing$isClassDeclara, _thing$isField, _thing$isRelationship, _thing$isEnumValue; - if ((_thing$isEnum = thing.isEnum) !== null && _thing$isEnum !== void 0 && _thing$isEnum.call(thing)) { - return this.visitEnumDeclaration(thing, parameters); - } else if ((_thing$isClassDeclara = thing.isClassDeclaration) !== null && _thing$isClassDeclara !== void 0 && _thing$isClassDeclara.call(thing)) { - return this.visitClassDeclaration(thing, parameters); - } else if ((_thing$isField = thing.isField) !== null && _thing$isField !== void 0 && _thing$isField.call(thing)) { - return this.visitField(thing, parameters); - } else if ((_thing$isRelationship = thing.isRelationship) !== null && _thing$isRelationship !== void 0 && _thing$isRelationship.call(thing)) { - return this.visitRelationship(thing, parameters); - } else if ((_thing$isEnumValue = thing.isEnumValue) !== null && _thing$isEnumValue !== void 0 && _thing$isEnumValue.call(thing)) { - return this.visitEnumValueDeclaration(thing, parameters); - } else { - throw new Error('Unrecognised type: ' + typeof thing + ', value: ' + thing); - } - } - - /** - * Visitor design pattern - * @param {EnumDeclaration} enumDeclaration - the object being visited - * @param {Object} parameters - the parameter - * @return {Object} the result of visiting or null - * @private - */ - visitEnumDeclaration(enumDeclaration, parameters) { - var result = {}; - result.$class = "".concat(TemplateMarkModel.NAMESPACE, ".EnumVariableDefinition"); - result.name = parameters.type; - return result; - } - - /** - * Visitor design pattern - * @param {ClassDeclaration} classDeclaration - the object being visited - * @param {Object} parameters - the parameter - * @return {Object} the result of visiting or null - * @private - */ - visitClassDeclaration(classDeclaration, parameters) { - var result = {}; - result.$class = "".concat(TemplateMarkModel.NAMESPACE, ".WithDefinition"); - result.name = parameters.name; - result.nodes = []; - var first = true; - classDeclaration.getProperties().forEach((property, index) => { - if (!first) { - var textNode = {}; - textNode.$class = "".concat(CommonMarkModel.NAMESPACE, ".Text"); - textNode.text = ' '; - result.nodes.push(textNode); - } - result.nodes.push(property.accept(this, parameters)); - first = false; - }); - return result; - } - - /** - * Visitor design pattern - * @param {Field} field - the object being visited - * @param {Object} parameters - the parameter - * @return {Object} the result of visiting or null - * @private - */ - visitField(field, parameters) { - var fieldName = field.getName(); - var result = {}; - result.$class = "".concat(TemplateMarkModel.NAMESPACE, ".VariableDefinition"); - result.name = fieldName; - if (field.isArray()) { - if (field.isPrimitive()) { - result.name = 'this'; - } - var arrayResult = {}; - arrayResult.$class = "".concat(TemplateMarkModel.NAMESPACE, ".JoinDefinition"); - arrayResult.separator = ' '; // XXX {{#join }} - arrayResult.name = fieldName; - arrayResult.nodes = [result]; - result = arrayResult; - } - if (field.isOptional()) { - if (field.isPrimitive()) { - result.name = 'this'; - } - var optionalResult = {}; - optionalResult.$class = "".concat(TemplateMarkModel.NAMESPACE, ".OptionalDefinition"); - optionalResult.name = fieldName; - optionalResult.whenSome = [result]; - optionalResult.whenNone = []; - result = optionalResult; - } - return result; - } - - /** - * Visitor design pattern - * @param {EnumValueDeclaration} enumValueDeclaration - the object being visited - * @param {Object} parameters - the parameter - * @private - */ - visitEnumValueDeclaration(enumValueDeclaration, parameters) { - throw new Error('visitEnumValueDeclaration not handled'); - } - - /** - * Visitor design pattern - * @param {Relationship} relationship - the object being visited - * @param {Object} parameters - the parameter - * @private - */ - visitRelationshipDeclaration(relationship, parameters) { - throw new Error('visitRelationshipDeclaration'); - } -} -module.exports = ModelVisitor; \ No newline at end of file diff --git a/packages/markdown-template/lib/TemplateMarkTransformer.js b/packages/markdown-template/lib/TemplateMarkTransformer.js deleted file mode 100644 index 5dda0419..00000000 --- a/packages/markdown-template/lib/TemplateMarkTransformer.js +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -/** @typedef {import('@accordproject/concerto-core').Serializer} Serializer */ -var { - templateMarkManager, - templateToTokens, - tokensToUntypedTemplateMark, - templateMarkTyping -} = require('./templatemarkutil'); -var ToMarkdownTemplateVisitor = require('./ToMarkdownTemplateVisitor'); - -/** - * Support for TemplateMark Templates - */ -class TemplateMarkTransformer { - /** - * Converts a template string to a token stream - * @param {object} templateInput the template template - * @returns {object} the token stream - */ - toTokens(templateInput) { - return templateToTokens(templateInput.content); - } - - /** - * Converts a template token strean string to a TemplateMark DOM - * @param {object} tokenStream the template token stream - * @param {object} modelManager - the model manager for this template - * @param {string} templateKind - either 'clause' or 'contract' - * @param {object} [options] configuration options - * @param {boolean} [options.verbose] verbose output - * @param {string} [conceptFullyQualifiedName] - the fully qualified name of the template concept - * @returns {object} the result of parsing - */ - tokensToMarkdownTemplate(tokenStream, modelManager, templateKind, options, conceptFullyQualifiedName) { - var template = tokensToUntypedTemplateMark(tokenStream, templateKind); - if (options && options.verbose) { - console.log('===== Untyped TemplateMark '); - console.log(JSON.stringify(template, null, 2)); - } - var typedTemplate = templateMarkTyping(template, modelManager, templateKind, conceptFullyQualifiedName); - if (options && options.verbose) { - console.log('===== TemplateMark '); - console.log(JSON.stringify(typedTemplate, null, 2)); - } - return typedTemplate; - } - - /** - * Converts a markdown string to a TemplateMark DOM - * @param {{fileName:string,content:string}} templateInput the template template - * @param {object} modelManager - the model manager for this template - * @param {string} templateKind - either 'clause' or 'contract' - * @param {object} [options] configuration options - * @param {boolean} [options.verbose] verbose output - * @param {string} [conceptFullyQualifiedName] - the fully qualified name of the template concept - * @returns {object} the result of parsing - */ - fromMarkdownTemplate(templateInput, modelManager, templateKind, options, conceptFullyQualifiedName) { - if (!modelManager) { - throw new Error('Cannot parse without template model'); - } - var tokenStream = this.toTokens(templateInput); - if (options && options.verbose) { - console.log('===== MarkdownIt Tokens '); - console.log(JSON.stringify(tokenStream, null, 2)); - } - return this.tokensToMarkdownTemplate(tokenStream, modelManager, templateKind, options, conceptFullyQualifiedName); - } - - /** - * Converts a TemplateMark DOM to a template markdown string - * @param {object} input TemplateMark DOM - * @returns {string} the template markdown text - */ - toMarkdownTemplate(input) { - var visitor = new ToMarkdownTemplateVisitor(); - return visitor.toMarkdownTemplate(templateMarkManager.serializer, input); - } - - /** - * Get TemplateMark serializer - * @return {Serializer} templatemark serializer - */ - getSerializer() { - return templateMarkManager.serializer; - } -} -module.exports = TemplateMarkTransformer; \ No newline at end of file diff --git a/packages/markdown-template/lib/ToCiceroMarkVisitor.js b/packages/markdown-template/lib/ToCiceroMarkVisitor.js deleted file mode 100644 index 14c9e3d7..00000000 --- a/packages/markdown-template/lib/ToCiceroMarkVisitor.js +++ /dev/null @@ -1,344 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -var dayjs = require('dayjs'); -var flatten = require('./util').flatten; -var generateJSON = require('./templatemarkutil').generateJSON; -var { - CommonMarkModel, - CiceroMarkModel -} = require('@accordproject/markdown-common'); - -/** - * Drafts a CiceroMark DOM from a TemplateMark DOM - */ -class ToCiceroMarkVisitor { - /** - * Clone a CiceroMark node - * @param {*} serializer the serializer - * @param {*} node the node to visit - * @param {*} [parameters] optional parameters - * @return {*} the cloned node - */ - static cloneNode(serializer, node) { - return serializer.fromJSON(serializer.toJSON(node)); - } - - /** - * Visits a sub-tree and return CiceroMark DOM - * @param {*} visitor the visitor to use - * @param {*} thing the node to visit - * @param {*} [parameters] optional parameters - */ - static visitChildren(visitor, thing, parameters) { - if (thing.nodes) { - thing.nodes = ToCiceroMarkVisitor.visitNodes(visitor, thing.nodes, parameters); - } - } - - /** - * Visits a list of nodes and return the CiceroMark DOM - * @param {*} visitor the visitor to use - * @param {*} things the list node to visit - * @param {*} [parameters] optional parameters - * @return {*} the visited nodes - */ - static visitNodes(visitor, things, parameters) { - return flatten(things.map(node => { - return node.accept(visitor, parameters); - })); - } - - /** - * Match template tag to instance tag - * @param {string} tag the template tag - * @return {string} the corresponding instance tag - */ - static matchTag(tag) { - if (tag === 'VariableDefinition') { - return "".concat(CiceroMarkModel.NAMESPACE, ".Variable"); - } else if (tag === 'FormattedVariableDefinition') { - return "".concat(CiceroMarkModel.NAMESPACE, ".FormattedVariable"); - } else if (tag === 'EnumVariableDefinition') { - return "".concat(CiceroMarkModel.NAMESPACE, ".EnumVariable"); - } else if (tag === 'FormulaDefinition') { - return "".concat(CiceroMarkModel.NAMESPACE, ".Formula"); - } else if (tag === 'ClauseDefinition') { - return "".concat(CiceroMarkModel.NAMESPACE, ".Clause"); - } else if (tag === 'ConditionalDefinition') { - return "".concat(CiceroMarkModel.NAMESPACE, ".Conditional"); - } else if (tag === 'OptionalDefinition') { - return "".concat(CiceroMarkModel.NAMESPACE, ".Optional"); - } else if (tag === 'ListBlockDefinition') { - return "".concat(CiceroMarkModel.NAMESPACE, ".ListBlock"); - } else { - return tag; - } - } - - /** - * Evaluates a JS expression - * @param {*} data the contract data - * @param {string} expression the JS expression - * @param {Date} now the current value for now - * @returns {Boolean} the result of evaluating the expression against the data - */ - static eval(data, expression, now) { - data.now = now ? now : dayjs(); - var args = Object.keys(data); - var values = Object.values(data); - // const types = values.map( v => typeof v); - // console.log('**** ' + JSON.stringify(data, null, 2)); - // console.log('**** ' + expression); - // console.log('**** ' + args); - // console.log('**** ' + values); - // console.log('**** ' + types); - var fun = new Function(...args, expression); // SECURITY! - var result = fun(...values); - // console.log('**** ' + result); - return result; - } - - /** - * Visit a node - * @param {*} thing the object being visited - * @param {*} parameters the parameters - * @return {*} the visited nodes - */ - visit(thing, parameters) { - var that = this; - switch (thing.getType()) { - case 'EnumVariableDefinition': - { - var ciceroMarkTag = ToCiceroMarkVisitor.matchTag(thing.getType()); - thing.$classDeclaration = parameters.templateMarkModelManager.getType(ciceroMarkTag); - thing.value = '' + parameters.data[thing.name]; - } - break; - case 'VariableDefinition': - case 'FormattedVariableDefinition': - { - var _ciceroMarkTag = ToCiceroMarkVisitor.matchTag(thing.getType()); - thing.$classDeclaration = parameters.templateMarkModelManager.getType(_ciceroMarkTag); - var data = thing.name === 'this' ? parameters.data : parameters.data[thing.name]; - var elementType = thing.identifiedBy ? 'Resource' : thing.elementType; - parameters.visitor = that; - var draftFun = parameters.parserManager.getParsingTable().getDrafter(thing.name, elementType, thing.format, parameters); - var draftedTo = draftFun(data, thing.format); - if (typeof draftedTo === 'string') { - thing.value = '' + draftedTo; - } else { - return draftedTo; - } - } - break; - case 'ContractDefinition': - { - return ToCiceroMarkVisitor.visitNodes(this, thing.nodes, parameters); - } - case 'FormulaDefinition': - { - var _ciceroMarkTag2 = ToCiceroMarkVisitor.matchTag(thing.getType()); - thing.$classDeclaration = parameters.templateMarkModelManager.getType(_ciceroMarkTag2); - // thing.value = parameters.parserManager.getFormulaEval(thing.name)(thing.code,parameters.fullData,parameters.currentTime); - thing.value = JSON.stringify(ToCiceroMarkVisitor.eval(parameters.fullData, thing.code, parameters.currentTime)); - } - break; - case 'ClauseDefinition': - { - if (parameters.kind === 'contract') { - var _ciceroMarkTag3 = ToCiceroMarkVisitor.matchTag(thing.getType()); - thing.$classDeclaration = parameters.templateMarkModelManager.getType(_ciceroMarkTag3); - var childrenParameters = { - parserManager: parameters.parserManager, - templateMarkModelManager: parameters.templateMarkModelManager, - templateMarkSerializer: parameters.templateMarkSerializer, - fullData: parameters.fullData, - data: parameters.data[thing.name], - kind: parameters.kind, - currentTime: parameters.currentTime - }; - ToCiceroMarkVisitor.visitChildren(this, thing, childrenParameters); - } else { - ToCiceroMarkVisitor.visitChildren(this, thing, parameters); - } - } - break; - case 'WithDefinition': - { - var _childrenParameters = { - parserManager: parameters.parserManager, - templateMarkModelManager: parameters.templateMarkModelManager, - templateMarkSerializer: parameters.templateMarkSerializer, - fullData: parameters.fullData, - data: parameters.data[thing.name], - kind: parameters.kind, - currentTime: parameters.currentTime - }; - return ToCiceroMarkVisitor.visitNodes(this, thing.nodes, _childrenParameters); - } - case 'ConditionalDefinition': - { - var _ciceroMarkTag4 = ToCiceroMarkVisitor.matchTag(thing.getType()); - thing.$classDeclaration = parameters.templateMarkModelManager.getType(_ciceroMarkTag4); - ToCiceroMarkVisitor.visitNodes(this, thing.whenTrue, parameters); - ToCiceroMarkVisitor.visitNodes(this, thing.whenFalse, parameters); - var conditionTrue = thing.condition ? ToCiceroMarkVisitor.eval(parameters.data, "return !!".concat(thing.condition), parameters.currentTime) : parameters.data[thing.name]; - delete thing.condition; - delete thing.dependencies; - if (conditionTrue) { - thing.isTrue = true; - thing.nodes = thing.whenTrue; - } else { - thing.isTrue = false; - thing.nodes = thing.whenFalse; - } - } - break; - case 'OptionalDefinition': - { - var _ciceroMarkTag5 = ToCiceroMarkVisitor.matchTag(thing.getType()); - thing.$classDeclaration = parameters.templateMarkModelManager.getType(_ciceroMarkTag5); - if (parameters.data[thing.name]) { - thing.hasSome = true; - thing.nodes = thing.whenSome; - var someParameters = { - parserManager: parameters.parserManager, - templateMarkModelManager: parameters.templateMarkModelManager, - templateMarkSerializer: parameters.templateMarkSerializer, - fullData: parameters.fullData, - data: parameters.data[thing.name], - kind: parameters.kind, - currentTime: parameters.currentTime - }; - ToCiceroMarkVisitor.visitNodes(this, thing.whenSome, someParameters); - var noneParameters = { - parserManager: parameters.parserManager, - templateMarkModelManager: parameters.templateMarkModelManager, - templateMarkSerializer: parameters.templateMarkSerializer, - fullData: parameters.fullData, - data: {}, - kind: parameters.kind, - currentTime: parameters.currentTime - }; - ToCiceroMarkVisitor.visitNodes(this, thing.whenNone, noneParameters); - } else { - thing.hasSome = false; - thing.nodes = thing.whenNone; - var invented = generateJSON(parameters.parserManager.getModelManager(), thing.elementType); - var _someParameters = { - parserManager: parameters.parserManager, - templateMarkModelManager: parameters.templateMarkModelManager, - templateMarkSerializer: parameters.templateMarkSerializer, - fullData: parameters.fullData, - data: invented, - // Need to invent some data here! - kind: parameters.kind, - currentTime: parameters.currentTime - }; - ToCiceroMarkVisitor.visitNodes(this, thing.whenSome, _someParameters); - var _noneParameters = { - parserManager: parameters.parserManager, - templateMarkModelManager: parameters.templateMarkModelManager, - templateMarkSerializer: parameters.templateMarkSerializer, - fullData: parameters.fullData, - data: {}, - kind: parameters.kind, - currentTime: parameters.currentTime - }; - ToCiceroMarkVisitor.visitNodes(this, thing.whenNone, _noneParameters); - } - } - break; - case 'ListBlockDefinition': - { - // Clone the thing and create an item blueprint - var itemNode = ToCiceroMarkVisitor.cloneNode(parameters.templateMarkSerializer, thing); - itemNode.$classDeclaration = parameters.templateMarkModelManager.getType("".concat(CommonMarkModel.NAMESPACE, ".Item")); - delete itemNode.elementType; - delete itemNode.decorators; - delete itemNode.name; - delete itemNode.type; - delete itemNode.start; - delete itemNode.tight; - delete itemNode.delimiter; - var dataItems = parameters.data[thing.name]; - var mapItems = function mapItems(item) { - var itemParameters = { - parserManager: parameters.parserManager, - templateMarkModelManager: parameters.templateMarkModelManager, - templateMarkSerializer: parameters.templateMarkSerializer, - fullData: parameters.fullData, - data: item, - kind: parameters.kind, - currentTime: parameters.currentTime - }; - return ToCiceroMarkVisitor.cloneNode(parameters.templateMarkSerializer, itemNode).accept(that, itemParameters); - }; - - // Result List node - var _ciceroMarkTag6 = ToCiceroMarkVisitor.matchTag(thing.getType()); - thing.$classDeclaration = parameters.templateMarkModelManager.getType(_ciceroMarkTag6); - thing.nodes = flatten(dataItems.map(mapItems)); - delete thing.elementType; - } - break; - case 'JoinDefinition': - { - // Clone the thing and create an item blueprint - var _itemNode = ToCiceroMarkVisitor.cloneNode(parameters.templateMarkSerializer, thing); - _itemNode.$classDeclaration = parameters.templateMarkModelManager.getType("".concat(CommonMarkModel.NAMESPACE, ".Item")); - delete _itemNode.elementType; - delete _itemNode.decorators; - delete _itemNode.name; - delete _itemNode.separator; - var _dataItems = parameters.data[thing.name]; - var _mapItems = function _mapItems(item, index) { - var itemParameters = { - parserManager: parameters.parserManager, - templateMarkModelManager: parameters.templateMarkModelManager, - templateMarkSerializer: parameters.templateMarkSerializer, - fullData: parameters.fullData, - data: item, - kind: parameters.kind, - currentTime: parameters.currentTime - }; - var resultNodes = ToCiceroMarkVisitor.cloneNode(parameters.templateMarkSerializer, _itemNode).accept(that, itemParameters)[0].nodes; - if (index > 0) { - resultNodes.unshift(parameters.templateMarkSerializer.fromJSON({ - '$class': "".concat(CommonMarkModel.NAMESPACE, ".Text"), - 'text': thing.separator - })); - } - return resultNodes; - }; - - // Result List node - return flatten(_dataItems.map(_mapItems)); - } - case 'Document': - { - ToCiceroMarkVisitor.visitChildren(this, thing.nodes[0], parameters); - thing.nodes = thing.nodes[0].nodes; - } - break; - default: - ToCiceroMarkVisitor.visitChildren(this, thing, parameters); - } - return [thing]; - } -} -module.exports = ToCiceroMarkVisitor; \ No newline at end of file diff --git a/packages/markdown-template/lib/ToMarkdownTemplateVisitor.js b/packages/markdown-template/lib/ToMarkdownTemplateVisitor.js deleted file mode 100644 index c440b79f..00000000 --- a/packages/markdown-template/lib/ToMarkdownTemplateVisitor.js +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -var { - CommonMarkUtils, - CommonMarkModel -} = require('@accordproject/markdown-common'); -var FromCommonMarkVisitor = require('@accordproject/markdown-common').FromCommonMarkVisitor; -var fromcommonmarkrules = require('@accordproject/markdown-common').fromcommonmarkrules; -var fromtemplatemarkrules = require('./fromtemplatemarkrules'); - -/** - * Fixes up the root note, removing Clause or Contract indication - * @param {object} input the input templatemark - * @return {object} the fixed up templatemark - */ -function fixupRootNode(input) { - var rootNode = { - '$class': "".concat(CommonMarkModel.NAMESPACE, ".Document"), - 'xmlns': 'http://commonmark.org/xml/1.0', - 'nodes': input.nodes[0].nodes - }; - return rootNode; -} - -/** - * Converts a TemplateMark DOM to a template markdown string. - */ -class ToMarkdownTemplateVisitor extends FromCommonMarkVisitor { - /** - * Construct the visitor. - * @param {object} [options] configuration options - * @param {*} resultSeq how to sequentially combine results - * @param {object} rules how to process each node type - */ - constructor(options) { - var resultString = result => { - return result; - }; - var resultSeq = (parameters, result) => { - result.forEach(next => { - parameters.result += next; - }); - }; - var setFirst = thingType => { - return thingType === 'Item' || thingType === 'ClauseDefinition' || thingType === 'ListBlockDefinition' ? true : false; - }; - var rules = fromcommonmarkrules; - Object.assign(rules, fromtemplatemarkrules); - super(options, resultString, resultSeq, rules, setFirst); - } - - /** - * Converts a TemplateMark DOM to a template markdown string. - * @param {*} serializer - TemplateMark serializer - * @param {*} input - TemplateMark DOM (JSON) - * @returns {string} the template markdown string - */ - toMarkdownTemplate(serializer, input) { - var parameters = {}; - var fixedInput = serializer.fromJSON(fixupRootNode(input)); - parameters.result = this.resultString(''); - parameters.stack = CommonMarkUtils.blocksInit(); - fixedInput.accept(this, parameters); - return parameters.result.trim(); - } -} -module.exports = ToMarkdownTemplateVisitor; \ No newline at end of file diff --git a/packages/markdown-template/lib/ToParserVisitor.js b/packages/markdown-template/lib/ToParserVisitor.js deleted file mode 100644 index d1b6bfe1..00000000 --- a/packages/markdown-template/lib/ToParserVisitor.js +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -var P = require('parsimmon'); -var flatten = require('./util').flatten; -var CommonMarkUtils = require('@accordproject/markdown-common').CommonMarkUtils; -var FromCommonMarkVisitor = require('@accordproject/markdown-common').FromCommonMarkVisitor; -var fromcommonmarkrules = require('@accordproject/markdown-common').fromcommonmarkrules; -var toparserrules = require('./toparserrules'); -var templateMarkManager = require('./templatemarkutil').templateMarkManager; -var seqFunParser = require('./combinators').seqFunParser; -var resultString = result => { - return r => P.string(result); -}; -var resultSeq = (parameters, result) => { - var resultParsers; - if (parameters.result) { - resultParsers = seqFunParser([parameters.result].concat(result)); - } else { - resultParsers = seqFunParser(result); - } - parameters.result = function (x) { - return resultParsers(x).map(flatten); - }; -}; - -/** - * Converts a TemplateMark DOM to a parser. - */ -class ToParserVisitor extends FromCommonMarkVisitor { - /** - * Construct the visitor. - * @param {object} [options] configuration options - * @param {*} resultSeq how to sequentially combine results - * @param {object} rules how to process each node type - */ - constructor(options) { - var setFirst = thingType => { - return thingType === 'Item' || thingType === 'ClauseDefinition' || thingType === 'ListBlockDefinition' ? true : false; - }; - var rules = fromcommonmarkrules; - Object.assign(rules, toparserrules); - super(options, resultString, resultSeq, rules, setFirst); - } - - /** - * Converts a TemplateMark DOM to a parser for that node, given parameters - * @param {object} visitor - the visitor - * @param {object} ast - the template AST - * @param {object} parameters - current parameters - * @returns {object} the parser - */ - static toParserWithParameters(visitor, ast, parameters) { - var localParameters = Object.assign({}, parameters); - localParameters.parserManager = parameters.parserManager; - localParameters.parsingTable = parameters.parsingTable, localParameters.templateParser = parameters.templateParser; - localParameters.result = resultString(''); - localParameters.stack = parameters.stack; - localParameters.first = parameters.first; - var dom = templateMarkManager.serializer.fromJSON(ast); - dom.accept(visitor, localParameters); - return localParameters.result; - } - - /** - * Converts a TemplateMark DOM to a full parser - * @param {*} parserManager - the parser manager - * @param {object} ast - the template AST - * @param {object} parsingTable - the parsing table - * @returns {object} the parser - */ - toParser(parserManager, ast, parsingTable) { - // Start with an empty parser - var templateParser = {}; - var parameters = {}; - parameters.parserManager = parserManager; - parameters.parsingTable = parsingTable, parameters.templateParser = templateParser; - parameters.result = resultString(''); - parameters.stack = CommonMarkUtils.blocksInit(); - parameters.first = false; - var parser = ToParserVisitor.toParserWithParameters(this, ast, parameters); - templateParser.main = r => parser(r).map(function (x) { - return x[0]; - }); - return P.createLanguage(templateParser).main; - } -} -module.exports = ToParserVisitor; \ No newline at end of file diff --git a/packages/markdown-template/lib/TypeVisitor.js b/packages/markdown-template/lib/TypeVisitor.js deleted file mode 100644 index 8b48c940..00000000 --- a/packages/markdown-template/lib/TypeVisitor.js +++ /dev/null @@ -1,371 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -var { - TemplateMarkModel, - ConcertoMetaModel -} = require('@accordproject/markdown-common'); -var _throwTemplateExceptionForElement = require('./errorutil')._throwTemplateExceptionForElement; - -/** - * @param {*} serializer - the serializer - * @param {object} decorated - the property - * @return {object} the array of decorators compliant with the Concerto metamodel (JSON) - */ -function processDecorators(serializer, decorated) { - var result = []; - var decorators = decorated.getDecorators(); - decorators.forEach(decorator => { - var metaDecorator = { - '$class': "".concat(ConcertoMetaModel.NAMESPACE, ".Decorator") - }; - - // The decorator's name - var name = decorator.getName(); - metaDecorator.name = name; - metaDecorator.arguments = []; - - // The decorator's arguments - var args = decorator.getArguments(); - args.forEach(arg => { - var metaArgument; - if (typeof arg === 'string') { - metaArgument = { - '$class': "".concat(ConcertoMetaModel.NAMESPACE, ".DecoratorString"), - 'value': arg - }; - } else if (typeof arg === 'number') { - metaArgument = { - '$class': "".concat(ConcertoMetaModel.NAMESPACE, ".DecoratorNumber"), - 'value': arg - }; - } else if (typeof arg === 'boolean') { - metaArgument = { - '$class': "".concat(ConcertoMetaModel.NAMESPACE, ".DecoratorBoolean"), - 'value': arg - }; - } else { - metaArgument = { - '$class': "".concat(ConcertoMetaModel.NAMESPACE, ".DecoratorTypeReference"), - 'type': { - '$class': "".concat(ConcertoMetaModel.NAMESPACE, ".TypeIdentifier"), - 'name': arg.name - }, - 'isArray': arg.array - }; - } - metaDecorator.arguments.push(metaArgument); - }); - - // Validate individual arguments here - //console.log('DECORATE ' + JSON.stringify(metaDecorator)); - result.push(serializer.fromJSON(metaDecorator)); - }); - if (result.length === 0) { - return null; - } else { - return result; - } -} - -/** - * Adds the elementType property to a TemplateMark DOM - * along with type specific metadata. This visitor verifies - * the structure of a template with respect to an associated - * template model and annotates the TemplateMark DOM with model - * information for use in downstream tools. - */ -class TypeVisitor { - /** - * Visits a sub-tree and return CiceroMark DOM - * @param {*} visitor the visitor to use - * @param {*} thing the node to visit - * @param {*} [parameters] optional parameters - * @param {string} field where the children are - */ - static visitChildren(visitor, thing, parameters) { - var field = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 'nodes'; - if (thing[field]) { - TypeVisitor.visitNodes(visitor, thing[field], parameters); - } - } - - /** - * Visits a list of nodes and return the CiceroMark DOM - * @param {*} visitor the visitor to use - * @param {*} things the list node to visit - * @param {*} [parameters] optional parameters - */ - static visitNodes(visitor, things, parameters) { - things.forEach(node => { - node.accept(visitor, parameters); - }); - } - - /** - * Get the type information for a property - * @param {*} property the propety - * @param {*} parameters the configuration parameters - * @returns {*} the information about the next model element (property or declaration) - */ - static nextModel(property, parameters) { - var declaration = property.isPrimitive() ? null : parameters.introspector.getClassDeclaration(property.getFullyQualifiedTypeName()); - return { - property: property.isPrimitive() ? property : null, - declaration, - typeIdentifier: property.isPrimitive() ? property.getFullyQualifiedTypeName() : declaration.getFullyQualifiedName(), - decorated: property.isPrimitive() ? property : declaration - }; - } - - /** - * Visit a node - * @param {*} thing the object being visited - * @param {*} parameters the parameters - */ - visit(thing, parameters) { - var currentModel = parameters.model; - switch (thing.getType()) { - case 'VariableDefinition': - case 'FormattedVariableDefinition': - { - if (!currentModel) { - _throwTemplateExceptionForElement('Unknown property: ' + thing.name, thing); - } - if (thing.name === 'this') { - var property = currentModel; // BUG... if we are iterating over an array - // of complex types using a {{this}}, then thing will be a ClassDeclaration or an - // EnumDeclaration!! - - if (property && property.getType) { - var _property$isRelations; - var serializer = parameters.templateMarkModelManager.getSerializer(); - thing.decorators = processDecorators(serializer, property); - if (property.isTypeEnum && property.isTypeEnum()) { - var enumVariableDeclaration = parameters.templateMarkModelManager.getType("".concat(TemplateMarkModel.NAMESPACE, ".EnumVariableDefinition")); - var enumType = property.getParent().getModelFile().getType(property.getType()); - thing.elementType = property.getFullyQualifiedTypeName(); - thing.$classDeclaration = enumVariableDeclaration; - thing.enumValues = enumType.getOwnProperties().map(x => x.getName()); - } else if (property.isPrimitive()) { - thing.elementType = property.getFullyQualifiedTypeName(); - } else if ((_property$isRelations = property.isRelationship) !== null && _property$isRelations !== void 0 && _property$isRelations.call(property)) { - var elementType = property.getFullyQualifiedTypeName(); - thing.elementType = elementType; - var nestedTemplateModel = parameters.introspector.getClassDeclaration(elementType); - var identifier = nestedTemplateModel.getIdentifierFieldName(); - thing.identifiedBy = identifier ? identifier : '$identifier'; // Consistent with Concerto 1.0 semantics - } else { - var _elementType = property.getFullyQualifiedTypeName(); - thing.elementType = _elementType; - } - } else { - // it is a class - var _elementType2 = property.getFullyQualifiedName(); - thing.elementType = _elementType2; - } - } else { - if (!currentModel.getProperty) { - _throwTemplateExceptionForElement('Unknown property: ' + thing.name, thing); - } - var _property = currentModel.getProperty(thing.name); - if (_property) { - var _property$isRelations2; - var _serializer = parameters.templateMarkModelManager.getSerializer(); - thing.decorators = processDecorators(_serializer, _property); - if (_property.isTypeEnum && _property.isTypeEnum()) { - var _enumVariableDeclaration = parameters.templateMarkModelManager.getType("".concat(TemplateMarkModel.NAMESPACE, ".EnumVariableDefinition")); - var _enumType = _property.getParent().getModelFile().getType(_property.getType()); - thing.elementType = _property.getFullyQualifiedTypeName(); - thing.$classDeclaration = _enumVariableDeclaration; - thing.enumValues = _enumType.getOwnProperties().map(x => x.getName()); - } else if (_property.isPrimitive()) { - thing.elementType = _property.getFullyQualifiedTypeName(); - } else if ((_property$isRelations2 = _property.isRelationship) !== null && _property$isRelations2 !== void 0 && _property$isRelations2.call(_property)) { - var _elementType3 = _property.getFullyQualifiedTypeName(); - thing.elementType = _elementType3; - var _nestedTemplateModel = parameters.introspector.getClassDeclaration(_elementType3); - var _identifier = _nestedTemplateModel.getIdentifierFieldName(); - thing.identifiedBy = _identifier ? _identifier : '$identifier'; // Consistent with Concerto 1.0 semantics - } else { - var _elementType4 = _property.getFullyQualifiedTypeName(); - thing.elementType = _elementType4; - } - } else { - _throwTemplateExceptionForElement('Unknown property: ' + thing.name, thing); - } - } - } - break; - case 'ClauseDefinition': - { - if (parameters.kind === 'contract') { - if (!currentModel) { - _throwTemplateExceptionForElement('Unknown property: ' + thing.name, thing); - } - var _property2 = currentModel.getOwnProperty(thing.name); - if (!_property2) { - _throwTemplateExceptionForElement('Unknown property: ' + thing.name, thing); - } - var { - typeIdentifier, - decorated - } = TypeVisitor.nextModel(_property2, parameters); - thing.elementType = typeIdentifier; - var _serializer2 = parameters.templateMarkModelManager.getSerializer(); - thing.decorators = processDecorators(_serializer2, decorated); - TypeVisitor.visitChildren(this, thing, { - templateMarkModelManager: parameters.templateMarkModelManager, - introspector: parameters.introspector, - model: decorated, - kind: parameters.kind - }); - } else { - if (!currentModel) { - _throwTemplateExceptionForElement('Unknown property: ' + thing.name, thing); - } - var _serializer3 = parameters.templateMarkModelManager.getSerializer(); - thing.decorators = processDecorators(_serializer3, currentModel); - thing.elementType = currentModel.getFullyQualifiedName(); - TypeVisitor.visitChildren(this, thing, parameters); - } - } - break; - case 'WithDefinition': - { - var _property3 = currentModel.getOwnProperty(thing.name); - var nextModel; - if (!_property3) { - _throwTemplateExceptionForElement('Unknown property: ' + thing.name, thing); - } - if (_property3.isPrimitive()) { - nextModel = _property3; - } else { - thing.elementType = _property3.getFullyQualifiedTypeName(); - nextModel = parameters.introspector.getClassDeclaration(thing.elementType); - } - var _serializer4 = parameters.templateMarkModelManager.getSerializer(); - thing.decorators = processDecorators(_serializer4, nextModel); - TypeVisitor.visitChildren(this, thing, { - templateMarkModelManager: parameters.templateMarkModelManager, - introspector: parameters.introspector, - model: nextModel, - kind: parameters.kind - }); - } - break; - case 'ForeachDefinition': - case 'JoinDefinition': - case 'ListBlockDefinition': - { - var _property4 = currentModel.getOwnProperty(thing.name); - var _nextModel; - if (!_property4) { - _throwTemplateExceptionForElement('Unknown property: ' + thing.name, thing); - } - if (!_property4.isArray()) { - _throwTemplateExceptionForElement("".concat(thing.getType(), " template not on an array property: ").concat(thing.name), thing); - } - var _serializer5 = parameters.templateMarkModelManager.getSerializer(); - thing.decorators = processDecorators(_serializer5, _property4); - if (_property4.isPrimitive()) { - _nextModel = _property4; - } else { - thing.elementType = _property4.getFullyQualifiedTypeName(); - _nextModel = parameters.introspector.getClassDeclaration(thing.elementType); - } - TypeVisitor.visitChildren(this, thing, { - templateMarkModelManager: parameters.templateMarkModelManager, - introspector: parameters.introspector, - model: _nextModel, - kind: parameters.kind - }); - } - break; - case 'ConditionalDefinition': - { - var _property5 = currentModel.getOwnProperty(thing.name); - var _nextModel2; - if (thing.name !== 'if' && !_property5) { - // hack, allow the node to have the name 'if' - _throwTemplateExceptionForElement('Unknown property: ' + thing.name, thing); - } - - // if (property.getType() !== 'Boolean') { - // _throwTemplateExceptionForElement('Conditional template not on a boolean property: ' + thing.name, thing); - // } - var _serializer6 = parameters.templateMarkModelManager.getSerializer(); - thing.decorators = _property5 ? processDecorators(_serializer6, _property5) : null; - _nextModel2 = _property5; - TypeVisitor.visitChildren(this, thing, { - templateMarkModelManager: parameters.templateMarkModelManager, - introspector: parameters.introspector, - model: _nextModel2, - kind: parameters.kind - }, 'whenTrue'); - TypeVisitor.visitChildren(this, thing, { - templateMarkModelManager: parameters.templateMarkModelManager, - introspector: parameters.introspector, - model: null, - kind: parameters.kind - }, 'whenFalse'); - } - break; - case 'OptionalDefinition': - { - var _property6 = currentModel.getOwnProperty(thing.name); - var _nextModel3; - if (!_property6) { - _throwTemplateExceptionForElement('Unknown property: ' + thing.name, thing); - } - if (!_property6.isOptional()) { - _throwTemplateExceptionForElement('Optional template not on an optional property: ' + thing.name, thing); - } - var _serializer7 = parameters.templateMarkModelManager.getSerializer(); - thing.decorators = processDecorators(_serializer7, _property6); - if (_property6.isPrimitive()) { - thing.elementType = _property6.getFullyQualifiedTypeName(); - _nextModel3 = _property6; - } else { - thing.elementType = _property6.getFullyQualifiedTypeName(); - _nextModel3 = parameters.introspector.getClassDeclaration(thing.elementType); - } - TypeVisitor.visitChildren(this, thing, { - templateMarkModelManager: parameters.templateMarkModelManager, - introspector: parameters.introspector, - model: _nextModel3, - kind: parameters.kind - }, 'whenSome'); - TypeVisitor.visitChildren(this, thing, { - templateMarkModelManager: parameters.templateMarkModelManager, - introspector: parameters.introspector, - model: null, - kind: parameters.kind - }, 'whenNone'); - } - break; - case 'ContractDefinition': - { - thing.elementType = currentModel.getFullyQualifiedName(); - TypeVisitor.visitChildren(this, thing, parameters); - } - break; - default: - TypeVisitor.visitChildren(this, thing, parameters); - } - } -} -module.exports = TypeVisitor; \ No newline at end of file diff --git a/packages/markdown-template/lib/datetimeutil.js b/packages/markdown-template/lib/datetimeutil.js deleted file mode 100644 index fc3e516a..00000000 --- a/packages/markdown-template/lib/datetimeutil.js +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -var dayjs = require('dayjs'); -var utc = require('dayjs/plugin/utc'); -dayjs.extend(utc); - -/** - * Ensures there is a proper current time - * - * @param {string} currentTime - the definition of 'now' - * @returns {object} if valid, the dayjs object for the current time - */ -function setCurrentTime(currentTime) { - if (!currentTime) { - // Defaults to current local time - return dayjs.utc(); - } - try { - return dayjs.utc(currentTime); - } catch (err) { - throw new Error("".concat(currentTime, " is not a valid date and time: ").concat(err.message)); - } -} -module.exports = { - setCurrentTime -}; \ No newline at end of file diff --git a/packages/markdown-template/lib/errorutil.js b/packages/markdown-template/lib/errorutil.js deleted file mode 100644 index 340aab9d..00000000 --- a/packages/markdown-template/lib/errorutil.js +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -var TemplateException = require('./templateexception'); - -/** - * Throw a template exception for the element - * @param {string} message - the error message - * @param {object} element the AST - * @throws {TemplateException} - */ -function _throwTemplateExceptionForElement(message, element) { - var fileName = 'text/grammar.tem.md'; - //let column = element.fieldName.col; - //let line = element.fieldName.line; - var column = -1; - var line = -1; - var token = element && element.value ? element.value : ' '; - var endColumn = column + token.length; - var fileLocation = { - start: { - line, - column - }, - end: { - line, - endColumn //XXX - } - }; - throw new TemplateException(message, fileLocation, fileName, null, 'markdown-template'); -} -module.exports._throwTemplateExceptionForElement = _throwTemplateExceptionForElement; \ No newline at end of file diff --git a/packages/markdown-template/lib/externalModels/README.md b/packages/markdown-template/lib/externalModels/README.md deleted file mode 100644 index 0a0406ba..00000000 --- a/packages/markdown-template/lib/externalModels/README.md +++ /dev/null @@ -1,6 +0,0 @@ -# Warning - -The *Model.js files of this directory are populated during build. Refrain from adding files to this directory by hand. - -## License -Accord Project source code files are made available under the Apache License, Version 2.0 (Apache-2.0), located in the LICENSE file. Accord Project documentation files are made available under the Creative Commons Attribution 4.0 International License (CC-BY-4.0), available at http://creativecommons.org/licenses/by/4.0/. \ No newline at end of file diff --git a/packages/markdown-template/lib/fromtemplatemarkrules.js b/packages/markdown-template/lib/fromtemplatemarkrules.js deleted file mode 100644 index 1ff043e5..00000000 --- a/packages/markdown-template/lib/fromtemplatemarkrules.js +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -var CommonMarkUtils = require('@accordproject/markdown-common').CommonMarkUtils; -var rules = {}; -// Inlines -rules.VariableDefinition = (visitor, thing, children, parameters, resultString, resultSeq) => { - var result = [resultString('{{'), resultString(thing.name), resultString('}}')]; - resultSeq(parameters, result); -}; -rules.FormattedVariableDefinition = (visitor, thing, children, parameters, resultString, resultSeq) => { - var result = [resultString('{{'), resultString(thing.name), resultString(' as "'), resultString(thing.format), resultString('"}}')]; - resultSeq(parameters, result); -}; -rules.EnumVariableDefinition = (visitor, thing, children, parameters, resultString, resultSeq) => { - var result = [resultString('{{'), resultString(thing.name), resultString('}}')]; - resultSeq(parameters, result); -}; -rules.FormulaDefinition = (visitor, thing, children, parameters, resultString, resultSeq) => { - var result = [resultString('{{%'), resultString(thing.code), resultString('%}}')]; - resultSeq(parameters, result); -}; -rules.ConditionalDefinition = (visitor, thing, children, parameters, resultString, resultSeq) => { - var next1 = "{{#if ".concat(thing.name, "}}"); - var whenTrue = visitor.visitChildren(visitor, thing, parameters, 'whenTrue'); - var whenFalse = visitor.visitChildren(visitor, thing, parameters, 'whenFalse'); - var next2 = '{{/if}}'; - var result; - if (whenFalse) { - var next3 = '{{else}}'; - result = [resultString(next1), resultString(whenTrue), resultString(next3), resultString(whenFalse), resultString(next2)]; - } else { - result = [resultString(next1), resultString(whenTrue), resultString(next2)]; - } - resultSeq(parameters, result); -}; -rules.OptionalDefinition = (visitor, thing, children, parameters, resultString, resultSeq) => { - var next1 = "{{#optional ".concat(thing.name, "}}"); - var whenSome = visitor.visitChildren(visitor, thing, parameters, 'whenSome'); - var whenNone = visitor.visitChildren(visitor, thing, parameters, 'whenNone'); - var next2 = '{{/optional}}'; - var result; - if (whenNone) { - var next3 = '{{else}}'; - result = [resultString(next1), resultString(whenSome), resultString(next3), resultString(whenNone), resultString(next2)]; - } else { - result = [resultString(next1), resultString(whenSome), resultString(next2)]; - } - resultSeq(parameters, result); -}; -rules.WithDefinition = (visitor, thing, children, parameters, resultString, resultSeq) => { - var next1 = "{{#with ".concat(thing.name, "}}"); - var next2 = '{{/with}}'; - var result = [resultString(next1), children, resultString(next2)]; - resultSeq(parameters, result); -}; -rules.JoinDefinition = (visitor, thing, children, parameters, resultString, resultSeq) => { - var sepAttr = thing.separator ? ' separator="' + thing.separator + '"' : ''; - var localeAttr = thing.locale ? ' locale="' + thing.locale + '"' : ''; - var typeAttr = thing.type ? ' type="' + thing.type + '"' : ''; - var styleAttr = thing.style ? ' style="' + thing.style + '"' : ''; - var next1 = "{{#join ".concat(thing.name).concat(sepAttr).concat(localeAttr).concat(typeAttr).concat(styleAttr, "}}"); - var next2 = '{{/join}}'; - var result = [resultString(next1), children, resultString(next2)]; - resultSeq(parameters, result); -}; -// Container blocks -rules.ListBlockDefinition = (visitor, thing, children, parameters, resultString, resultSeq) => { - var listKind = thing.type === 'bullet' ? 'ulist' : 'olist'; - var prefix = CommonMarkUtils.mkPrefix(parameters, 1); - var next1 = prefix; - var next2 = "{{#".concat(listKind, " ").concat(thing.name, "}}\n"); - var next3 = prefix; - var next4 = "{{/".concat(listKind, "}}"); - var result = [resultString(next1), resultString(next2), children, resultString(next3), resultString(next4)]; - resultSeq(parameters, result); -}; -rules.ClauseDefinition = (visitor, thing, children, parameters, resultString, resultSeq) => { - var next1 = CommonMarkUtils.mkPrefix(parameters, 2); - var srcAttr = thing.src ? ' src="' + thing.src + '"' : ''; - var next2 = "{{#clause ".concat(thing.name).concat(srcAttr, "}}\n"); - var next3 = '\n{{/clause}}'; - var result = [resultString(next1), resultString(next2), children, resultString(next3)]; - resultSeq(parameters, result); -}; -module.exports = rules; \ No newline at end of file diff --git a/packages/markdown-template/lib/normalize.js b/packages/markdown-template/lib/normalize.js deleted file mode 100644 index ceebc2d3..00000000 --- a/packages/markdown-template/lib/normalize.js +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -var CiceroMarkTransformer = require('@accordproject/markdown-cicero').CiceroMarkTransformer; - -/** - * Prepare the text for parsing (normalizes new lines, etc) - * @param {string} input - the text - * @return {string} - the normalized text - */ -function normalizeNLs(input) { - // we replace all \r and \n with \n - var text = input.replace(/\r/gm, ''); - return text; -} - -/** - * Normalize to markdown cicero text - * @param {*} input - the CiceroMark DOM - * @return {string} - the normalized markdown cicero text - */ -function normalizeToMarkdownCicero(input) { - var ciceroMarkTransformer = new CiceroMarkTransformer(); - var result = ciceroMarkTransformer.toMarkdownCicero(input); - return result; -} - -/** - * Normalize from markdown cicero text - * @param {string} input - the markdown cicero text - * @return {object} - the normalized CiceroMark DOM - */ -function normalizeFromMarkdownCicero(input) { - // Normalizes new lines - var inputNLs = normalizeNLs(input); - // Roundtrip through the CommonMark parser - var ciceroMarkTransformer = new CiceroMarkTransformer(); - return ciceroMarkTransformer.fromMarkdownCicero(inputNLs); -} -module.exports.normalizeNLs = normalizeNLs; -module.exports.normalizeToMarkdownCicero = normalizeToMarkdownCicero; -module.exports.normalizeFromMarkdownCicero = normalizeFromMarkdownCicero; \ No newline at end of file diff --git a/packages/markdown-template/lib/templateexception.js b/packages/markdown-template/lib/templateexception.js deleted file mode 100644 index 676edb97..00000000 --- a/packages/markdown-template/lib/templateexception.js +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -var ParseException = require('@accordproject/concerto-cto').ParseException; - -/** - * Exception thrown for invalid templates - * @extends BaseFileException - * @see See {@link BaseFileException} - * @class - * @memberof module:markdown-template - * @private - */ -class TemplateException extends ParseException { - /** - * Create a TemplateException - * @param {string} message - the message for the exception - * @param {string} fileLocation - the optional file location associated with the exception - * @param {string} fileName - the optional file name associated with the exception - * @param {string} fullMessageOverride - the optional pre-existing full message - * @param {string} component - the optional component which throws this error - */ - constructor(message, fileLocation, fileName, fullMessageOverride, component) { - super(message, fileLocation, fileName, fullMessageOverride, component || 'cicero-core'); - } -} -module.exports = TemplateException; \ No newline at end of file diff --git a/packages/markdown-template/lib/templatemarkutil.js b/packages/markdown-template/lib/templatemarkutil.js deleted file mode 100644 index f8d01e4e..00000000 --- a/packages/markdown-template/lib/templatemarkutil.js +++ /dev/null @@ -1,250 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } -function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } -function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; } -function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; } -function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } -var dayjs = require('dayjs'); -var utc = require('dayjs/plugin/utc'); -dayjs.extend(utc); -var { - ModelManager, - Factory, - Serializer, - Introspector -} = require('@accordproject/concerto-core'); -var { - CommonMarkModel, - CiceroMarkModel, - ConcertoMetaModel, - TemplateMarkModel -} = require('@accordproject/markdown-common'); -var normalizeNLs = require('./normalize').normalizeNLs; -var TypeVisitor = require('./TypeVisitor'); -var FormulaVisitor = require('./FormulaVisitor'); -var MarkdownIt = require('markdown-it'); -var MarkdownItTemplate = require('@accordproject/markdown-it-template'); -var FromMarkdownIt = require('@accordproject/markdown-common').FromMarkdownIt; -var templaterules = require('./templaterules'); - -/** - * Model manager for TemplateMark - * @param {object} options - optional parameters - * @param {number} [options.utcOffset] - UTC Offset for this execution - * @returns {object} model manager and utilities for TemplateMark - */ -function mkTemplateMarkManager(options) { - var result = {}; - var newOpts = _objectSpread(_objectSpread({}, options), {}, { - strict: true - }); - result.modelManager = new ModelManager(newOpts); - result.modelManager.addCTOModel(CommonMarkModel.MODEL, 'commonmark.cto'); - result.modelManager.addCTOModel(ConcertoMetaModel.MODEL, 'metamodel.cto'); - result.modelManager.addCTOModel(CiceroMarkModel.MODEL, 'ciceromark.cto'); - result.modelManager.addCTOModel(TemplateMarkModel.MODEL, 'templatemark.cto'); - result.factory = new Factory(result.modelManager); - result.serializer = new Serializer(result.factory, result.modelManager, { - utcOffset: 0 - }); - return result; -} -var templateMarkManager = mkTemplateMarkManager(); - -/** - * Returns the concept for the template - * @param {object} introspector - the model introspector for this template - * @param {string} templateKind - either 'clause' or 'contract' - * @param {string} [conceptFullyQualifiedName] - the fully qualified name of the template concept - * @throws {Error} if no template model is found, or multiple template models are found - * @returns {object} the concept for the template - */ -function findTemplateConcept(introspector, templateKind, conceptFullyQualifiedName) { - if (conceptFullyQualifiedName) { - return introspector.getClassDeclaration(conceptFullyQualifiedName); - } else { - var templateModels = introspector.getClassDeclarations().filter(item => { - return !item.isAbstract() && item.getDecorator('template'); - }); - if (templateModels.length > 1) { - throw new Error('Found multiple concepts with @template decorator. The model for the template must contain a single concept with the @template decorator.'); - } else if (templateModels.length === 0) { - throw new Error('Failed to find a concept with the @template decorator. The model for the template must contain a single concept with the @template decoratpr.'); - } else { - return templateModels[0]; - } - } -} - -/** - * Returns the template model for a type - * @param {object} introspector - the model introspector for this template - * @param {string} elementType - the element type - * @throws {Error} if no template model is found, or multiple template models are found - * @returns {object} the template model for the template - */ -function findElementModel(introspector, elementType) { - return introspector.getClassDeclaration(elementType); -} - -/** - * Decorate TemplateMark DOM with its types - * @param {object} template the TemplateMark DOM - * @param {object} introspector - the introspector for this template - * @param {string} model - the model - * @param {string} templateKind - either 'clause' or 'contract' - * @param {object} options - optional parameters - * @param {number} [options.utcOffset] - UTC Offset for this execution - * @returns {object} the typed TemplateMark DOM - */ -function templateMarkTypingGen(template, introspector, model, templateKind, options) { - var input = templateMarkManager.serializer.fromJSON(template, options); - var parameters = { - templateMarkModelManager: templateMarkManager.modelManager, - introspector: introspector, - model: model, - kind: templateKind - }; - var visitor = new TypeVisitor(); - input.accept(visitor, parameters); - var result = Object.assign({}, templateMarkManager.serializer.toJSON(input, options)); - - // Calculates formula dependencies - var fvisitor = new FormulaVisitor(); - result = fvisitor.calculateDependencies(templateMarkManager.modelManager.serializer, result, options); - return result; -} - -/** - * Decorate TemplateMark DOM with its types - * @param {object} template the TemplateMark DOM - * @param {object} modelManager - the modelManager for this template - * @param {string} templateKind - either 'clause' or 'contract' - * @param {string} [conceptFullyQualifiedName] - the fully qualified name of the template concept - * @returns {object} the typed TemplateMark DOM - */ -function templateMarkTyping(template, modelManager, templateKind, conceptFullyQualifiedName) { - var introspector = new Introspector(modelManager); - var model = findTemplateConcept(introspector, templateKind, conceptFullyQualifiedName); - return templateMarkTypingGen(template, introspector, model, templateKind); -} - -/** - * Decorate TemplateMark DOM with its types - * @param {object} template the TemplateMark DOM - * @param {object} modelManager - the modelManager for this template - * @param {string} elementType - the element type - * @returns {object} the typed TemplateMark DOM - */ -function templateMarkTypingFromType(template, modelManager, elementType) { - var introspector = new Introspector(modelManager); - var model = findElementModel(introspector, elementType); - var rootNode = { - '$class': "".concat(CommonMarkModel.NAMESPACE, ".Document"), - 'xmlns': 'http://commonmark.org/xml/1.0', - 'nodes': [{ - '$class': "".concat(TemplateMarkModel.NAMESPACE, ".ContractDefinition"), - 'name': 'top', - 'nodes': template - }] - }; - var rootNodeTyped = templateMarkTypingGen(rootNode, introspector, model, 'clause'); - return rootNodeTyped.nodes[0].nodes; -} - -/** - * Converts a templatemark string to a token stream - * @param {object} input the templatemark string - * @returns {object} the token stream - */ -function templateToTokens(input) { - var norm = normalizeNLs(input); - var parser = new MarkdownIt({ - html: true - }).use(MarkdownItTemplate); - return parser.parse(norm, {}); -} - -/** - * Converts a template token strean string to an untyped TemplateMark DOM - * @param {object} tokenStream the template token stream - * @returns {object} the TemplateMark DOM - */ -function tokensToUntypedTemplateMarkGen(tokenStream) { - var fromMarkdownIt = new FromMarkdownIt(templaterules); - var partialTemplate = fromMarkdownIt.toCommonMark(tokenStream); - var result = templateMarkManager.serializer.toJSON(templateMarkManager.serializer.fromJSON(partialTemplate)); - return result.nodes; -} - -/** - * Converts a template token strean string to an untyped TemplateMark DOM - * @param {object} tokenStream the template token stream - * @param {string} templateKind - either 'clause' or 'contract' - * @returns {object} the TemplateMark DOM - */ -function tokensToUntypedTemplateMark(tokenStream, templateKind) { - var partialTemplate = tokensToUntypedTemplateMarkGen(tokenStream); - if (templateKind === 'contract') { - return { - '$class': "".concat(CommonMarkModel.NAMESPACE, ".Document"), - 'xmlns': 'http://commonmark.org/xml/1.0', - 'nodes': [{ - '$class': "".concat(TemplateMarkModel.NAMESPACE, ".ContractDefinition"), - 'name': 'top', - 'nodes': partialTemplate - }] - }; - } else { - return { - '$class': "".concat(CommonMarkModel.NAMESPACE, ".Document"), - 'xmlns': 'http://commonmark.org/xml/1.0', - 'nodes': [{ - '$class': "".concat(TemplateMarkModel.NAMESPACE, ".ClauseDefinition"), - 'name': 'top', - 'nodes': partialTemplate - }] - }; - } -} - -/** - * Converts a template token strean string to an untyped TemplateMark DOM - * @param {object} tokenStream the template token stream - * @param {string} templateKind - either 'clause' or 'contract' - * @returns {object} the TemplateMark DOM - */ -function tokensToUntypedTemplateMarkFragment(tokenStream) { - var partialTemplate = tokensToUntypedTemplateMarkGen(tokenStream); - return { - '$class': "".concat(CommonMarkModel.NAMESPACE, ".Document"), - 'xmlns': 'http://commonmark.org/xml/1.0', - 'nodes': [{ - '$class': "".concat(TemplateMarkModel.NAMESPACE, ".ClauseDefinition"), - 'name': 'top', - 'nodes': partialTemplate - }] - }; -} -module.exports.findTemplateConcept = findTemplateConcept; -module.exports.templateMarkManager = templateMarkManager; -module.exports.templateToTokens = templateToTokens; -module.exports.tokensToUntypedTemplateMarkFragment = tokensToUntypedTemplateMarkFragment; -module.exports.tokensToUntypedTemplateMark = tokensToUntypedTemplateMark; -module.exports.templateMarkTyping = templateMarkTyping; -module.exports.templateMarkTypingFromType = templateMarkTypingFromType; \ No newline at end of file diff --git a/packages/markdown-template/lib/templaterules.js b/packages/markdown-template/lib/templaterules.js deleted file mode 100644 index 8bfe6ced..00000000 --- a/packages/markdown-template/lib/templaterules.js +++ /dev/null @@ -1,276 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -var formulaName = require('./util').formulaName; -var { - getAttr -} = require('@accordproject/markdown-common').CommonMarkUtils; -var { - TemplateMarkModel -} = require('@accordproject/markdown-common'); - -// Inline rules -var variableRule = { - tag: "".concat(TemplateMarkModel.NAMESPACE, ".VariableDefinition"), - leaf: true, - open: false, - close: false, - enter: (node, token, callback) => { - var format = getAttr(token.attrs, 'format', null); - if (format) { - node.$class = "".concat(TemplateMarkModel.NAMESPACE, ".FormattedVariableDefinition"); - node.format = format; - } - node.name = getAttr(token.attrs, 'name', null); - node.format = getAttr(token.attrs, 'format', null); - }, - skipEmpty: false -}; -var thisRule = { - // 'this' is a special variable for the current data in scope within the template - tag: "".concat(TemplateMarkModel.NAMESPACE, ".VariableDefinition"), - leaf: true, - open: false, - close: false, - enter: (node, token, callback) => { - var format = getAttr(token.attrs, 'format', null); - if (format) { - node.$class = "".concat(TemplateMarkModel.NAMESPACE, ".FormattedVariableDefinition"); - node.format = format; - } - node.name = 'this'; - node.format = getAttr(token.attrs, 'format', null); - }, - skipEmpty: false -}; -var formulaRule = { - tag: "".concat(TemplateMarkModel.NAMESPACE, ".FormulaDefinition"), - leaf: true, - open: false, - close: false, - enter: (node, token, callback) => { - var code = token.content; - node.name = formulaName(code); - node.code = { - $class: "".concat(TemplateMarkModel.NAMESPACE, ".Code"), - type: 'TYPESCRIPT', - contents: code - }; - node.dependencies = []; - }, - skipEmpty: false -}; -var ifOpenRule = { - tag: "".concat(TemplateMarkModel.NAMESPACE, ".ConditionalDefinition"), - leaf: false, - open: true, - close: false, - enter: (node, token, callback) => { - node.name = getAttr(token.attrs, 'name', null); - var condition = getAttr(token.attrs, 'condition', null); - if (condition) { - node.condition = { - $class: "".concat(TemplateMarkModel.NAMESPACE, ".Code"), - type: 'TYPESCRIPT', - contents: condition - }; - } - node.whenTrue = null; - node.whenFalse = null; - }, - skipEmpty: false -}; -var ifCloseRule = { - tag: "".concat(TemplateMarkModel.NAMESPACE, ".ConditionalDefinition"), - leaf: false, - open: false, - close: true, - exit: (node, token, callback) => { - if (node.whenTrue) { - node.whenFalse = node.nodes ? node.nodes : []; - } else { - node.whenTrue = node.nodes ? node.nodes : []; - node.whenFalse = []; - } - delete node.nodes; // Delete children (now in whenTrue or whenFalse) - }, - skipEmpty: false -}; -var elseRule = { - tag: "".concat(TemplateMarkModel.NAMESPACE, ".ConditionalDefinition"), - leaf: false, - open: false, - close: false, - enter: (node, token, callback) => { - if (node.$class === "".concat(TemplateMarkModel.NAMESPACE, ".ConditionalDefinition")) { - node.whenTrue = node.nodes ? node.nodes : []; - node.nodes = []; // Reset children (now in whenTrue) - } else { - // Optional definition - node.whenSome = node.nodes ? node.nodes : []; - node.nodes = []; // Reset children (now in whenSome) - } - }, - skipEmpty: false -}; -var optionalOpenRule = { - tag: "".concat(TemplateMarkModel.NAMESPACE, ".OptionalDefinition"), - leaf: false, - open: true, - close: false, - enter: (node, token, callback) => { - node.name = getAttr(token.attrs, 'name', null); - node.whenSome = null; - node.whenNone = null; - }, - skipEmpty: false -}; -var optionalCloseRule = { - tag: "".concat(TemplateMarkModel.NAMESPACE, ".OptionalDefinition"), - leaf: false, - open: false, - close: true, - exit: (node, token, callback) => { - if (node.whenSome) { - node.whenNone = node.nodes ? node.nodes : []; - } else { - node.whenSome = node.nodes ? node.nodes : []; - node.whenNone = []; - } - delete node.nodes; // Delete children (now in whenSome or whenNone) - }, - skipEmpty: false -}; -var withOpenRule = { - tag: "".concat(TemplateMarkModel.NAMESPACE, ".WithDefinition"), - leaf: false, - open: true, - close: false, - enter: (node, token, callback) => { - node.name = getAttr(token.attrs, 'name', null); - }, - skipEmpty: false -}; -var withCloseRule = { - tag: "".concat(TemplateMarkModel.NAMESPACE, ".WithDefinition"), - leaf: false, - open: false, - close: true -}; -var joinOpenRule = { - tag: "".concat(TemplateMarkModel.NAMESPACE, ".JoinDefinition"), - leaf: false, - open: true, - close: false, - enter: (node, token, callback) => { - node.name = getAttr(token.attrs, 'name', null); - node.separator = getAttr(token.attrs, 'separator', null); - node.locale = getAttr(token.attrs, 'locale', null); - node.type = getAttr(token.attrs, 'type', null); - node.style = getAttr(token.attrs, 'style', null); - }, - skipEmpty: false -}; -var joinCloseRule = { - tag: "".concat(TemplateMarkModel.NAMESPACE, ".JoinDefinition"), - leaf: false, - open: false, - close: true -}; - -// Block rules -var clauseOpenRule = { - tag: "".concat(TemplateMarkModel.NAMESPACE, ".ClauseDefinition"), - leaf: false, - open: true, - close: false, - enter: (node, token, callback) => { - node.name = getAttr(token.attrs, 'name', null); - var condition = getAttr(token.attrs, 'condition', null); - if (condition) { - node.condition = { - $class: "".concat(TemplateMarkModel.NAMESPACE, ".Code"), - type: 'TYPESCRIPT', - contents: condition - }; - } - } -}; -var clauseCloseRule = { - tag: "".concat(TemplateMarkModel.NAMESPACE, ".ClauseDefinition"), - leaf: false, - open: false, - close: true -}; -var ulistOpenRule = { - tag: "".concat(TemplateMarkModel.NAMESPACE, ".ListBlockDefinition"), - leaf: false, - open: true, - close: false, - enter: (node, token, callback) => { - node.name = getAttr(token.attrs, 'name', null); - node.type = 'bullet'; - node.tight = 'true'; - } -}; -var ulistCloseRule = { - tag: "".concat(TemplateMarkModel.NAMESPACE, ".ListBlockDefinition"), - leaf: false, - open: false, - close: true -}; -var olistOpenRule = { - tag: "".concat(TemplateMarkModel.NAMESPACE, ".ListBlockDefinition"), - leaf: false, - open: true, - close: false, - enter: (node, token, callback) => { - node.name = getAttr(token.attrs, 'name', null); - node.type = 'ordered'; - node.tight = 'true'; - node.start = '1'; - node.delimiter = 'period'; - } -}; -var olistCloseRule = { - tag: "".concat(TemplateMarkModel.NAMESPACE, ".ListBlockDefinition"), - leaf: false, - open: false, - close: true -}; -var rules = { - inlines: {}, - blocks: {} -}; -rules.inlines.variable = variableRule; -rules.inlines.this = thisRule; -rules.inlines.formula = formulaRule; -rules.inlines.inline_block_if_open = ifOpenRule; -rules.inlines.inline_block_if_close = ifCloseRule; -rules.inlines.inline_block_optional_open = optionalOpenRule; -rules.inlines.inline_block_optional_close = optionalCloseRule; -rules.inlines.inline_block_else = elseRule; -rules.inlines.inline_block_with_open = withOpenRule; -rules.inlines.inline_block_with_close = withCloseRule; -rules.inlines.inline_block_join_open = joinOpenRule; -rules.inlines.inline_block_join_close = joinCloseRule; -rules.blocks.block_clause_open = clauseOpenRule; -rules.blocks.block_clause_close = clauseCloseRule; -rules.blocks.block_ulist_open = ulistOpenRule; -rules.blocks.block_ulist_close = ulistCloseRule; -rules.blocks.block_olist_open = olistOpenRule; -rules.blocks.block_olist_close = olistCloseRule; -module.exports = rules; \ No newline at end of file diff --git a/packages/markdown-template/lib/toparserrules.js b/packages/markdown-template/lib/toparserrules.js deleted file mode 100644 index 48dfdf2c..00000000 --- a/packages/markdown-template/lib/toparserrules.js +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -var CommonMarkUtils = require('@accordproject/markdown-common').CommonMarkUtils; - -// Basic parser constructors -var computedParser = require('./combinators').computedParser; -var enumParser = require('./combinators').enumParser; -var conditionalParser = require('./combinators').conditionalParser; -var optionalParser = require('./combinators').optionalParser; -var ulistBlockParser = require('./combinators').ulistBlockParser; -var olistBlockParser = require('./combinators').olistBlockParser; -var joinBlockParser = require('./combinators').joinBlockParser; -var withParser = require('./combinators').withParser; -var clauseParser = require('./combinators').clauseParser; -var wrappedClauseParser = require('./combinators').wrappedClauseParser; -var contractParser = require('./combinators').contractParser; -var mkVariable = require('./combinators').mkVariable; - -/** - * inContract - * @param {string[]} stack - the stack of ancestor nodes - * @return {boolean} are we inside a contract definition - */ -function inContract(stack) { - return stack.blocks.includes('ContractDefinition'); -} - -/* Those are additional rules to for the templatemark parser generation, complementing those for commonmark */ - -//const CommonMarkUtils = require('./CommonMarkUtils'); - -var rules = {}; - -// Inline blocks -rules.EnumVariableDefinition = (visitor, thing, children, parameters, resultString, resultSeq) => { - var result = r => enumParser(thing.enumValues).map(value => mkVariable(thing, value)); - resultSeq(parameters, result); -}; -rules.VariableDefinition = (visitor, thing, children, parameters, resultString, resultSeq) => { - var elementType = thing.identifiedBy ? 'Resource' : thing.elementType; - var format = thing.format ? thing.format : null; - var parserName = format ? elementType + '_' + format : elementType; - var result; - if (parameters.templateParser[parserName]) { - result = r => { - return r[parserName].map(value => mkVariable(thing, value)); - }; - } else { - var fragmentParameters = {}; - fragmentParameters.parserManager = parameters.parserManager; - fragmentParameters.parsingTable = parameters.parsingTable; - fragmentParameters.templateParser = parameters.templateParser; - fragmentParameters.result = resultString(''); - fragmentParameters.first = parameters.first; - fragmentParameters.stack = parameters.stack; - var parsingFun = parameters.parsingTable.getParser(thing.name, elementType, format, fragmentParameters); - parameters.templateParser[parserName] = parsingFun; - result = r => { - try { - return r[parserName].map(value => mkVariable(thing, value)); - } catch (err) { - console.log('ERROR HANDLING VARIABLE ' + elementType); - throw err; - } - }; - } - resultSeq(parameters, result); -}; -rules.FormattedVariableDefinition = rules.VariableDefinition; -rules.ConditionalDefinition = (visitor, thing, children, parameters, resultString, resultSeq) => { - var whenTrueParser = visitor.visitChildren(visitor, thing, parameters, 'whenTrue'); - var whenFalseParser = visitor.visitChildren(visitor, thing, parameters, 'whenFalse'); - var result = r => conditionalParser(thing, whenTrueParser(r), whenFalseParser(r)); - resultSeq(parameters, result); -}; -rules.OptionalDefinition = (visitor, thing, children, parameters, resultString, resultSeq) => { - var whenSomeParser = visitor.visitChildren(visitor, thing, parameters, 'whenSome'); - var whenNoneParser = visitor.visitChildren(visitor, thing, parameters, 'whenNone'); - var result = r => optionalParser(thing, whenSomeParser(r), whenNoneParser(r)); - resultSeq(parameters, result); -}; -rules.JoinDefinition = (visitor, thing, children, parameters, resultString, resultSeq) => { - var result = r => joinBlockParser(thing, children(r)); - resultSeq(parameters, result); -}; -rules.WithDefinition = (visitor, thing, children, parameters, resultString, resultSeq) => { - var result = r => withParser(thing.elementType, children(r)).map(value => mkVariable(thing, value)); - resultSeq(parameters, result); -}; -rules.FormulaDefinition = (visitor, thing, children, parameters, resultString, resultSeq) => { - var result = r => computedParser(thing.value); - resultSeq(parameters, result); -}; - -// Container blocks -rules.ListBlockDefinition = (visitor, thing, children, parameters, resultString, resultSeq) => { - // const level = parameters.tight && parameters.tight === 'false' && parameters.index !== parameters.indexInit ? 2 : 1; - var level = 1; - var prefix = CommonMarkUtils.mkPrefix(parameters, level); - var result = thing.type === 'bullet' ? r => ulistBlockParser(thing, children(r), prefix) : r => olistBlockParser(thing, children(r), prefix); - resultSeq(parameters, result); -}; -rules.ClauseDefinition = (visitor, thing, children, parameters, resultString, resultSeq) => { - var result; - if (inContract(parameters.stack)) { - result = r => wrappedClauseParser(thing, children(r)); - } else { - result = r => clauseParser(thing, children(r)); - } - resultSeq(parameters, result); -}; -rules.ContractDefinition = (visitor, thing, children, parameters, resultString, resultSeq) => { - var result = r => contractParser(thing, children(r)); - resultSeq(parameters, result); -}; -module.exports = rules; \ No newline at end of file diff --git a/packages/markdown-template/lib/util.js b/packages/markdown-template/lib/util.js deleted file mode 100644 index fa1f495c..00000000 --- a/packages/markdown-template/lib/util.js +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -var crypto = require('crypto'); - -/** - * Flatten an array of array - * @param {*[]} arr the input array - * @return {*[]} the flattened array - */ -function flatten(arr) { - return arr.reduce((acc, val) => acc.concat(val), []); -} - -/** - * Returns a unique chosen name for a formula - * @param {string} code - the formula code - * @return {string} the unique name - */ -function formulaName(code) { - var hasher = crypto.createHash('sha256'); - hasher.update(code); - return 'formula_' + hasher.digest('hex'); -} -module.exports.flatten = flatten; -module.exports.formulaName = formulaName; \ No newline at end of file diff --git a/packages/markdown-template/package.json b/packages/markdown-template/package.json index b5afdab9..5d1a2969 100644 --- a/packages/markdown-template/package.json +++ b/packages/markdown-template/package.json @@ -1,6 +1,6 @@ { "name": "@accordproject/markdown-template", - "version": "0.16.25", + "version": "1.0.0", "description": "A framework for transforming markdown", "engines": { "node": ">=22", @@ -10,31 +10,27 @@ "access": "public" }, "files": [ - "bin", "lib", - "types", "umd" ], - "main": "index.js", + "main": "lib/index.js", "browser": "umd/markdown-template.js", + "types": "lib/index.d.ts", + "typings": "lib/index.d.ts", "scripts": { "webpack": "webpack --config webpack.config.js --mode production", - "build": "babel src -d lib --copy-files && npm run build:types", - "build:types": "tsc", - "build:dist": "NODE_ENV=production babel src -d lib --copy-files", - "build:watch": "babel src -d lib --copy-files --watch", - "prepublishOnly": "npm run build:dist && npm run webpack", - "prepare": "npm run build", + "build": "tsc -p tsconfig.json", + "build:dist": "npm run build && npm run webpack", + "prepublishOnly": "npm run build:dist", "pretest": "npm run lint && npm run build", - "lint": "eslint .", + "lint": "eslint . --ext .ts", "postlint": "npm run licchk", "licchk": "license-check-and-add", - "test": "mocha --timeout 30000", - "test:cov": "npm run lint && nyc mocha --timeout 30000", - "jsdoc": "jsdoc -c jsdoc.json package.json", - "typescript": "jsdoc -t node_modules/tsd-jsdoc/dist -r ./src/" + "test": "jest --silent", + "test:noisy": "jest", + "test:cov": "npm run lint && npm run build && jest --coverage --silent", + "clean": "rimraf lib umd" }, - "typings": "types/index.d.ts", "repository": { "type": "git", "url": "git+https://github.com/accordproject/markdown-transform.git", @@ -55,27 +51,22 @@ "devDependencies": { "@accordproject/concerto-core": "^4.1.3", "@accordproject/concerto-cto": "^4.1.3", - "@babel/cli": "7.25.9", - "@babel/core": "7.26.0", - "@babel/preset-env": "7.26.0", - "babel-loader": "9.2.1", - "babel-plugin-istanbul": "7.0.0", - "chai": "4.3.6", - "chai-as-promised": "7.1.1", - "chai-string": "^1.5.0", - "chai-things": "0.2.0", + "@types/jest": "^29.5.12", + "@types/markdown-it": "^14.1.2", + "@types/node": "^20.11.30", + "@typescript-eslint/eslint-plugin": "^7.4.0", + "@typescript-eslint/parser": "^7.4.0", "crypto-browserify": "3.12.1", "eslint": "8.57.1", - "jsdoc": "^4.0.4", + "jest": "^29.7.0", "license-check-and-add": "2.3.6", - "mocha": "10.8.2", - "nyc": "17.1.0", - "raw-loader": "^4.0.2", + "rimraf": "^5.0.5", "stream-browserify": "3.0.0", - "tsd-jsdoc": "^2.5.0", + "ts-jest": "^29.1.2", + "ts-loader": "^9.5.1", + "typescript": "^5.9.3", "webpack": "5.104.1", - "webpack-cli": "5.1.4", - "typescript": "^5.9.3" + "webpack-cli": "5.1.4" }, "peerDependencies": { "@accordproject/concerto-core": "^4.1.3", @@ -86,17 +77,16 @@ "@accordproject/markdown-common": "*", "@accordproject/markdown-it-template": "*", "dayjs": "1.11.13", - "markdown-it": "^14.1.0" + "markdown-it": "^14.1.0", + "process": "^0.11.10" }, "license-check-and-add-config": { - "folder": "./lib", + "folder": "./src", "license": "header.txt", "exact_paths_method": "EXCLUDE", "exact_paths": [ - "externalModels/.npmignore", - "externalModels/.gitignore", "coverage", - "index.d.ts", + "__snapshots__", "./system", "LICENSE", "node_modules", @@ -114,7 +104,7 @@ ], "insert_license": false, "license_formats": { - "js|njk|pegjs|cto|acl|qry": { + "ts|tsx|js|njk|pegjs|cto|acl|qry": { "prepend": "/*", "append": " */", "eachLine": { @@ -130,28 +120,5 @@ "file": "header.md" } } - }, - "nyc": { - "produce-source-map": "true", - "sourceMap": "inline", - "reporter": [ - "lcov", - "text", - "text-summary", - "html", - "json" - ], - "include": [ - "lib/**/*.js" - ], - "exclude": [ - "scripts/**/*.js" - ], - "all": true, - "check-coverage": false, - "statements": 87, - "branches": 76, - "functions": 84, - "lines": 87 } } diff --git a/packages/markdown-template/src/FormulaVisitor.js b/packages/markdown-template/src/FormulaVisitor.js deleted file mode 100644 index 7684a2df..00000000 --- a/packages/markdown-template/src/FormulaVisitor.js +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -/** - * Converts a CommonMark DOM to a CiceroMark DOM - */ -class FormulaVisitor { - - /** - * Visits a sub-tree and return CiceroMark DOM - * @param {*} visitor the visitor to use - * @param {*} thing the node to visit - * @param {*} [parameters] optional parameters - * @param {string} field where the children are - */ - static visitChildren(visitor, thing, parameters, field = 'nodes') { - if(thing[field]) { - FormulaVisitor.visitNodes(visitor, thing[field], parameters); - } - } - - /** - * Visits a list of nodes and return the CiceroMark DOM - * @param {*} visitor the visitor to use - * @param {*} things the list node to visit - * @param {*} [parameters] optional parameters - */ - static visitNodes(visitor, things, parameters) { - things.forEach(node => { - node.accept(visitor, parameters); - }); - } - - /** - * Calculates the dependencies for TS code - * @param {string} tsCode the TS code to analyze - * @returns {string[]} array of dependencies - */ - static calculateDependencies(tsCode) { - try { - const deps = []; - // TODO!! - return deps; - } - catch(err) { - throw new Error(`Failed to calculate dependencies in code '${tsCode}'. Error: ${err}`); - } - } - - /** - * Visit a node - * @param {*} thing the object being visited - * @param {*} parameters the parameters - */ - visit(thing, parameters) { - switch(thing.getType()) { - case 'ConditionalDefinition': - { - if (parameters.calculateDependencies) { - if(thing.condition) { - thing.dependencies = FormulaVisitor.calculateDependencies(thing.condition.contents); - } - } else { - parameters.result.push({ name : thing.name, code: thing.condition }); - } - } - break; - case 'FormulaDefinition': { - if (parameters.calculateDependencies) { - if(thing.code) { - thing.dependencies = FormulaVisitor.calculateDependencies(thing.code.contents); - } - } else { - parameters.result.push({ name : thing.name, code: thing.code }); - } - } - break; - default: - FormulaVisitor.visitChildren(this, thing, parameters); - } - } - - /** - * Calculate dependencies - * @param {*} serializer - the template mark serializer - * @param {object} ast - the template AST - * @param {object} options - options - * @param {number} [options.utcOffset] - UTC Offset for this execution - * @returns {*} the formulas - */ - calculateDependencies(serializer,ast,options) { - const parameters = { - calculateDependencies: true, - variables: [], - result: [] - }; - const input = serializer.fromJSON(ast,options); - input.accept(this, parameters); - return serializer.toJSON(input,options); - } - - /** - * Process formulas and returns the list of those formulas from a TemplateMark DOM - * @param {*} serializer - the template mark serializer - * @param {object} ast - the template AST - * @param {object} options - options - * @param {number} [options.utcOffset] - UTC Offset for this execution - * @returns {*} the formulas - */ - processFormulas(serializer,ast,options) { - const parameters = { - calculateDependencies: false, - variables: [], - result: [], - }; - const input = serializer.fromJSON(ast,options); - input.accept(this, parameters); - return parameters.result; - } -} - -module.exports = FormulaVisitor; \ No newline at end of file diff --git a/packages/markdown-template/src/FormulaVisitor.ts b/packages/markdown-template/src/FormulaVisitor.ts new file mode 100644 index 00000000..7731bb1f --- /dev/null +++ b/packages/markdown-template/src/FormulaVisitor.ts @@ -0,0 +1,89 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Walks a TemplateMark DOM to compute or extract formula dependencies + */ +export class FormulaVisitor { + static visitChildren(visitor: FormulaVisitor, thing: any, parameters: any, field = 'nodes'): void { + if (thing[field]) { + FormulaVisitor.visitNodes(visitor, thing[field], parameters); + } + } + + static visitNodes(visitor: FormulaVisitor, things: any[], parameters: any): void { + things.forEach((node) => { + node.accept(visitor, parameters); + }); + } + + static calculateDependencies(tsCode: string): string[] { + try { + const deps: string[] = []; + // TODO!! + return deps; + } catch (err) { + throw new Error(`Failed to calculate dependencies in code '${tsCode}'. Error: ${err}`); + } + } + + visit(thing: any, parameters: any): void { + switch (thing.getType()) { + case 'ConditionalDefinition': + if (parameters.calculateDependencies) { + if (thing.condition) { + thing.dependencies = FormulaVisitor.calculateDependencies(thing.condition.contents); + } + } else { + parameters.result.push({ name: thing.name, code: thing.condition }); + } + break; + case 'FormulaDefinition': + if (parameters.calculateDependencies) { + if (thing.code) { + thing.dependencies = FormulaVisitor.calculateDependencies(thing.code.contents); + } + } else { + parameters.result.push({ name: thing.name, code: thing.code }); + } + break; + default: + FormulaVisitor.visitChildren(this, thing, parameters); + } + } + + calculateDependencies(serializer: any, ast: any, options?: any): any { + const parameters = { + calculateDependencies: true, + variables: [], + result: [], + }; + const input = serializer.fromJSON(ast, options); + input.accept(this, parameters); + return serializer.toJSON(input, options); + } + + processFormulas(serializer: any, ast: any, options?: any): any[] { + const parameters: any = { + calculateDependencies: false, + variables: [], + result: [], + }; + const input = serializer.fromJSON(ast, options); + input.accept(this, parameters); + return parameters.result; + } +} + +export default FormulaVisitor; diff --git a/packages/markdown-template/src/ModelVisitor.js b/packages/markdown-template/src/ModelVisitor.ts similarity index 57% rename from packages/markdown-template/src/ModelVisitor.js rename to packages/markdown-template/src/ModelVisitor.ts index afa9380e..40dae352 100644 --- a/packages/markdown-template/src/ModelVisitor.js +++ b/packages/markdown-template/src/ModelVisitor.ts @@ -12,25 +12,13 @@ * limitations under the License. */ -'use strict'; - -const { CommonMarkModel, TemplateMarkModel } = require('@accordproject/markdown-common'); +import { CommonMarkModel, TemplateMarkModel } from '@accordproject/markdown-common'; /** * Converts concerto models to TemplateMark - * - * @private - * @class */ -class ModelVisitor { - /** - * Visitor design pattern - * @param {Object} thing - the object being visited - * @param {Object} parameters - the parameter - * @return {Object} the result of visiting or null - * @private - */ - visit(thing, parameters) { +export class ModelVisitor { + visit(thing: any, parameters: any): any { if (thing.isEnum?.()) { return this.visitEnumDeclaration(thing, parameters); } else if (thing.isClassDeclaration?.()) { @@ -46,37 +34,23 @@ class ModelVisitor { } } - /** - * Visitor design pattern - * @param {EnumDeclaration} enumDeclaration - the object being visited - * @param {Object} parameters - the parameter - * @return {Object} the result of visiting or null - * @private - */ - visitEnumDeclaration(enumDeclaration, parameters) { - let result = {}; + visitEnumDeclaration(_enumDeclaration: any, parameters: any): any { + const result: any = {}; result.$class = `${TemplateMarkModel.NAMESPACE}.EnumVariableDefinition`; result.name = parameters.type; return result; } - /** - * Visitor design pattern - * @param {ClassDeclaration} classDeclaration - the object being visited - * @param {Object} parameters - the parameter - * @return {Object} the result of visiting or null - * @private - */ - visitClassDeclaration(classDeclaration, parameters) { - let result = {}; + visitClassDeclaration(classDeclaration: any, parameters: any): any { + const result: any = {}; result.$class = `${TemplateMarkModel.NAMESPACE}.WithDefinition`; result.name = parameters.name; result.nodes = []; let first = true; - classDeclaration.getProperties().forEach((property,index) => { + classDeclaration.getProperties().forEach((property: any) => { if (!first) { - let textNode = {}; + const textNode: any = {}; textNode.$class = `${CommonMarkModel.NAMESPACE}.Text`; textNode.text = ' '; result.nodes.push(textNode); @@ -88,35 +62,28 @@ class ModelVisitor { return result; } - /** - * Visitor design pattern - * @param {Field} field - the object being visited - * @param {Object} parameters - the parameter - * @return {Object} the result of visiting or null - * @private - */ - visitField(field, parameters) { + visitField(field: any, _parameters: any): any { const fieldName = field.getName(); - let result = {}; + let result: any = {}; result.$class = `${TemplateMarkModel.NAMESPACE}.VariableDefinition`; result.name = fieldName; - if(field.isArray()) { + if (field.isArray()) { if (field.isPrimitive()) { result.name = 'this'; } - const arrayResult = {}; + const arrayResult: any = {}; arrayResult.$class = `${TemplateMarkModel.NAMESPACE}.JoinDefinition`; - arrayResult.separator = ' '; // XXX {{#join }} + arrayResult.separator = ' '; arrayResult.name = fieldName; arrayResult.nodes = [result]; result = arrayResult; } - if(field.isOptional()) { + if (field.isOptional()) { if (field.isPrimitive()) { result.name = 'this'; } - const optionalResult = {}; + const optionalResult: any = {}; optionalResult.$class = `${TemplateMarkModel.NAMESPACE}.OptionalDefinition`; optionalResult.name = fieldName; optionalResult.whenSome = [result]; @@ -127,25 +94,17 @@ class ModelVisitor { return result; } - /** - * Visitor design pattern - * @param {EnumValueDeclaration} enumValueDeclaration - the object being visited - * @param {Object} parameters - the parameter - * @private - */ - visitEnumValueDeclaration(enumValueDeclaration, parameters) { + visitEnumValueDeclaration(_enumValueDeclaration: any, _parameters: any): never { throw new Error('visitEnumValueDeclaration not handled'); } - /** - * Visitor design pattern - * @param {Relationship} relationship - the object being visited - * @param {Object} parameters - the parameter - * @private - */ - visitRelationshipDeclaration(relationship, parameters) { + visitRelationship(_relationship: any, _parameters: any): never { + throw new Error('visitRelationship not handled'); + } + + visitRelationshipDeclaration(_relationship: any, _parameters: any): never { throw new Error('visitRelationshipDeclaration'); } } -module.exports = ModelVisitor; +export default ModelVisitor; diff --git a/packages/markdown-template/test/TemplateMarkTransformer.js b/packages/markdown-template/src/TemplateMarkTransformer.test.ts similarity index 57% rename from packages/markdown-template/test/TemplateMarkTransformer.js rename to packages/markdown-template/src/TemplateMarkTransformer.test.ts index e8d8cc66..5e6aaed4 100644 --- a/packages/markdown-template/test/TemplateMarkTransformer.js +++ b/packages/markdown-template/src/TemplateMarkTransformer.test.ts @@ -12,19 +12,9 @@ * limitations under the License. */ -'use strict'; - -const chai = require('chai'); -chai.use(require('chai-string')); - -chai.should(); -chai.use(require('chai-things')); -chai.use(require('chai-as-promised')); - -const { ModelManager } = require('@accordproject/concerto-core'); -const { TemplateMarkModel } = require('@accordproject/markdown-common'); - -const TemplateMarkTransformer = require('../lib/TemplateMarkTransformer'); +import { ModelManager } from '@accordproject/concerto-core'; +import { TemplateMarkModel } from '@accordproject/markdown-common'; +import { TemplateMarkTransformer } from './TemplateMarkTransformer'; const MODEL = ` namespace test@1.0.0 @@ -35,61 +25,61 @@ concept Thing { describe('#TemplateMarkTransformer', () => { describe('#tokensToMarkdownTemplate', () => { - it('should handle join with type, style and locale', async () => { + it('should handle join with type, style and locale', () => { const transformer = new TemplateMarkTransformer(); const modelManager = new ModelManager(); modelManager.addCTOModel(MODEL); - const tokens = transformer.toTokens({content: '{{#join items type="conjunction" style="long" locale="en"}}{{/join}}'}); + const tokens = transformer.toTokens({ content: '{{#join items type="conjunction" style="long" locale="en"}}{{/join}}' }); const result = transformer.tokensToMarkdownTemplate(tokens, modelManager, 'clause'); const joinNode = result.nodes[0].nodes[0].nodes[0]; - joinNode.$class.should.equal(`${TemplateMarkModel.NAMESPACE}.JoinDefinition`); - joinNode.locale.should.equal('en'); - joinNode.type.should.equal('conjunction'); - joinNode.style.should.equal('long'); + expect(joinNode.$class).toBe(`${TemplateMarkModel.NAMESPACE}.JoinDefinition`); + expect(joinNode.locale).toBe('en'); + expect(joinNode.type).toBe('conjunction'); + expect(joinNode.style).toBe('long'); }); - it('should handle join with type, style', async () => { + it('should handle join with type, style', () => { const transformer = new TemplateMarkTransformer(); const modelManager = new ModelManager(); modelManager.addCTOModel(MODEL); - const tokens = transformer.toTokens({content: '{{#join items type="conjunction" style="long"}}{{/join}}'}); + const tokens = transformer.toTokens({ content: '{{#join items type="conjunction" style="long"}}{{/join}}' }); const result = transformer.tokensToMarkdownTemplate(tokens, modelManager, 'clause'); const joinNode = result.nodes[0].nodes[0].nodes[0]; - joinNode.$class.should.equal(`${TemplateMarkModel.NAMESPACE}.JoinDefinition`); - joinNode.type.should.equal('conjunction'); - joinNode.style.should.equal('long'); + expect(joinNode.$class).toBe(`${TemplateMarkModel.NAMESPACE}.JoinDefinition`); + expect(joinNode.type).toBe('conjunction'); + expect(joinNode.style).toBe('long'); }); - it('should handle join with type', async () => { + it('should handle join with type', () => { const transformer = new TemplateMarkTransformer(); const modelManager = new ModelManager(); modelManager.addCTOModel(MODEL); - const tokens = transformer.toTokens({content: '{{#join items type="conjunction"}}{{/join}}'}); + const tokens = transformer.toTokens({ content: '{{#join items type="conjunction"}}{{/join}}' }); const result = transformer.tokensToMarkdownTemplate(tokens, modelManager, 'clause'); const joinNode = result.nodes[0].nodes[0].nodes[0]; - joinNode.$class.should.equal(`${TemplateMarkModel.NAMESPACE}.JoinDefinition`); - joinNode.type.should.equal('conjunction'); + expect(joinNode.$class).toBe(`${TemplateMarkModel.NAMESPACE}.JoinDefinition`); + expect(joinNode.type).toBe('conjunction'); }); - it('should handle join', async () => { + it('should handle join', () => { const transformer = new TemplateMarkTransformer(); const modelManager = new ModelManager(); modelManager.addCTOModel(MODEL); - const tokens = transformer.toTokens({content: '{{#join items}}{{/join}}'}); + const tokens = transformer.toTokens({ content: '{{#join items}}{{/join}}' }); const result = transformer.tokensToMarkdownTemplate(tokens, modelManager, 'clause'); const joinNode = result.nodes[0].nodes[0].nodes[0]; - joinNode.$class.should.equal(`${TemplateMarkModel.NAMESPACE}.JoinDefinition`); + expect(joinNode.$class).toBe(`${TemplateMarkModel.NAMESPACE}.JoinDefinition`); }); - it('should ignore unknown attributes on join', async () => { + it('should ignore unknown attributes on join', () => { const transformer = new TemplateMarkTransformer(); const modelManager = new ModelManager(); modelManager.addCTOModel(MODEL); - const tokens = transformer.toTokens({content: '{{#join items foo="bar"}}{{/join}}'}); + const tokens = transformer.toTokens({ content: '{{#join items foo="bar"}}{{/join}}' }); const result = transformer.tokensToMarkdownTemplate(tokens, modelManager, 'clause'); const joinNode = result.nodes[0].nodes[0].nodes[0]; - joinNode.$class.should.equal(`${TemplateMarkModel.NAMESPACE}.JoinDefinition`); - (joinNode.foo === undefined).should.be.true; + expect(joinNode.$class).toBe(`${TemplateMarkModel.NAMESPACE}.JoinDefinition`); + expect(joinNode.foo).toBeUndefined(); }); }); }); diff --git a/packages/markdown-template/src/TemplateMarkTransformer.js b/packages/markdown-template/src/TemplateMarkTransformer.ts similarity index 50% rename from packages/markdown-template/src/TemplateMarkTransformer.js rename to packages/markdown-template/src/TemplateMarkTransformer.ts index e7cbc35f..36f06e5e 100644 --- a/packages/markdown-template/src/TemplateMarkTransformer.js +++ b/packages/markdown-template/src/TemplateMarkTransformer.ts @@ -12,67 +12,66 @@ * limitations under the License. */ -'use strict'; +import { Serializer, ModelManager } from '@accordproject/concerto-core'; -/** @typedef {import('@accordproject/concerto-core').Serializer} Serializer */ - -const { +import { templateMarkManager, templateToTokens, tokensToUntypedTemplateMark, templateMarkTyping, -} = require('./templatemarkutil'); +} from './templatemarkutil'; + +import { ToMarkdownTemplateVisitor } from './ToMarkdownTemplateVisitor'; -const ToMarkdownTemplateVisitor = require('./ToMarkdownTemplateVisitor'); +export interface TemplateInput { + fileName?: string; + content: string; +} /** * Support for TemplateMark Templates */ -class TemplateMarkTransformer { +export class TemplateMarkTransformer { /** * Converts a template string to a token stream - * @param {object} templateInput the template template - * @returns {object} the token stream */ - toTokens(templateInput) { + toTokens(templateInput: TemplateInput): any[] { return templateToTokens(templateInput.content); } /** - * Converts a template token strean string to a TemplateMark DOM - * @param {object} tokenStream the template token stream - * @param {object} modelManager - the model manager for this template - * @param {string} templateKind - either 'clause' or 'contract' - * @param {object} [options] configuration options - * @param {boolean} [options.verbose] verbose output - * @param {string} [conceptFullyQualifiedName] - the fully qualified name of the template concept - * @returns {object} the result of parsing + * Converts a template token stream string to a TemplateMark DOM */ - tokensToMarkdownTemplate(tokenStream, modelManager, templateKind, options, conceptFullyQualifiedName) { + tokensToMarkdownTemplate( + tokenStream: any[], + modelManager: ModelManager, + templateKind: string, + options?: { verbose?: boolean }, + conceptFullyQualifiedName?: string, + ): any { const template = tokensToUntypedTemplateMark(tokenStream, templateKind); if (options && options.verbose) { console.log('===== Untyped TemplateMark '); - console.log(JSON.stringify(template,null,2)); + console.log(JSON.stringify(template, null, 2)); } const typedTemplate = templateMarkTyping(template, modelManager, templateKind, conceptFullyQualifiedName); if (options && options.verbose) { console.log('===== TemplateMark '); - console.log(JSON.stringify(typedTemplate,null,2)); + console.log(JSON.stringify(typedTemplate, null, 2)); } return typedTemplate; } /** * Converts a markdown string to a TemplateMark DOM - * @param {{fileName:string,content:string}} templateInput the template template - * @param {object} modelManager - the model manager for this template - * @param {string} templateKind - either 'clause' or 'contract' - * @param {object} [options] configuration options - * @param {boolean} [options.verbose] verbose output - * @param {string} [conceptFullyQualifiedName] - the fully qualified name of the template concept - * @returns {object} the result of parsing */ - fromMarkdownTemplate(templateInput, modelManager, templateKind, options, conceptFullyQualifiedName) { + fromMarkdownTemplate( + templateInput: TemplateInput, + modelManager: ModelManager, + templateKind: string, + options?: { verbose?: boolean }, + conceptFullyQualifiedName?: string, + ): any { if (!modelManager) { throw new Error('Cannot parse without template model'); } @@ -80,28 +79,25 @@ class TemplateMarkTransformer { const tokenStream = this.toTokens(templateInput); if (options && options.verbose) { console.log('===== MarkdownIt Tokens '); - console.log(JSON.stringify(tokenStream,null,2)); + console.log(JSON.stringify(tokenStream, null, 2)); } return this.tokensToMarkdownTemplate(tokenStream, modelManager, templateKind, options, conceptFullyQualifiedName); } /** * Converts a TemplateMark DOM to a template markdown string - * @param {object} input TemplateMark DOM - * @returns {string} the template markdown text */ - toMarkdownTemplate(input) { + toMarkdownTemplate(input: any): string { const visitor = new ToMarkdownTemplateVisitor(); - return visitor.toMarkdownTemplate(templateMarkManager.serializer,input); + return visitor.toMarkdownTemplate(templateMarkManager.serializer, input); } /** * Get TemplateMark serializer - * @return {Serializer} templatemark serializer */ - getSerializer() { + getSerializer(): Serializer { return templateMarkManager.serializer; } } -module.exports = TemplateMarkTransformer; +export default TemplateMarkTransformer; diff --git a/packages/markdown-template/src/ToMarkdownTemplateVisitor.js b/packages/markdown-template/src/ToMarkdownTemplateVisitor.js deleted file mode 100644 index 44aace10..00000000 --- a/packages/markdown-template/src/ToMarkdownTemplateVisitor.js +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -const {CommonMarkUtils,CommonMarkModel} = require('@accordproject/markdown-common'); -const FromCommonMarkVisitor = require('@accordproject/markdown-common').FromCommonMarkVisitor; -const fromcommonmarkrules = require('@accordproject/markdown-common').fromcommonmarkrules; -const fromtemplatemarkrules = require('./fromtemplatemarkrules'); - -/** - * Fixes up the root note, removing Clause or Contract indication - * @param {object} input the input templatemark - * @return {object} the fixed up templatemark - */ -function fixupRootNode(input) { - const rootNode = { - '$class': `${CommonMarkModel.NAMESPACE}.Document`, - 'xmlns' : 'http://commonmark.org/xml/1.0', - 'nodes': input.nodes[0].nodes - }; - return rootNode; -} - -/** - * Converts a TemplateMark DOM to a template markdown string. - */ -class ToMarkdownTemplateVisitor extends FromCommonMarkVisitor { - /** - * Construct the visitor. - * @param {object} [options] configuration options - * @param {*} resultSeq how to sequentially combine results - * @param {object} rules how to process each node type - */ - constructor(options) { - const resultString = (result) => { - return result; - }; - const resultSeq = (parameters,result) => { - result.forEach((next) => { - parameters.result += next; - }); - }; - const setFirst = (thingType) => { - return thingType === 'Item' || thingType === 'ClauseDefinition' || thingType === 'ListBlockDefinition' ? true : false; - }; - const rules = fromcommonmarkrules; - Object.assign(rules,fromtemplatemarkrules); - super(options,resultString,resultSeq,rules,setFirst); - } - - /** - * Converts a TemplateMark DOM to a template markdown string. - * @param {*} serializer - TemplateMark serializer - * @param {*} input - TemplateMark DOM (JSON) - * @returns {string} the template markdown string - */ - toMarkdownTemplate(serializer,input) { - const parameters = {}; - const fixedInput = serializer.fromJSON(fixupRootNode(input)); - parameters.result = this.resultString(''); - parameters.stack = CommonMarkUtils.blocksInit(); - fixedInput.accept(this, parameters); - return parameters.result.trim(); - } -} - -module.exports = ToMarkdownTemplateVisitor; \ No newline at end of file diff --git a/packages/markdown-template/src/ToMarkdownTemplateVisitor.ts b/packages/markdown-template/src/ToMarkdownTemplateVisitor.ts new file mode 100644 index 00000000..956e072c --- /dev/null +++ b/packages/markdown-template/src/ToMarkdownTemplateVisitor.ts @@ -0,0 +1,59 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { CommonMarkUtils, CommonMarkModel, FromCommonMarkVisitor, fromcommonmarkrules } from '@accordproject/markdown-common'; +import fromtemplatemarkrules from './fromtemplatemarkrules'; + +/** + * Fixes up the root note, removing Clause or Contract indication + */ +function fixupRootNode(input: any): any { + return { + '$class': `${CommonMarkModel.NAMESPACE}.Document`, + 'xmlns': 'http://commonmark.org/xml/1.0', + 'nodes': input.nodes[0].nodes, + }; +} + +/** + * Converts a TemplateMark DOM to a template markdown string. + */ +export class ToMarkdownTemplateVisitor extends FromCommonMarkVisitor { + constructor(options?: any) { + const resultString = (result: string) => result; + const resultSeq = (parameters: any, result: any[]) => { + result.forEach((next) => { + parameters.result += next; + }); + }; + const setFirst = (thingType: string) => thingType === 'Item' || thingType === 'ClauseDefinition' || thingType === 'ListBlockDefinition'; + const rules = fromcommonmarkrules; + Object.assign(rules, fromtemplatemarkrules); + super(options, resultString, resultSeq, rules, setFirst); + } + + /** + * Converts a TemplateMark DOM to a template markdown string. + */ + toMarkdownTemplate(serializer: any, input: any): string { + const parameters: any = {}; + const fixedInput = serializer.fromJSON(fixupRootNode(input)); + parameters.result = this.resultString(''); + parameters.stack = CommonMarkUtils.blocksInit(); + fixedInput.accept(this, parameters); + return parameters.result.trim(); + } +} + +export default ToMarkdownTemplateVisitor; diff --git a/packages/markdown-template/src/TypeVisitor.js b/packages/markdown-template/src/TypeVisitor.js deleted file mode 100644 index eb02a3da..00000000 --- a/packages/markdown-template/src/TypeVisitor.js +++ /dev/null @@ -1,359 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -const {TemplateMarkModel, ConcertoMetaModel} = require('@accordproject/markdown-common'); - -const _throwTemplateExceptionForElement = require('./errorutil')._throwTemplateExceptionForElement; - -/** - * @param {*} serializer - the serializer - * @param {object} decorated - the property - * @return {object} the array of decorators compliant with the Concerto metamodel (JSON) - */ -function processDecorators(serializer,decorated) { - const result = []; - const decorators = decorated.getDecorators(); - - decorators.forEach((decorator) => { - const metaDecorator = { - '$class': `${ConcertoMetaModel.NAMESPACE}.Decorator`, - }; - - // The decorator's name - const name = decorator.getName(); - metaDecorator.name = name; - metaDecorator.arguments = []; - - // The decorator's arguments - const args = decorator.getArguments(); - args.forEach((arg) => { - let metaArgument; - if (typeof arg === 'string') { - metaArgument = { - '$class': `${ConcertoMetaModel.NAMESPACE}.DecoratorString`, - 'value': arg - }; - } else if (typeof arg === 'number') { - metaArgument = { - '$class': `${ConcertoMetaModel.NAMESPACE}.DecoratorNumber`, - 'value': arg - }; - } else if (typeof arg === 'boolean') { - metaArgument = { - '$class': `${ConcertoMetaModel.NAMESPACE}.DecoratorBoolean`, - 'value': arg - }; - } else { - metaArgument = { - '$class':`${ConcertoMetaModel.NAMESPACE}.DecoratorTypeReference`, - 'type': { - '$class': `${ConcertoMetaModel.NAMESPACE}.TypeIdentifier`, - 'name': arg.name - }, - 'isArray': arg.array - }; - } - metaDecorator.arguments.push(metaArgument); - }); - - // Validate individual arguments here - //console.log('DECORATE ' + JSON.stringify(metaDecorator)); - result.push(serializer.fromJSON(metaDecorator)); - }); - - if (result.length === 0) { - return null; - } else { - return result; - } -} - -/** - * Adds the elementType property to a TemplateMark DOM - * along with type specific metadata. This visitor verifies - * the structure of a template with respect to an associated - * template model and annotates the TemplateMark DOM with model - * information for use in downstream tools. - */ -class TypeVisitor { - /** - * Visits a sub-tree and return CiceroMark DOM - * @param {*} visitor the visitor to use - * @param {*} thing the node to visit - * @param {*} [parameters] optional parameters - * @param {string} field where the children are - */ - static visitChildren(visitor, thing, parameters, field = 'nodes') { - if(thing[field]) { - TypeVisitor.visitNodes(visitor, thing[field], parameters); - } - } - - /** - * Visits a list of nodes and return the CiceroMark DOM - * @param {*} visitor the visitor to use - * @param {*} things the list node to visit - * @param {*} [parameters] optional parameters - */ - static visitNodes(visitor, things, parameters) { - things.forEach(node => { - node.accept(visitor, parameters); - }); - } - - /** - * Get the type information for a property - * @param {*} property the propety - * @param {*} parameters the configuration parameters - * @returns {*} the information about the next model element (property or declaration) - */ - static nextModel(property, parameters) { - const declaration = property.isPrimitive() ? null : parameters.introspector.getClassDeclaration(property.getFullyQualifiedTypeName()); - return { - property: property.isPrimitive() ? property : null, - declaration, - typeIdentifier: property.isPrimitive() ? property.getFullyQualifiedTypeName() : declaration.getFullyQualifiedName(), - decorated: property.isPrimitive() ? property : declaration - }; - } - - /** - * Visit a node - * @param {*} thing the object being visited - * @param {*} parameters the parameters - */ - visit(thing, parameters) { - const currentModel = parameters.model; - switch(thing.getType()) { - case 'VariableDefinition': - case 'FormattedVariableDefinition': { - if (!currentModel) { - _throwTemplateExceptionForElement('Unknown property: ' + thing.name, thing); - } - if (thing.name === 'this') { - const property = currentModel; // BUG... if we are iterating over an array - // of complex types using a {{this}}, then thing will be a ClassDeclaration or an - // EnumDeclaration!! - - if (property && property.getType) { - const serializer = parameters.templateMarkModelManager.getSerializer(); - thing.decorators = processDecorators(serializer,property); - if (property.isTypeEnum && property.isTypeEnum()) { - const enumVariableDeclaration = parameters.templateMarkModelManager.getType(`${TemplateMarkModel.NAMESPACE}.EnumVariableDefinition`); - const enumType = property.getParent().getModelFile().getType(property.getType()); - thing.elementType = property.getFullyQualifiedTypeName(); - thing.$classDeclaration = enumVariableDeclaration; - thing.enumValues = enumType.getOwnProperties().map(x => x.getName()); - } else if (property.isPrimitive()) { - thing.elementType = property.getFullyQualifiedTypeName(); - } else if (property.isRelationship?.()) { - const elementType = property.getFullyQualifiedTypeName(); - thing.elementType = elementType; - const nestedTemplateModel = parameters.introspector.getClassDeclaration(elementType); - const identifier = nestedTemplateModel.getIdentifierFieldName(); - thing.identifiedBy = identifier ? identifier : '$identifier'; // Consistent with Concerto 1.0 semantics - } else { - const elementType = property.getFullyQualifiedTypeName(); - thing.elementType = elementType; - } - } - else { - // it is a class - const elementType = property.getFullyQualifiedName(); - thing.elementType = elementType; - } - } else { - if (!currentModel.getProperty) { - _throwTemplateExceptionForElement('Unknown property: ' + thing.name, thing); - } - const property = currentModel.getProperty(thing.name); - if (property) { - const serializer = parameters.templateMarkModelManager.getSerializer(); - thing.decorators = processDecorators(serializer,property); - if (property.isTypeEnum && property.isTypeEnum()) { - const enumVariableDeclaration = parameters.templateMarkModelManager.getType(`${TemplateMarkModel.NAMESPACE}.EnumVariableDefinition`); - const enumType = property.getParent().getModelFile().getType(property.getType()); - thing.elementType = property.getFullyQualifiedTypeName(); - thing.$classDeclaration = enumVariableDeclaration; - thing.enumValues = enumType.getOwnProperties().map(x => x.getName()); - } else if (property.isPrimitive()) { - thing.elementType = property.getFullyQualifiedTypeName(); - } else if (property.isRelationship?.()) { - const elementType = property.getFullyQualifiedTypeName(); - thing.elementType = elementType; - const nestedTemplateModel = parameters.introspector.getClassDeclaration(elementType); - const identifier = nestedTemplateModel.getIdentifierFieldName(); - thing.identifiedBy = identifier ? identifier : '$identifier'; // Consistent with Concerto 1.0 semantics - } else { - const elementType = property.getFullyQualifiedTypeName(); - thing.elementType = elementType; - } - } else { - _throwTemplateExceptionForElement('Unknown property: ' + thing.name, thing); - } - } - } - break; - case 'ClauseDefinition': { - if (parameters.kind === 'contract') { - if (!currentModel) { - _throwTemplateExceptionForElement('Unknown property: ' + thing.name, thing); - } - const property = currentModel.getOwnProperty(thing.name); - if (!property) { - _throwTemplateExceptionForElement('Unknown property: ' + thing.name, thing); - } - const {typeIdentifier, decorated} = TypeVisitor.nextModel(property, parameters); - thing.elementType = typeIdentifier; - const serializer = parameters.templateMarkModelManager.getSerializer(); - thing.decorators = processDecorators(serializer,decorated); - TypeVisitor.visitChildren(this, thing, { - templateMarkModelManager:parameters.templateMarkModelManager, - introspector:parameters.introspector, - model:decorated, - kind:parameters.kind - }); - } else { - if (!currentModel) { - _throwTemplateExceptionForElement('Unknown property: ' + thing.name, thing); - } - const serializer = parameters.templateMarkModelManager.getSerializer(); - thing.decorators = processDecorators(serializer,currentModel); - thing.elementType = currentModel.getFullyQualifiedName(); - TypeVisitor.visitChildren(this, thing, parameters); - } - } - break; - case 'WithDefinition': { - const property = currentModel.getOwnProperty(thing.name); - let nextModel; - if (!property) { - _throwTemplateExceptionForElement('Unknown property: ' + thing.name, thing); - } - if (property.isPrimitive()) { - nextModel = property; - } else { - thing.elementType = property.getFullyQualifiedTypeName(); - nextModel = parameters.introspector.getClassDeclaration(thing.elementType); - } - const serializer = parameters.templateMarkModelManager.getSerializer(); - thing.decorators = processDecorators(serializer,nextModel); - TypeVisitor.visitChildren(this, thing, { - templateMarkModelManager:parameters.templateMarkModelManager, - introspector:parameters.introspector, - model:nextModel, - kind:parameters.kind - }); - } - break; - case 'ForeachDefinition': - case 'JoinDefinition': - case 'ListBlockDefinition': { - const property = currentModel.getOwnProperty(thing.name); - let nextModel; - if (!property) { - _throwTemplateExceptionForElement('Unknown property: ' + thing.name, thing); - } - if (!property.isArray()) { - _throwTemplateExceptionForElement(`${thing.getType()} template not on an array property: ${thing.name}`, thing); - } - const serializer = parameters.templateMarkModelManager.getSerializer(); - thing.decorators = processDecorators(serializer,property); - if (property.isPrimitive()) { - nextModel = property; - } else { - thing.elementType = property.getFullyQualifiedTypeName(); - nextModel = parameters.introspector.getClassDeclaration(thing.elementType); - } - TypeVisitor.visitChildren(this, thing, { - templateMarkModelManager:parameters.templateMarkModelManager, - introspector:parameters.introspector, - model:nextModel, - kind:parameters.kind - }); - } - break; - case 'ConditionalDefinition': { - const property = currentModel.getOwnProperty(thing.name); - let nextModel; - if (thing.name !== 'if' && !property) { // hack, allow the node to have the name 'if' - _throwTemplateExceptionForElement('Unknown property: ' + thing.name, thing); - } - - // if (property.getType() !== 'Boolean') { - // _throwTemplateExceptionForElement('Conditional template not on a boolean property: ' + thing.name, thing); - // } - const serializer = parameters.templateMarkModelManager.getSerializer(); - thing.decorators = property ? processDecorators(serializer,property) : null; - nextModel = property; - TypeVisitor.visitChildren(this, thing, { - templateMarkModelManager:parameters.templateMarkModelManager, - introspector:parameters.introspector, - model:nextModel, - kind:parameters.kind - }, 'whenTrue'); - TypeVisitor.visitChildren(this, thing, { - templateMarkModelManager:parameters.templateMarkModelManager, - introspector:parameters.introspector, - model:null, - kind:parameters.kind - }, 'whenFalse'); - } - break; - case 'OptionalDefinition': { - const property = currentModel.getOwnProperty(thing.name); - let nextModel; - if (!property) { - _throwTemplateExceptionForElement('Unknown property: ' + thing.name, thing); - } - if (!property.isOptional()) { - _throwTemplateExceptionForElement('Optional template not on an optional property: ' + thing.name, thing); - } - const serializer = parameters.templateMarkModelManager.getSerializer(); - thing.decorators = processDecorators(serializer,property); - if (property.isPrimitive()) { - thing.elementType = property.getFullyQualifiedTypeName(); - nextModel = property; - } else { - thing.elementType = property.getFullyQualifiedTypeName(); - nextModel = parameters.introspector.getClassDeclaration(thing.elementType); - } - TypeVisitor.visitChildren(this, thing, { - templateMarkModelManager:parameters.templateMarkModelManager, - introspector:parameters.introspector, - model:nextModel, - kind:parameters.kind - }, 'whenSome'); - TypeVisitor.visitChildren(this, thing, { - templateMarkModelManager:parameters.templateMarkModelManager, - introspector:parameters.introspector, - model:null, - kind:parameters.kind - }, 'whenNone'); - } - break; - case 'ContractDefinition': { - thing.elementType = currentModel.getFullyQualifiedName(); - TypeVisitor.visitChildren(this, thing, parameters); - } - break; - default: - TypeVisitor.visitChildren(this, thing, parameters); - } - } -} - -module.exports = TypeVisitor; diff --git a/packages/markdown-template/src/TypeVisitor.ts b/packages/markdown-template/src/TypeVisitor.ts new file mode 100644 index 00000000..9ee8d718 --- /dev/null +++ b/packages/markdown-template/src/TypeVisitor.ts @@ -0,0 +1,306 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { TemplateMarkModel, ConcertoMetaModel } from '@accordproject/markdown-common'; +import { _throwTemplateExceptionForElement } from './errorutil'; + +/** + * Process the decorators on a model element + */ +function processDecorators(serializer: any, decorated: any): any[] | null { + const result: any[] = []; + const decorators = decorated.getDecorators(); + + decorators.forEach((decorator: any) => { + const metaDecorator: any = { + '$class': `${ConcertoMetaModel.NAMESPACE}.Decorator`, + }; + + const name = decorator.getName(); + metaDecorator.name = name; + metaDecorator.arguments = []; + + const args = decorator.getArguments(); + args.forEach((arg: any) => { + let metaArgument; + if (typeof arg === 'string') { + metaArgument = { + '$class': `${ConcertoMetaModel.NAMESPACE}.DecoratorString`, + 'value': arg, + }; + } else if (typeof arg === 'number') { + metaArgument = { + '$class': `${ConcertoMetaModel.NAMESPACE}.DecoratorNumber`, + 'value': arg, + }; + } else if (typeof arg === 'boolean') { + metaArgument = { + '$class': `${ConcertoMetaModel.NAMESPACE}.DecoratorBoolean`, + 'value': arg, + }; + } else { + metaArgument = { + '$class': `${ConcertoMetaModel.NAMESPACE}.DecoratorTypeReference`, + 'type': { + '$class': `${ConcertoMetaModel.NAMESPACE}.TypeIdentifier`, + 'name': arg.name, + }, + 'isArray': arg.array, + }; + } + metaDecorator.arguments.push(metaArgument); + }); + + result.push(serializer.fromJSON(metaDecorator)); + }); + + return result.length === 0 ? null : result; +} + +/** + * Adds the elementType property to a TemplateMark DOM + */ +export class TypeVisitor { + static visitChildren(visitor: TypeVisitor, thing: any, parameters: any, field = 'nodes'): void { + if (thing[field]) { + TypeVisitor.visitNodes(visitor, thing[field], parameters); + } + } + + static visitNodes(visitor: TypeVisitor, things: any[], parameters: any): void { + things.forEach((node) => { + node.accept(visitor, parameters); + }); + } + + static nextModel(property: any, parameters: any): any { + const declaration = property.isPrimitive() ? null : parameters.introspector.getClassDeclaration(property.getFullyQualifiedTypeName()); + return { + property: property.isPrimitive() ? property : null, + declaration, + typeIdentifier: property.isPrimitive() ? property.getFullyQualifiedTypeName() : declaration.getFullyQualifiedName(), + decorated: property.isPrimitive() ? property : declaration, + }; + } + + visit(thing: any, parameters: any): void { + const currentModel = parameters.model; + switch (thing.getType()) { + case 'VariableDefinition': + case 'FormattedVariableDefinition': { + if (!currentModel) { + _throwTemplateExceptionForElement('Unknown property: ' + thing.name, thing); + } + if (thing.name === 'this') { + const property = currentModel; + + if (property && property.getType) { + const serializer = parameters.templateMarkModelManager.getSerializer(); + thing.decorators = processDecorators(serializer, property); + if (property.isTypeEnum && property.isTypeEnum()) { + const enumVariableDeclaration = parameters.templateMarkModelManager.getType(`${TemplateMarkModel.NAMESPACE}.EnumVariableDefinition`); + const enumType = property.getParent().getModelFile().getType(property.getType()); + thing.elementType = property.getFullyQualifiedTypeName(); + thing.$classDeclaration = enumVariableDeclaration; + thing.enumValues = enumType.getOwnProperties().map((x: any) => x.getName()); + } else if (property.isPrimitive()) { + thing.elementType = property.getFullyQualifiedTypeName(); + } else if (property.isRelationship?.()) { + const elementType = property.getFullyQualifiedTypeName(); + thing.elementType = elementType; + const nestedTemplateModel = parameters.introspector.getClassDeclaration(elementType); + const identifier = nestedTemplateModel.getIdentifierFieldName(); + thing.identifiedBy = identifier ? identifier : '$identifier'; + } else { + thing.elementType = property.getFullyQualifiedTypeName(); + } + } else { + thing.elementType = property.getFullyQualifiedName(); + } + } else { + if (!currentModel.getProperty) { + _throwTemplateExceptionForElement('Unknown property: ' + thing.name, thing); + } + const property = currentModel.getProperty(thing.name); + if (property) { + const serializer = parameters.templateMarkModelManager.getSerializer(); + thing.decorators = processDecorators(serializer, property); + if (property.isTypeEnum && property.isTypeEnum()) { + const enumVariableDeclaration = parameters.templateMarkModelManager.getType(`${TemplateMarkModel.NAMESPACE}.EnumVariableDefinition`); + const enumType = property.getParent().getModelFile().getType(property.getType()); + thing.elementType = property.getFullyQualifiedTypeName(); + thing.$classDeclaration = enumVariableDeclaration; + thing.enumValues = enumType.getOwnProperties().map((x: any) => x.getName()); + } else if (property.isPrimitive()) { + thing.elementType = property.getFullyQualifiedTypeName(); + } else if (property.isRelationship?.()) { + const elementType = property.getFullyQualifiedTypeName(); + thing.elementType = elementType; + const nestedTemplateModel = parameters.introspector.getClassDeclaration(elementType); + const identifier = nestedTemplateModel.getIdentifierFieldName(); + thing.identifiedBy = identifier ? identifier : '$identifier'; + } else { + thing.elementType = property.getFullyQualifiedTypeName(); + } + } else { + _throwTemplateExceptionForElement('Unknown property: ' + thing.name, thing); + } + } + break; + } + case 'ClauseDefinition': { + if (parameters.kind === 'contract') { + if (!currentModel) { + _throwTemplateExceptionForElement('Unknown property: ' + thing.name, thing); + } + const property = currentModel.getOwnProperty(thing.name); + if (!property) { + _throwTemplateExceptionForElement('Unknown property: ' + thing.name, thing); + } + const { typeIdentifier, decorated } = TypeVisitor.nextModel(property, parameters); + thing.elementType = typeIdentifier; + const serializer = parameters.templateMarkModelManager.getSerializer(); + thing.decorators = processDecorators(serializer, decorated); + TypeVisitor.visitChildren(this, thing, { + templateMarkModelManager: parameters.templateMarkModelManager, + introspector: parameters.introspector, + model: decorated, + kind: parameters.kind, + }); + } else { + if (!currentModel) { + _throwTemplateExceptionForElement('Unknown property: ' + thing.name, thing); + } + const serializer = parameters.templateMarkModelManager.getSerializer(); + thing.decorators = processDecorators(serializer, currentModel); + thing.elementType = currentModel.getFullyQualifiedName(); + TypeVisitor.visitChildren(this, thing, parameters); + } + break; + } + case 'WithDefinition': { + const property = currentModel.getOwnProperty(thing.name); + let nextModel; + if (!property) { + _throwTemplateExceptionForElement('Unknown property: ' + thing.name, thing); + } + if (property.isPrimitive()) { + nextModel = property; + } else { + thing.elementType = property.getFullyQualifiedTypeName(); + nextModel = parameters.introspector.getClassDeclaration(thing.elementType); + } + const serializer = parameters.templateMarkModelManager.getSerializer(); + thing.decorators = processDecorators(serializer, nextModel); + TypeVisitor.visitChildren(this, thing, { + templateMarkModelManager: parameters.templateMarkModelManager, + introspector: parameters.introspector, + model: nextModel, + kind: parameters.kind, + }); + break; + } + case 'ForeachDefinition': + case 'JoinDefinition': + case 'ListBlockDefinition': { + const property = currentModel.getOwnProperty(thing.name); + let nextModel; + if (!property) { + _throwTemplateExceptionForElement('Unknown property: ' + thing.name, thing); + } + if (!property.isArray()) { + _throwTemplateExceptionForElement(`${thing.getType()} template not on an array property: ${thing.name}`, thing); + } + const serializer = parameters.templateMarkModelManager.getSerializer(); + thing.decorators = processDecorators(serializer, property); + if (property.isPrimitive()) { + nextModel = property; + } else { + thing.elementType = property.getFullyQualifiedTypeName(); + nextModel = parameters.introspector.getClassDeclaration(thing.elementType); + } + TypeVisitor.visitChildren(this, thing, { + templateMarkModelManager: parameters.templateMarkModelManager, + introspector: parameters.introspector, + model: nextModel, + kind: parameters.kind, + }); + break; + } + case 'ConditionalDefinition': { + const property = currentModel.getOwnProperty(thing.name); + if (thing.name !== 'if' && !property) { + _throwTemplateExceptionForElement('Unknown property: ' + thing.name, thing); + } + + const serializer = parameters.templateMarkModelManager.getSerializer(); + thing.decorators = property ? processDecorators(serializer, property) : null; + const nextModel = property; + TypeVisitor.visitChildren(this, thing, { + templateMarkModelManager: parameters.templateMarkModelManager, + introspector: parameters.introspector, + model: nextModel, + kind: parameters.kind, + }, 'whenTrue'); + TypeVisitor.visitChildren(this, thing, { + templateMarkModelManager: parameters.templateMarkModelManager, + introspector: parameters.introspector, + model: null, + kind: parameters.kind, + }, 'whenFalse'); + break; + } + case 'OptionalDefinition': { + const property = currentModel.getOwnProperty(thing.name); + let nextModel; + if (!property) { + _throwTemplateExceptionForElement('Unknown property: ' + thing.name, thing); + } + if (!property.isOptional()) { + _throwTemplateExceptionForElement('Optional template not on an optional property: ' + thing.name, thing); + } + const serializer = parameters.templateMarkModelManager.getSerializer(); + thing.decorators = processDecorators(serializer, property); + if (property.isPrimitive()) { + thing.elementType = property.getFullyQualifiedTypeName(); + nextModel = property; + } else { + thing.elementType = property.getFullyQualifiedTypeName(); + nextModel = parameters.introspector.getClassDeclaration(thing.elementType); + } + TypeVisitor.visitChildren(this, thing, { + templateMarkModelManager: parameters.templateMarkModelManager, + introspector: parameters.introspector, + model: nextModel, + kind: parameters.kind, + }, 'whenSome'); + TypeVisitor.visitChildren(this, thing, { + templateMarkModelManager: parameters.templateMarkModelManager, + introspector: parameters.introspector, + model: null, + kind: parameters.kind, + }, 'whenNone'); + break; + } + case 'ContractDefinition': + thing.elementType = currentModel.getFullyQualifiedName(); + TypeVisitor.visitChildren(this, thing, parameters); + break; + default: + TypeVisitor.visitChildren(this, thing, parameters); + } + } +} + +export default TypeVisitor; diff --git a/packages/markdown-template/src/datetimeutil.js b/packages/markdown-template/src/datetimeutil.ts similarity index 70% rename from packages/markdown-template/src/datetimeutil.js rename to packages/markdown-template/src/datetimeutil.ts index 7506dc18..62c37768 100644 --- a/packages/markdown-template/src/datetimeutil.js +++ b/packages/markdown-template/src/datetimeutil.ts @@ -12,28 +12,20 @@ * limitations under the License. */ -'use strict'; - -const dayjs = require('dayjs'); -const utc = require('dayjs/plugin/utc'); +import dayjs from 'dayjs'; +import utc from 'dayjs/plugin/utc'; dayjs.extend(utc); /** * Ensures there is a proper current time - * - * @param {string} currentTime - the definition of 'now' - * @returns {object} if valid, the dayjs object for the current time */ -function setCurrentTime(currentTime) { +export function setCurrentTime(currentTime?: string): dayjs.Dayjs { if (!currentTime) { - // Defaults to current local time return dayjs.utc(); } try { return dayjs.utc(currentTime); - } catch (err) { + } catch (err: any) { throw new Error(`${currentTime} is not a valid date and time: ${err.message}`); } } - -module.exports = { setCurrentTime }; diff --git a/packages/markdown-template/src/errorutil.js b/packages/markdown-template/src/errorutil.ts similarity index 53% rename from packages/markdown-template/src/errorutil.js rename to packages/markdown-template/src/errorutil.ts index 68fe849b..7e222e1c 100644 --- a/packages/markdown-template/src/errorutil.js +++ b/packages/markdown-template/src/errorutil.ts @@ -12,39 +12,23 @@ * limitations under the License. */ -'use strict'; - -const TemplateException = require('./templateexception'); - +import { TemplateException } from './templateexception'; /** * Throw a template exception for the element - * @param {string} message - the error message - * @param {object} element the AST - * @throws {TemplateException} */ -function _throwTemplateExceptionForElement(message, element) { +export function _throwTemplateExceptionForElement(message: string, element: any): never { const fileName = 'text/grammar.tem.md'; - //let column = element.fieldName.col; - //let line = element.fieldName.line; - let column = -1; - let line = -1; + const column = -1; + const line = -1; - let token = element && element.value ? element.value : ' '; + const token = element && element.value ? element.value : ' '; const endColumn = column + token.length; const fileLocation = { - start: { - line, - column, - }, - end: { - line, - endColumn,//XXX - }, + start: { line, column }, + end: { line, endColumn }, }; - throw new TemplateException(message, fileLocation, fileName, null, 'markdown-template'); + throw new TemplateException(message, fileLocation, fileName, undefined, 'markdown-template'); } - -module.exports._throwTemplateExceptionForElement = _throwTemplateExceptionForElement; diff --git a/packages/markdown-template/src/fromtemplatemarkrules.js b/packages/markdown-template/src/fromtemplatemarkrules.js deleted file mode 100644 index 768c006c..00000000 --- a/packages/markdown-template/src/fromtemplatemarkrules.js +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -const CommonMarkUtils = require('@accordproject/markdown-common').CommonMarkUtils; - -const rules = {}; -// Inlines -rules.VariableDefinition = (visitor,thing,children,parameters,resultString,resultSeq) => { - const result = [resultString('{{'),resultString(thing.name),resultString('}}')]; - resultSeq(parameters,result); -}; -rules.FormattedVariableDefinition = (visitor,thing,children,parameters,resultString,resultSeq) => { - const result = [resultString('{{'),resultString(thing.name),resultString(' as "'),resultString(thing.format),resultString('"}}')]; - resultSeq(parameters,result); -}; -rules.EnumVariableDefinition = (visitor,thing,children,parameters,resultString,resultSeq) => { - const result = [resultString('{{'),resultString(thing.name),resultString('}}')]; - resultSeq(parameters,result); -}; -rules.FormulaDefinition = (visitor,thing,children,parameters,resultString,resultSeq) => { - const result = [resultString('{{%'),resultString(thing.code),resultString('%}}')]; - resultSeq(parameters,result); -}; -rules.ConditionalDefinition = (visitor,thing,children,parameters,resultString,resultSeq) => { - const next1 = `{{#if ${thing.name}}}`; - const whenTrue = visitor.visitChildren(visitor,thing,parameters,'whenTrue'); - const whenFalse = visitor.visitChildren(visitor,thing,parameters,'whenFalse'); - const next2 = '{{/if}}'; - let result; - if (whenFalse) { - const next3 = '{{else}}'; - result = [resultString(next1),resultString(whenTrue),resultString(next3),resultString(whenFalse),resultString(next2)]; - } else { - result = [resultString(next1),resultString(whenTrue),resultString(next2)]; - } - resultSeq(parameters,result); -}; -rules.OptionalDefinition = (visitor,thing,children,parameters,resultString,resultSeq) => { - const next1 = `{{#optional ${thing.name}}}`; - const whenSome = visitor.visitChildren(visitor,thing,parameters,'whenSome'); - const whenNone = visitor.visitChildren(visitor,thing,parameters,'whenNone'); - const next2 = '{{/optional}}'; - let result; - if (whenNone) { - const next3 = '{{else}}'; - result = [resultString(next1),resultString(whenSome),resultString(next3),resultString(whenNone),resultString(next2)]; - } else { - result = [resultString(next1),resultString(whenSome),resultString(next2)]; - } - resultSeq(parameters,result); -}; -rules.WithDefinition = (visitor,thing,children,parameters,resultString,resultSeq) => { - const next1 = `{{#with ${thing.name}}}`; - const next2 = '{{/with}}'; - const result = [resultString(next1),children,resultString(next2)]; - resultSeq(parameters,result); -}; -rules.JoinDefinition = (visitor,thing,children,parameters,resultString,resultSeq) => { - const sepAttr = thing.separator ? ' separator="' + thing.separator + '"' : ''; - const localeAttr = thing.locale ? ' locale="' + thing.locale + '"' : ''; - const typeAttr = thing.type ? ' type="' + thing.type + '"' : ''; - const styleAttr = thing.style ? ' style="' + thing.style + '"' : ''; - - const next1 = `{{#join ${thing.name}${sepAttr}${localeAttr}${typeAttr}${styleAttr}}}`; - const next2 = '{{/join}}'; - const result = [resultString(next1),children,resultString(next2)]; - resultSeq(parameters,result); -}; -// Container blocks -rules.ListBlockDefinition = (visitor,thing,children,parameters,resultString,resultSeq) => { - const listKind = thing.type === 'bullet' ? 'ulist' : 'olist'; - const prefix = CommonMarkUtils.mkPrefix(parameters,1); - const next1 = prefix; - const next2 = `{{#${listKind} ${thing.name}}}\n`; - const next3 = prefix; - const next4 = `{{/${listKind}}}`; - const result = [resultString(next1),resultString(next2),children,resultString(next3),resultString(next4)]; - resultSeq(parameters,result); -}; -rules.ClauseDefinition = (visitor,thing,children,parameters,resultString,resultSeq) => { - const next1 = CommonMarkUtils.mkPrefix(parameters,2); - const srcAttr = thing.src ? ' src="' + thing.src + '"' : ''; - const next2 = `{{#clause ${thing.name}${srcAttr}}}\n`; - const next3 = '\n{{/clause}}'; - const result = [resultString(next1),resultString(next2),children,resultString(next3)]; - resultSeq(parameters,result); -}; - -module.exports = rules; \ No newline at end of file diff --git a/packages/markdown-template/src/fromtemplatemarkrules.ts b/packages/markdown-template/src/fromtemplatemarkrules.ts new file mode 100644 index 00000000..e66ec4d7 --- /dev/null +++ b/packages/markdown-template/src/fromtemplatemarkrules.ts @@ -0,0 +1,100 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { CommonMarkUtils } from '@accordproject/markdown-common'; + +const rules: Record = {}; + +rules.VariableDefinition = (visitor: any, thing: any, children: any, parameters: any, resultString: any, resultSeq: any) => { + const result = [resultString('{{'), resultString(thing.name), resultString('}}')]; + resultSeq(parameters, result); +}; +rules.FormattedVariableDefinition = (visitor: any, thing: any, children: any, parameters: any, resultString: any, resultSeq: any) => { + const result = [resultString('{{'), resultString(thing.name), resultString(' as "'), resultString(thing.format), resultString('"}}')]; + resultSeq(parameters, result); +}; +rules.EnumVariableDefinition = (visitor: any, thing: any, children: any, parameters: any, resultString: any, resultSeq: any) => { + const result = [resultString('{{'), resultString(thing.name), resultString('}}')]; + resultSeq(parameters, result); +}; +rules.FormulaDefinition = (visitor: any, thing: any, children: any, parameters: any, resultString: any, resultSeq: any) => { + const result = [resultString('{{%'), resultString(thing.code), resultString('%}}')]; + resultSeq(parameters, result); +}; +rules.ConditionalDefinition = (visitor: any, thing: any, children: any, parameters: any, resultString: any, resultSeq: any) => { + const next1 = `{{#if ${thing.name}}}`; + const whenTrue = visitor.visitChildren(visitor, thing, parameters, 'whenTrue'); + const whenFalse = visitor.visitChildren(visitor, thing, parameters, 'whenFalse'); + const next2 = '{{/if}}'; + let result; + if (whenFalse) { + const next3 = '{{else}}'; + result = [resultString(next1), resultString(whenTrue), resultString(next3), resultString(whenFalse), resultString(next2)]; + } else { + result = [resultString(next1), resultString(whenTrue), resultString(next2)]; + } + resultSeq(parameters, result); +}; +rules.OptionalDefinition = (visitor: any, thing: any, children: any, parameters: any, resultString: any, resultSeq: any) => { + const next1 = `{{#optional ${thing.name}}}`; + const whenSome = visitor.visitChildren(visitor, thing, parameters, 'whenSome'); + const whenNone = visitor.visitChildren(visitor, thing, parameters, 'whenNone'); + const next2 = '{{/optional}}'; + let result; + if (whenNone) { + const next3 = '{{else}}'; + result = [resultString(next1), resultString(whenSome), resultString(next3), resultString(whenNone), resultString(next2)]; + } else { + result = [resultString(next1), resultString(whenSome), resultString(next2)]; + } + resultSeq(parameters, result); +}; +rules.WithDefinition = (visitor: any, thing: any, children: any, parameters: any, resultString: any, resultSeq: any) => { + const next1 = `{{#with ${thing.name}}}`; + const next2 = '{{/with}}'; + const result = [resultString(next1), children, resultString(next2)]; + resultSeq(parameters, result); +}; +rules.JoinDefinition = (visitor: any, thing: any, children: any, parameters: any, resultString: any, resultSeq: any) => { + const sepAttr = thing.separator ? ' separator="' + thing.separator + '"' : ''; + const localeAttr = thing.locale ? ' locale="' + thing.locale + '"' : ''; + const typeAttr = thing.type ? ' type="' + thing.type + '"' : ''; + const styleAttr = thing.style ? ' style="' + thing.style + '"' : ''; + + const next1 = `{{#join ${thing.name}${sepAttr}${localeAttr}${typeAttr}${styleAttr}}}`; + const next2 = '{{/join}}'; + const result = [resultString(next1), children, resultString(next2)]; + resultSeq(parameters, result); +}; +// Container blocks +rules.ListBlockDefinition = (visitor: any, thing: any, children: any, parameters: any, resultString: any, resultSeq: any) => { + const listKind = thing.type === 'bullet' ? 'ulist' : 'olist'; + const prefix = CommonMarkUtils.mkPrefix(parameters, 1); + const next1 = prefix; + const next2 = `{{#${listKind} ${thing.name}}}\n`; + const next3 = prefix; + const next4 = `{{/${listKind}}}`; + const result = [resultString(next1), resultString(next2), children, resultString(next3), resultString(next4)]; + resultSeq(parameters, result); +}; +rules.ClauseDefinition = (visitor: any, thing: any, children: any, parameters: any, resultString: any, resultSeq: any) => { + const next1 = CommonMarkUtils.mkPrefix(parameters, 2); + const srcAttr = thing.src ? ' src="' + thing.src + '"' : ''; + const next2 = `{{#clause ${thing.name}${srcAttr}}}\n`; + const next3 = '\n{{/clause}}'; + const result = [resultString(next1), resultString(next2), children, resultString(next3)]; + resultSeq(parameters, result); +}; + +export default rules; diff --git a/packages/markdown-template/src/index.ts b/packages/markdown-template/src/index.ts new file mode 100644 index 00000000..095ccc12 --- /dev/null +++ b/packages/markdown-template/src/index.ts @@ -0,0 +1,38 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as util from './util'; +import * as templatemarkutil from './templatemarkutil'; +import * as datetimeutil from './datetimeutil'; +import { normalizeNLs } from './normalize'; +import { TemplateException } from './templateexception'; +import { TemplateMarkTransformer } from './TemplateMarkTransformer'; + +export { + util, + templatemarkutil, + datetimeutil, + normalizeNLs, + TemplateException, + TemplateMarkTransformer, +}; + +export default { + util, + templatemarkutil, + datetimeutil, + normalizeNLs, + TemplateException, + TemplateMarkTransformer, +}; diff --git a/packages/markdown-template/src/normalize.js b/packages/markdown-template/src/normalize.js deleted file mode 100644 index e094b353..00000000 --- a/packages/markdown-template/src/normalize.js +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -const CiceroMarkTransformer = require('@accordproject/markdown-cicero').CiceroMarkTransformer; - -/** - * Prepare the text for parsing (normalizes new lines, etc) - * @param {string} input - the text - * @return {string} - the normalized text - */ -function normalizeNLs(input) { - // we replace all \r and \n with \n - let text = input.replace(/\r/gm,''); - return text; -} - -/** - * Normalize to markdown cicero text - * @param {*} input - the CiceroMark DOM - * @return {string} - the normalized markdown cicero text - */ -function normalizeToMarkdownCicero(input) { - const ciceroMarkTransformer = new CiceroMarkTransformer(); - const result = ciceroMarkTransformer.toMarkdownCicero(input); - return result; -} - -/** - * Normalize from markdown cicero text - * @param {string} input - the markdown cicero text - * @return {object} - the normalized CiceroMark DOM - */ -function normalizeFromMarkdownCicero(input) { - // Normalizes new lines - const inputNLs = normalizeNLs(input); - // Roundtrip through the CommonMark parser - const ciceroMarkTransformer = new CiceroMarkTransformer(); - return ciceroMarkTransformer.fromMarkdownCicero(inputNLs); -} - -module.exports.normalizeNLs = normalizeNLs; -module.exports.normalizeToMarkdownCicero = normalizeToMarkdownCicero; -module.exports.normalizeFromMarkdownCicero = normalizeFromMarkdownCicero; diff --git a/packages/markdown-template/src/normalize.test.ts b/packages/markdown-template/src/normalize.test.ts new file mode 100644 index 00000000..e2adb296 --- /dev/null +++ b/packages/markdown-template/src/normalize.test.ts @@ -0,0 +1,28 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { normalizeNLs, normalizeFromMarkdownCicero, normalizeToMarkdownCicero } from './normalize'; + +describe('#normalize', () => { + describe('#normalizeNLs', () => { + it('should normalize to \\n', () => { + expect(normalizeNLs('Hello\r\nWorld!')).toBe('Hello\nWorld!'); + }); + }); + describe('#normalizeMarkdownCicero', () => { + it('should normalize to \\n', () => { + expect(normalizeToMarkdownCicero(normalizeFromMarkdownCicero('Hello\r\nWorld!'))).toBe('Hello\nWorld!'); + }); + }); +}); diff --git a/packages/markdown-template/src/normalize.ts b/packages/markdown-template/src/normalize.ts new file mode 100644 index 00000000..5603f718 --- /dev/null +++ b/packages/markdown-template/src/normalize.ts @@ -0,0 +1,39 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { CiceroMarkTransformer } from '@accordproject/markdown-cicero'; + +/** + * Prepare the text for parsing (normalizes new lines, etc) + */ +export function normalizeNLs(input: string): string { + return input.replace(/\r/gm, ''); +} + +/** + * Normalize to markdown cicero text + */ +export function normalizeToMarkdownCicero(input: any): string { + const ciceroMarkTransformer = new CiceroMarkTransformer(); + return ciceroMarkTransformer.toMarkdownCicero(input); +} + +/** + * Normalize from markdown cicero text + */ +export function normalizeFromMarkdownCicero(input: string): any { + const inputNLs = normalizeNLs(input); + const ciceroMarkTransformer = new CiceroMarkTransformer(); + return ciceroMarkTransformer.fromMarkdownCicero(inputNLs); +} diff --git a/packages/markdown-template/src/templateexception.js b/packages/markdown-template/src/templateexception.js deleted file mode 100644 index 9af11227..00000000 --- a/packages/markdown-template/src/templateexception.js +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -const ParseException = require('@accordproject/concerto-cto').ParseException; - -/** - * Exception thrown for invalid templates - * @extends BaseFileException - * @see See {@link BaseFileException} - * @class - * @memberof module:markdown-template - * @private - */ -class TemplateException extends ParseException { - /** - * Create a TemplateException - * @param {string} message - the message for the exception - * @param {string} fileLocation - the optional file location associated with the exception - * @param {string} fileName - the optional file name associated with the exception - * @param {string} fullMessageOverride - the optional pre-existing full message - * @param {string} component - the optional component which throws this error - */ - constructor(message, fileLocation, fileName, fullMessageOverride, component) { - super(message, fileLocation, fileName, fullMessageOverride, component || 'cicero-core'); - } -} - -module.exports = TemplateException; \ No newline at end of file diff --git a/packages/markdown-template/src/templateexception.ts b/packages/markdown-template/src/templateexception.ts new file mode 100644 index 00000000..15661d91 --- /dev/null +++ b/packages/markdown-template/src/templateexception.ts @@ -0,0 +1,32 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ParseException } from '@accordproject/concerto-cto'; + +/** + * Exception thrown for invalid templates + */ +export class TemplateException extends ParseException { + constructor( + message: string, + fileLocation?: any, + fileName?: string, + fullMessageOverride?: string, + component?: string, + ) { + super(message, fileLocation, fileName, fullMessageOverride, component || 'cicero-core'); + } +} + +export default TemplateException; diff --git a/packages/markdown-template/src/templatemarkutil.js b/packages/markdown-template/src/templatemarkutil.js deleted file mode 100644 index 6d6754d2..00000000 --- a/packages/markdown-template/src/templatemarkutil.js +++ /dev/null @@ -1,241 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -const dayjs = require('dayjs'); -const utc = require('dayjs/plugin/utc'); -dayjs.extend(utc); - -const { ModelManager, Factory, Serializer, Introspector } = require('@accordproject/concerto-core'); -const { CommonMarkModel, CiceroMarkModel, ConcertoMetaModel, TemplateMarkModel } = require('@accordproject/markdown-common'); - -const normalizeNLs = require('./normalize').normalizeNLs; -const TypeVisitor = require('./TypeVisitor'); -const FormulaVisitor = require('./FormulaVisitor'); -const MarkdownIt = require('markdown-it'); -const MarkdownItTemplate = require('@accordproject/markdown-it-template'); -const FromMarkdownIt = require('@accordproject/markdown-common').FromMarkdownIt; -const templaterules = require('./templaterules'); - -/** - * Model manager for TemplateMark - * @param {object} options - optional parameters - * @param {number} [options.utcOffset] - UTC Offset for this execution - * @returns {object} model manager and utilities for TemplateMark - */ -function mkTemplateMarkManager(options) { - const result = {}; - const newOpts = { - ...options, - strict: true - }; - result.modelManager = new ModelManager(newOpts); - result.modelManager.addCTOModel(CommonMarkModel.MODEL, 'commonmark.cto'); - result.modelManager.addCTOModel(ConcertoMetaModel.MODEL, 'metamodel.cto'); - result.modelManager.addCTOModel(CiceroMarkModel.MODEL, 'ciceromark.cto'); - result.modelManager.addCTOModel(TemplateMarkModel.MODEL, 'templatemark.cto'); - result.factory = new Factory(result.modelManager); - result.serializer = new Serializer(result.factory, result.modelManager, { utcOffset: 0 }); - return result; -} - -const templateMarkManager = mkTemplateMarkManager(); - -/** - * Returns the concept for the template - * @param {object} introspector - the model introspector for this template - * @param {string} templateKind - either 'clause' or 'contract' - * @param {string} [conceptFullyQualifiedName] - the fully qualified name of the template concept - * @throws {Error} if no template model is found, or multiple template models are found - * @returns {object} the concept for the template - */ -function findTemplateConcept(introspector, templateKind, conceptFullyQualifiedName) { - if(conceptFullyQualifiedName) { - return introspector.getClassDeclaration(conceptFullyQualifiedName); - } - else { - const templateModels = introspector.getClassDeclarations().filter((item) => { - return !item.isAbstract() && item.getDecorator('template'); - }); - if (templateModels.length > 1) { - throw new Error('Found multiple concepts with @template decorator. The model for the template must contain a single concept with the @template decorator.'); - } else if (templateModels.length === 0) { - throw new Error('Failed to find a concept with the @template decorator. The model for the template must contain a single concept with the @template decoratpr.'); - } else { - return templateModels[0]; - } - } -} - -/** - * Returns the template model for a type - * @param {object} introspector - the model introspector for this template - * @param {string} elementType - the element type - * @throws {Error} if no template model is found, or multiple template models are found - * @returns {object} the template model for the template - */ -function findElementModel(introspector, elementType) { - return introspector.getClassDeclaration(elementType); -} - -/** - * Decorate TemplateMark DOM with its types - * @param {object} template the TemplateMark DOM - * @param {object} introspector - the introspector for this template - * @param {string} model - the model - * @param {string} templateKind - either 'clause' or 'contract' - * @param {object} options - optional parameters - * @param {number} [options.utcOffset] - UTC Offset for this execution - * @returns {object} the typed TemplateMark DOM - */ -function templateMarkTypingGen(template,introspector,model,templateKind,options) { - const input = templateMarkManager.serializer.fromJSON(template,options); - - const parameters = { - templateMarkModelManager: templateMarkManager.modelManager, - introspector: introspector, - model: model, - kind: templateKind, - }; - const visitor = new TypeVisitor(); - input.accept(visitor, parameters); - let result = Object.assign({}, templateMarkManager.serializer.toJSON(input,options)); - - // Calculates formula dependencies - const fvisitor = new FormulaVisitor(); - result = fvisitor.calculateDependencies(templateMarkManager.modelManager.serializer,result,options); - return result; -} - -/** - * Decorate TemplateMark DOM with its types - * @param {object} template the TemplateMark DOM - * @param {object} modelManager - the modelManager for this template - * @param {string} templateKind - either 'clause' or 'contract' - * @param {string} [conceptFullyQualifiedName] - the fully qualified name of the template concept - * @returns {object} the typed TemplateMark DOM - */ -function templateMarkTyping(template,modelManager,templateKind,conceptFullyQualifiedName) { - const introspector = new Introspector(modelManager); - const model = findTemplateConcept(introspector, templateKind,conceptFullyQualifiedName); - return templateMarkTypingGen(template,introspector,model,templateKind); -} - -/** - * Decorate TemplateMark DOM with its types - * @param {object} template the TemplateMark DOM - * @param {object} modelManager - the modelManager for this template - * @param {string} elementType - the element type - * @returns {object} the typed TemplateMark DOM - */ -function templateMarkTypingFromType(template,modelManager,elementType) { - const introspector = new Introspector(modelManager); - const model = findElementModel(introspector, elementType); - - const rootNode = { - '$class': `${CommonMarkModel.NAMESPACE}.Document`, - 'xmlns' : 'http://commonmark.org/xml/1.0', - 'nodes': [{ - '$class': `${TemplateMarkModel.NAMESPACE}.ContractDefinition`, - 'name': 'top', - 'nodes': template - }] - }; - const rootNodeTyped = templateMarkTypingGen(rootNode,introspector,model,'clause'); - return rootNodeTyped.nodes[0].nodes; -} - -/** - * Converts a templatemark string to a token stream - * @param {object} input the templatemark string - * @returns {object} the token stream - */ -function templateToTokens(input) { - const norm = normalizeNLs(input); - const parser = new MarkdownIt({html:true}).use(MarkdownItTemplate); - return parser.parse(norm,{}); -} - -/** - * Converts a template token strean string to an untyped TemplateMark DOM - * @param {object} tokenStream the template token stream - * @returns {object} the TemplateMark DOM - */ -function tokensToUntypedTemplateMarkGen(tokenStream) { - const fromMarkdownIt = new FromMarkdownIt(templaterules); - const partialTemplate = fromMarkdownIt.toCommonMark(tokenStream); - const result = templateMarkManager.serializer.toJSON(templateMarkManager.serializer.fromJSON(partialTemplate)); - return result.nodes; -} - -/** - * Converts a template token strean string to an untyped TemplateMark DOM - * @param {object} tokenStream the template token stream - * @param {string} templateKind - either 'clause' or 'contract' - * @returns {object} the TemplateMark DOM - */ -function tokensToUntypedTemplateMark(tokenStream, templateKind) { - const partialTemplate = tokensToUntypedTemplateMarkGen(tokenStream); - - if (templateKind === 'contract') { - return { - '$class': `${CommonMarkModel.NAMESPACE}.Document`, - 'xmlns' : 'http://commonmark.org/xml/1.0', - 'nodes': [{ - '$class': `${TemplateMarkModel.NAMESPACE}.ContractDefinition`, - 'name': 'top', - 'nodes': partialTemplate - }] - }; - } else { - return { - '$class': `${CommonMarkModel.NAMESPACE}.Document`, - 'xmlns' : 'http://commonmark.org/xml/1.0', - 'nodes': [{ - '$class': `${TemplateMarkModel.NAMESPACE}.ClauseDefinition`, - 'name': 'top', - 'nodes': partialTemplate - }] - }; - } -} - -/** - * Converts a template token strean string to an untyped TemplateMark DOM - * @param {object} tokenStream the template token stream - * @param {string} templateKind - either 'clause' or 'contract' - * @returns {object} the TemplateMark DOM - */ -function tokensToUntypedTemplateMarkFragment(tokenStream) { - const partialTemplate = tokensToUntypedTemplateMarkGen(tokenStream); - return { - '$class': `${CommonMarkModel.NAMESPACE}.Document`, - 'xmlns' : 'http://commonmark.org/xml/1.0', - 'nodes': [{ - '$class': `${TemplateMarkModel.NAMESPACE}.ClauseDefinition`, - 'name': 'top', - 'nodes': partialTemplate - }] - }; -} - -module.exports.findTemplateConcept = findTemplateConcept; -module.exports.templateMarkManager = templateMarkManager; - -module.exports.templateToTokens = templateToTokens; -module.exports.tokensToUntypedTemplateMarkFragment = tokensToUntypedTemplateMarkFragment; -module.exports.tokensToUntypedTemplateMark = tokensToUntypedTemplateMark; -module.exports.templateMarkTyping = templateMarkTyping; -module.exports.templateMarkTypingFromType = templateMarkTypingFromType; diff --git a/packages/markdown-template/src/templatemarkutil.ts b/packages/markdown-template/src/templatemarkutil.ts new file mode 100644 index 00000000..8dc58abf --- /dev/null +++ b/packages/markdown-template/src/templatemarkutil.ts @@ -0,0 +1,197 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import dayjs from 'dayjs'; +import utc from 'dayjs/plugin/utc'; +dayjs.extend(utc); + +import { ModelManager, Factory, Serializer, Introspector } from '@accordproject/concerto-core'; +import { + CommonMarkModel, + CiceroMarkModel, + ConcertoMetaModel, + TemplateMarkModel, + FromMarkdownIt, +} from '@accordproject/markdown-common'; + +import { normalizeNLs } from './normalize'; +import { TypeVisitor } from './TypeVisitor'; +import { FormulaVisitor } from './FormulaVisitor'; +import MarkdownIt from 'markdown-it'; +import MarkdownItTemplate = require('@accordproject/markdown-it-template'); +import templaterules from './templaterules'; + +export interface TemplateMarkManager { + modelManager: ModelManager; + factory: Factory; + serializer: Serializer; +} + +/** + * Model manager for TemplateMark + */ +export function mkTemplateMarkManager(_options?: any): TemplateMarkManager { + const result: any = {}; + result.modelManager = new ModelManager(); + result.modelManager.addCTOModel(CommonMarkModel.MODEL, 'commonmark.cto'); + result.modelManager.addCTOModel(ConcertoMetaModel.MODEL, 'metamodel.cto'); + result.modelManager.addCTOModel(CiceroMarkModel.MODEL, 'ciceromark.cto'); + result.modelManager.addCTOModel(TemplateMarkModel.MODEL, 'templatemark.cto'); + result.factory = new Factory(result.modelManager); + result.serializer = new Serializer(result.factory, result.modelManager, { utcOffset: 0 }); + return result; +} + +export const templateMarkManager = mkTemplateMarkManager(); + +/** + * Returns the concept for the template + */ +export function findTemplateConcept(introspector: Introspector, _templateKind: string, conceptFullyQualifiedName?: string): any { + if (conceptFullyQualifiedName) { + return introspector.getClassDeclaration(conceptFullyQualifiedName); + } else { + const templateModels = introspector.getClassDeclarations().filter((item: any) => { + return !item.isAbstract() && item.getDecorator('template'); + }); + if (templateModels.length > 1) { + throw new Error('Found multiple concepts with @template decorator. The model for the template must contain a single concept with the @template decorator.'); + } else if (templateModels.length === 0) { + throw new Error('Failed to find a concept with the @template decorator. The model for the template must contain a single concept with the @template decoratpr.'); + } else { + return templateModels[0]; + } + } +} + +/** + * Returns the template model for a type + */ +function findElementModel(introspector: Introspector, elementType: string): any { + return introspector.getClassDeclaration(elementType); +} + +/** + * Decorate TemplateMark DOM with its types + */ +function templateMarkTypingGen(template: any, introspector: Introspector, model: any, templateKind: string, options?: any): any { + const input = templateMarkManager.serializer.fromJSON(template, options); + + const parameters = { + templateMarkModelManager: templateMarkManager.modelManager, + introspector, + model, + kind: templateKind, + }; + const visitor = new TypeVisitor(); + input.accept(visitor, parameters); + let result = Object.assign({}, templateMarkManager.serializer.toJSON(input, options)); + + const fvisitor = new FormulaVisitor(); + result = fvisitor.calculateDependencies((templateMarkManager.modelManager as any).serializer, result, options); + return result; +} + +/** + * Decorate TemplateMark DOM with its types + */ +export function templateMarkTyping(template: any, modelManager: ModelManager, templateKind: string, conceptFullyQualifiedName?: string): any { + const introspector = new Introspector(modelManager); + const model = findTemplateConcept(introspector, templateKind, conceptFullyQualifiedName); + return templateMarkTypingGen(template, introspector, model, templateKind); +} + +/** + * Decorate TemplateMark DOM with its types from an element type + */ +export function templateMarkTypingFromType(template: any, modelManager: ModelManager, elementType: string): any { + const introspector = new Introspector(modelManager); + const model = findElementModel(introspector, elementType); + + const rootNode = { + '$class': `${CommonMarkModel.NAMESPACE}.Document`, + 'xmlns': 'http://commonmark.org/xml/1.0', + 'nodes': [{ + '$class': `${TemplateMarkModel.NAMESPACE}.ContractDefinition`, + 'name': 'top', + 'nodes': template, + }], + }; + const rootNodeTyped = templateMarkTypingGen(rootNode, introspector, model, 'clause'); + return rootNodeTyped.nodes[0].nodes; +} + +/** + * Converts a templatemark string to a token stream + */ +export function templateToTokens(input: string): any[] { + const norm = normalizeNLs(input); + const parser = new MarkdownIt({ html: true }).use(MarkdownItTemplate); + return parser.parse(norm, {}); +} + +/** + * Converts a template token stream string to an untyped TemplateMark DOM + */ +function tokensToUntypedTemplateMarkGen(tokenStream: any[]): any[] { + const fromMarkdownIt = new FromMarkdownIt(templaterules); + const partialTemplate = fromMarkdownIt.toCommonMark(tokenStream); + const result = templateMarkManager.serializer.toJSON(templateMarkManager.serializer.fromJSON(partialTemplate)); + return result.nodes; +} + +/** + * Converts a template token stream string to an untyped TemplateMark DOM + */ +export function tokensToUntypedTemplateMark(tokenStream: any[], templateKind: string): any { + const partialTemplate = tokensToUntypedTemplateMarkGen(tokenStream); + + if (templateKind === 'contract') { + return { + '$class': `${CommonMarkModel.NAMESPACE}.Document`, + 'xmlns': 'http://commonmark.org/xml/1.0', + 'nodes': [{ + '$class': `${TemplateMarkModel.NAMESPACE}.ContractDefinition`, + 'name': 'top', + 'nodes': partialTemplate, + }], + }; + } else { + return { + '$class': `${CommonMarkModel.NAMESPACE}.Document`, + 'xmlns': 'http://commonmark.org/xml/1.0', + 'nodes': [{ + '$class': `${TemplateMarkModel.NAMESPACE}.ClauseDefinition`, + 'name': 'top', + 'nodes': partialTemplate, + }], + }; + } +} + +/** + * Converts a template token stream string to an untyped TemplateMark DOM fragment + */ +export function tokensToUntypedTemplateMarkFragment(tokenStream: any[]): any { + const partialTemplate = tokensToUntypedTemplateMarkGen(tokenStream); + return { + '$class': `${CommonMarkModel.NAMESPACE}.Document`, + 'xmlns': 'http://commonmark.org/xml/1.0', + 'nodes': [{ + '$class': `${TemplateMarkModel.NAMESPACE}.ClauseDefinition`, + 'name': 'top', + 'nodes': partialTemplate, + }], + }; +} diff --git a/packages/markdown-template/src/templaterules.js b/packages/markdown-template/src/templaterules.ts similarity index 69% rename from packages/markdown-template/src/templaterules.js rename to packages/markdown-template/src/templaterules.ts index b1886ac8..9fedf24b 100644 --- a/packages/markdown-template/src/templaterules.js +++ b/packages/markdown-template/src/templaterules.ts @@ -12,11 +12,10 @@ * limitations under the License. */ -'use strict'; +import { CommonMarkUtils, TemplateMarkModel } from '@accordproject/markdown-common'; +import { formulaName } from './util'; -const formulaName = require('./util').formulaName; -const { getAttr } = require('@accordproject/markdown-common').CommonMarkUtils; -const { TemplateMarkModel } = require('@accordproject/markdown-common'); +const { getAttr } = CommonMarkUtils; // Inline rules const variableRule = { @@ -24,30 +23,30 @@ const variableRule = { leaf: true, open: false, close: false, - enter: (node,token,callback) => { - const format = getAttr(token.attrs,'format',null); + enter: (node: any, token: any) => { + const format = getAttr(token.attrs, 'format', null); if (format) { node.$class = `${TemplateMarkModel.NAMESPACE}.FormattedVariableDefinition`; node.format = format; } - node.name = getAttr(token.attrs,'name',null); - node.format = getAttr(token.attrs,'format',null); + node.name = getAttr(token.attrs, 'name', null); + node.format = getAttr(token.attrs, 'format', null); }, skipEmpty: false, }; -const thisRule = { // 'this' is a special variable for the current data in scope within the template +const thisRule = { tag: `${TemplateMarkModel.NAMESPACE}.VariableDefinition`, leaf: true, open: false, close: false, - enter: (node,token,callback) => { - const format = getAttr(token.attrs,'format',null); + enter: (node: any, token: any) => { + const format = getAttr(token.attrs, 'format', null); if (format) { node.$class = `${TemplateMarkModel.NAMESPACE}.FormattedVariableDefinition`; node.format = format; } node.name = 'this'; - node.format = getAttr(token.attrs,'format',null); + node.format = getAttr(token.attrs, 'format', null); }, skipEmpty: false, }; @@ -56,13 +55,13 @@ const formulaRule = { leaf: true, open: false, close: false, - enter: (node,token,callback) => { + enter: (node: any, token: any) => { const code = token.content; node.name = formulaName(code); node.code = { - $class: `${TemplateMarkModel.NAMESPACE}.Code`, + $class: `${TemplateMarkModel.NAMESPACE}.Code`, type: 'TYPESCRIPT', - contents: code + contents: code, }; node.dependencies = []; }, @@ -73,14 +72,14 @@ const ifOpenRule = { leaf: false, open: true, close: false, - enter: (node,token,callback) => { - node.name = getAttr(token.attrs,'name',null); - const condition = getAttr(token.attrs,'condition',null); - if(condition) { + enter: (node: any, token: any) => { + node.name = getAttr(token.attrs, 'name', null); + const condition = getAttr(token.attrs, 'condition', null); + if (condition) { node.condition = { - $class: `${TemplateMarkModel.NAMESPACE}.Code`, + $class: `${TemplateMarkModel.NAMESPACE}.Code`, type: 'TYPESCRIPT', - contents: condition + contents: condition, }; } node.whenTrue = null; @@ -93,14 +92,14 @@ const ifCloseRule = { leaf: false, open: false, close: true, - exit: (node,token,callback) => { + exit: (node: any) => { if (node.whenTrue) { node.whenFalse = node.nodes ? node.nodes : []; } else { node.whenTrue = node.nodes ? node.nodes : []; node.whenFalse = []; } - delete node.nodes; // Delete children (now in whenTrue or whenFalse) + delete node.nodes; }, skipEmpty: false, }; @@ -109,13 +108,13 @@ const elseRule = { leaf: false, open: false, close: false, - enter: (node,token,callback) => { + enter: (node: any) => { if (node.$class === `${TemplateMarkModel.NAMESPACE}.ConditionalDefinition`) { node.whenTrue = node.nodes ? node.nodes : []; - node.nodes = []; // Reset children (now in whenTrue) - } else { // Optional definition + node.nodes = []; + } else { node.whenSome = node.nodes ? node.nodes : []; - node.nodes = []; // Reset children (now in whenSome) + node.nodes = []; } }, skipEmpty: false, @@ -125,8 +124,8 @@ const optionalOpenRule = { leaf: false, open: true, close: false, - enter: (node,token,callback) => { - node.name = getAttr(token.attrs,'name',null); + enter: (node: any, token: any) => { + node.name = getAttr(token.attrs, 'name', null); node.whenSome = null; node.whenNone = null; }, @@ -137,14 +136,14 @@ const optionalCloseRule = { leaf: false, open: false, close: true, - exit: (node,token,callback) => { + exit: (node: any) => { if (node.whenSome) { node.whenNone = node.nodes ? node.nodes : []; } else { node.whenSome = node.nodes ? node.nodes : []; node.whenNone = []; } - delete node.nodes; // Delete children (now in whenSome or whenNone) + delete node.nodes; }, skipEmpty: false, }; @@ -153,7 +152,9 @@ const withOpenRule = { leaf: false, open: true, close: false, - enter: (node,token,callback) => { node.name = getAttr(token.attrs,'name',null); }, + enter: (node: any, token: any) => { + node.name = getAttr(token.attrs, 'name', null); + }, skipEmpty: false, }; const withCloseRule = { @@ -167,12 +168,12 @@ const joinOpenRule = { leaf: false, open: true, close: false, - enter: (node,token,callback) => { - node.name = getAttr(token.attrs,'name',null); - node.separator = getAttr(token.attrs,'separator',null); - node.locale = getAttr(token.attrs,'locale',null); - node.type = getAttr(token.attrs,'type',null); - node.style = getAttr(token.attrs,'style',null); + enter: (node: any, token: any) => { + node.name = getAttr(token.attrs, 'name', null); + node.separator = getAttr(token.attrs, 'separator', null); + node.locale = getAttr(token.attrs, 'locale', null); + node.type = getAttr(token.attrs, 'type', null); + node.style = getAttr(token.attrs, 'style', null); }, skipEmpty: false, }; @@ -189,14 +190,14 @@ const clauseOpenRule = { leaf: false, open: true, close: false, - enter: (node,token,callback) => { - node.name = getAttr(token.attrs,'name',null); - const condition = getAttr(token.attrs,'condition',null); - if(condition) { + enter: (node: any, token: any) => { + node.name = getAttr(token.attrs, 'name', null); + const condition = getAttr(token.attrs, 'condition', null); + if (condition) { node.condition = { - $class: `${TemplateMarkModel.NAMESPACE}.Code`, + $class: `${TemplateMarkModel.NAMESPACE}.Code`, type: 'TYPESCRIPT', - contents: condition + contents: condition, }; } }, @@ -212,8 +213,8 @@ const ulistOpenRule = { leaf: false, open: true, close: false, - enter: (node,token,callback) => { - node.name = getAttr(token.attrs,'name',null); + enter: (node: any, token: any) => { + node.name = getAttr(token.attrs, 'name', null); node.type = 'bullet'; node.tight = 'true'; }, @@ -229,8 +230,8 @@ const olistOpenRule = { leaf: false, open: true, close: false, - enter: (node,token,callback) => { - node.name = getAttr(token.attrs,'name',null); + enter: (node: any, token: any) => { + node.name = getAttr(token.attrs, 'name', null); node.type = 'ordered'; node.tight = 'true'; node.start = '1'; @@ -244,7 +245,7 @@ const olistCloseRule = { close: true, }; -const rules = { inlines: {}, blocks: {}}; +const rules: any = { inlines: {}, blocks: {} }; rules.inlines.variable = variableRule; rules.inlines.this = thisRule; rules.inlines.formula = formulaRule; @@ -265,4 +266,4 @@ rules.blocks.block_ulist_close = ulistCloseRule; rules.blocks.block_olist_open = olistOpenRule; rules.blocks.block_olist_close = olistCloseRule; -module.exports = rules; +export default rules; diff --git a/packages/markdown-template/src/util.js b/packages/markdown-template/src/util.ts similarity index 64% rename from packages/markdown-template/src/util.js rename to packages/markdown-template/src/util.ts index 9c9630b9..80818089 100644 --- a/packages/markdown-template/src/util.js +++ b/packages/markdown-template/src/util.ts @@ -12,29 +12,20 @@ * limitations under the License. */ -'use strict'; - -const crypto = require('crypto'); +import * as crypto from 'crypto'; /** - * Flatten an array of array - * @param {*[]} arr the input array - * @return {*[]} the flattened array + * Flatten an array of arrays */ -function flatten(arr) { - return arr.reduce((acc, val) => acc.concat(val), []); +export function flatten(arr: T[][]): T[] { + return arr.reduce((acc, val) => acc.concat(val), []); } /** * Returns a unique chosen name for a formula - * @param {string} code - the formula code - * @return {string} the unique name */ -function formulaName(code) { +export function formulaName(code: string): string { const hasher = crypto.createHash('sha256'); hasher.update(code); return 'formula_' + hasher.digest('hex'); } - -module.exports.flatten = flatten; -module.exports.formulaName = formulaName; diff --git a/packages/markdown-template/test/normalize.js b/packages/markdown-template/test/normalize.js deleted file mode 100644 index 54b3fb52..00000000 --- a/packages/markdown-template/test/normalize.js +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -const chai = require('chai'); -chai.use(require('chai-string')); - -chai.should(); -chai.use(require('chai-things')); -chai.use(require('chai-as-promised')); - -// Basic parser constructors -const normalizeNLs = require('../lib/normalize').normalizeNLs; -const normalizeFromMarkdownCicero = require('../lib/normalize').normalizeFromMarkdownCicero; -const normalizeToMarkdownCicero = require('../lib/normalize').normalizeToMarkdownCicero; - -describe('#normalize', () => { - describe('#normalizeNLs', () => { - it('should normalize to \n', async () => { - normalizeNLs('Hello\r\nWorld!').should.equal('Hello\nWorld!'); - }); - }); - describe('#normalizeMarkdownCicero', () => { - it('should normalize to \n', async () => { - normalizeToMarkdownCicero(normalizeFromMarkdownCicero('Hello\r\nWorld!')).should.equal('Hello\nWorld!'); - }); - }); -}); diff --git a/packages/markdown-template/tsconfig.json b/packages/markdown-template/tsconfig.json index d2e666ea..fb3e9f82 100644 --- a/packages/markdown-template/tsconfig.json +++ b/packages/markdown-template/tsconfig.json @@ -1,10 +1,11 @@ { + "extends": "../../tsconfig.base.json", "compilerOptions": { - "allowJs": true, + "rootDir": "src", + "outDir": "lib", "declaration": true, - "emitDeclarationOnly": true, - "outDir": "types", - "strict": false + "sourceMap": true }, - "include": ["index.js", "lib/**/*.js"] + "include": ["src/**/*.ts"], + "exclude": ["src/**/*.test.ts", "lib", "node_modules"] } diff --git a/packages/markdown-template/tsconfig.test.json b/packages/markdown-template/tsconfig.test.json new file mode 100644 index 00000000..58810225 --- /dev/null +++ b/packages/markdown-template/tsconfig.test.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "rootDir": ".", + "noEmit": true + }, + "include": ["src/**/*.ts"], + "exclude": ["lib", "node_modules"] +} diff --git a/packages/markdown-template/types/index.d.ts b/packages/markdown-template/types/index.d.ts deleted file mode 100644 index 1434af5a..00000000 --- a/packages/markdown-template/types/index.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -export const util: typeof import("./lib/util"); -export const templatemarkutil: typeof import("./lib/templatemarkutil"); -export const datetimeutil: typeof import("./lib/datetimeutil"); -export const normalizeNLs: typeof import("./lib/normalize").normalizeNLs; -export const TemplateException: typeof import("./lib/templateexception"); -export const TemplateMarkTransformer: typeof import("./lib/TemplateMarkTransformer"); diff --git a/packages/markdown-template/types/lib/FormulaVisitor.d.ts b/packages/markdown-template/types/lib/FormulaVisitor.d.ts deleted file mode 100644 index 64e1cb65..00000000 --- a/packages/markdown-template/types/lib/FormulaVisitor.d.ts +++ /dev/null @@ -1,55 +0,0 @@ -export = FormulaVisitor; -/** - * Converts a CommonMark DOM to a CiceroMark DOM - */ -declare class FormulaVisitor { - /** - * Visits a sub-tree and return CiceroMark DOM - * @param {*} visitor the visitor to use - * @param {*} thing the node to visit - * @param {*} [parameters] optional parameters - * @param {string} field where the children are - */ - static visitChildren(visitor: any, thing: any, parameters?: any, ...args: any[]): void; - /** - * Visits a list of nodes and return the CiceroMark DOM - * @param {*} visitor the visitor to use - * @param {*} things the list node to visit - * @param {*} [parameters] optional parameters - */ - static visitNodes(visitor: any, things: any, parameters?: any): void; - /** - * Calculates the dependencies for TS code - * @param {string} tsCode the TS code to analyze - * @returns {string[]} array of dependencies - */ - static calculateDependencies(tsCode: string): string[]; - /** - * Visit a node - * @param {*} thing the object being visited - * @param {*} parameters the parameters - */ - visit(thing: any, parameters: any): void; - /** - * Calculate dependencies - * @param {*} serializer - the template mark serializer - * @param {object} ast - the template AST - * @param {object} options - options - * @param {number} [options.utcOffset] - UTC Offset for this execution - * @returns {*} the formulas - */ - calculateDependencies(serializer: any, ast: object, options: { - utcOffset?: number; - }): any; - /** - * Process formulas and returns the list of those formulas from a TemplateMark DOM - * @param {*} serializer - the template mark serializer - * @param {object} ast - the template AST - * @param {object} options - options - * @param {number} [options.utcOffset] - UTC Offset for this execution - * @returns {*} the formulas - */ - processFormulas(serializer: any, ast: object, options: { - utcOffset?: number; - }): any; -} diff --git a/packages/markdown-template/types/lib/ModelVisitor.d.ts b/packages/markdown-template/types/lib/ModelVisitor.d.ts deleted file mode 100644 index 3646072b..00000000 --- a/packages/markdown-template/types/lib/ModelVisitor.d.ts +++ /dev/null @@ -1,55 +0,0 @@ -export = ModelVisitor; -/** - * Converts concerto models to TemplateMark - * - * @private - * @class - */ -declare class ModelVisitor { - /** - * Visitor design pattern - * @param {Object} thing - the object being visited - * @param {Object} parameters - the parameter - * @return {Object} the result of visiting or null - * @private - */ - private visit; - /** - * Visitor design pattern - * @param {EnumDeclaration} enumDeclaration - the object being visited - * @param {Object} parameters - the parameter - * @return {Object} the result of visiting or null - * @private - */ - private visitEnumDeclaration; - /** - * Visitor design pattern - * @param {ClassDeclaration} classDeclaration - the object being visited - * @param {Object} parameters - the parameter - * @return {Object} the result of visiting or null - * @private - */ - private visitClassDeclaration; - /** - * Visitor design pattern - * @param {Field} field - the object being visited - * @param {Object} parameters - the parameter - * @return {Object} the result of visiting or null - * @private - */ - private visitField; - /** - * Visitor design pattern - * @param {EnumValueDeclaration} enumValueDeclaration - the object being visited - * @param {Object} parameters - the parameter - * @private - */ - private visitEnumValueDeclaration; - /** - * Visitor design pattern - * @param {Relationship} relationship - the object being visited - * @param {Object} parameters - the parameter - * @private - */ - private visitRelationshipDeclaration; -} diff --git a/packages/markdown-template/types/lib/TemplateMarkTransformer.d.ts b/packages/markdown-template/types/lib/TemplateMarkTransformer.d.ts deleted file mode 100644 index d2409c8c..00000000 --- a/packages/markdown-template/types/lib/TemplateMarkTransformer.d.ts +++ /dev/null @@ -1,56 +0,0 @@ -export = TemplateMarkTransformer; -/** - * Support for TemplateMark Templates - */ -declare class TemplateMarkTransformer { - /** - * Converts a template string to a token stream - * @param {object} templateInput the template template - * @returns {object} the token stream - */ - toTokens(templateInput: object): object; - /** - * Converts a template token strean string to a TemplateMark DOM - * @param {object} tokenStream the template token stream - * @param {object} modelManager - the model manager for this template - * @param {string} templateKind - either 'clause' or 'contract' - * @param {object} [options] configuration options - * @param {boolean} [options.verbose] verbose output - * @param {string} [conceptFullyQualifiedName] - the fully qualified name of the template concept - * @returns {object} the result of parsing - */ - tokensToMarkdownTemplate(tokenStream: object, modelManager: object, templateKind: string, options?: { - verbose?: boolean; - }, conceptFullyQualifiedName?: string): object; - /** - * Converts a markdown string to a TemplateMark DOM - * @param {{fileName:string,content:string}} templateInput the template template - * @param {object} modelManager - the model manager for this template - * @param {string} templateKind - either 'clause' or 'contract' - * @param {object} [options] configuration options - * @param {boolean} [options.verbose] verbose output - * @param {string} [conceptFullyQualifiedName] - the fully qualified name of the template concept - * @returns {object} the result of parsing - */ - fromMarkdownTemplate(templateInput: { - fileName: string; - content: string; - }, modelManager: object, templateKind: string, options?: { - verbose?: boolean; - }, conceptFullyQualifiedName?: string): object; - /** - * Converts a TemplateMark DOM to a template markdown string - * @param {object} input TemplateMark DOM - * @returns {string} the template markdown text - */ - toMarkdownTemplate(input: object): string; - /** - * Get TemplateMark serializer - * @return {Serializer} templatemark serializer - */ - getSerializer(): Serializer; -} -declare namespace TemplateMarkTransformer { - export { Serializer }; -} -type Serializer = import("@accordproject/concerto-core").Serializer; diff --git a/packages/markdown-template/types/lib/ToCiceroMarkVisitor.d.ts b/packages/markdown-template/types/lib/ToCiceroMarkVisitor.d.ts deleted file mode 100644 index 00832445..00000000 --- a/packages/markdown-template/types/lib/ToCiceroMarkVisitor.d.ts +++ /dev/null @@ -1,50 +0,0 @@ -export = ToCiceroMarkVisitor; -/** - * Drafts a CiceroMark DOM from a TemplateMark DOM - */ -declare class ToCiceroMarkVisitor { - /** - * Clone a CiceroMark node - * @param {*} serializer the serializer - * @param {*} node the node to visit - * @param {*} [parameters] optional parameters - * @return {*} the cloned node - */ - static cloneNode(serializer: any, node: any): any; - /** - * Visits a sub-tree and return CiceroMark DOM - * @param {*} visitor the visitor to use - * @param {*} thing the node to visit - * @param {*} [parameters] optional parameters - */ - static visitChildren(visitor: any, thing: any, parameters?: any): void; - /** - * Visits a list of nodes and return the CiceroMark DOM - * @param {*} visitor the visitor to use - * @param {*} things the list node to visit - * @param {*} [parameters] optional parameters - * @return {*} the visited nodes - */ - static visitNodes(visitor: any, things: any, parameters?: any): any; - /** - * Match template tag to instance tag - * @param {string} tag the template tag - * @return {string} the corresponding instance tag - */ - static matchTag(tag: string): string; - /** - * Evaluates a JS expression - * @param {*} data the contract data - * @param {string} expression the JS expression - * @param {Date} now the current value for now - * @returns {Boolean} the result of evaluating the expression against the data - */ - static eval(data: any, expression: string, now: Date): boolean; - /** - * Visit a node - * @param {*} thing the object being visited - * @param {*} parameters the parameters - * @return {*} the visited nodes - */ - visit(thing: any, parameters: any): any; -} diff --git a/packages/markdown-template/types/lib/ToMarkdownTemplateVisitor.d.ts b/packages/markdown-template/types/lib/ToMarkdownTemplateVisitor.d.ts deleted file mode 100644 index 4187a9c7..00000000 --- a/packages/markdown-template/types/lib/ToMarkdownTemplateVisitor.d.ts +++ /dev/null @@ -1,21 +0,0 @@ -export = ToMarkdownTemplateVisitor; -declare const ToMarkdownTemplateVisitor_base: typeof import("@accordproject/markdown-common/types/lib/FromCommonMarkVisitor"); -/** - * Converts a TemplateMark DOM to a template markdown string. - */ -declare class ToMarkdownTemplateVisitor extends ToMarkdownTemplateVisitor_base { - /** - * Construct the visitor. - * @param {object} [options] configuration options - * @param {*} resultSeq how to sequentially combine results - * @param {object} rules how to process each node type - */ - constructor(options?: object); - /** - * Converts a TemplateMark DOM to a template markdown string. - * @param {*} serializer - TemplateMark serializer - * @param {*} input - TemplateMark DOM (JSON) - * @returns {string} the template markdown string - */ - toMarkdownTemplate(serializer: any, input: any): string; -} diff --git a/packages/markdown-template/types/lib/ToParserVisitor.d.ts b/packages/markdown-template/types/lib/ToParserVisitor.d.ts deleted file mode 100644 index 6b740525..00000000 --- a/packages/markdown-template/types/lib/ToParserVisitor.d.ts +++ /dev/null @@ -1,30 +0,0 @@ -export = ToParserVisitor; -declare const ToParserVisitor_base: typeof import("@accordproject/markdown-common/types/lib/FromCommonMarkVisitor"); -/** - * Converts a TemplateMark DOM to a parser. - */ -declare class ToParserVisitor extends ToParserVisitor_base { - /** - * Converts a TemplateMark DOM to a parser for that node, given parameters - * @param {object} visitor - the visitor - * @param {object} ast - the template AST - * @param {object} parameters - current parameters - * @returns {object} the parser - */ - static toParserWithParameters(visitor: object, ast: object, parameters: object): object; - /** - * Construct the visitor. - * @param {object} [options] configuration options - * @param {*} resultSeq how to sequentially combine results - * @param {object} rules how to process each node type - */ - constructor(options?: object); - /** - * Converts a TemplateMark DOM to a full parser - * @param {*} parserManager - the parser manager - * @param {object} ast - the template AST - * @param {object} parsingTable - the parsing table - * @returns {object} the parser - */ - toParser(parserManager: any, ast: object, parsingTable: object): object; -} diff --git a/packages/markdown-template/types/lib/TypeVisitor.d.ts b/packages/markdown-template/types/lib/TypeVisitor.d.ts deleted file mode 100644 index a9b80f6b..00000000 --- a/packages/markdown-template/types/lib/TypeVisitor.d.ts +++ /dev/null @@ -1,38 +0,0 @@ -export = TypeVisitor; -/** - * Adds the elementType property to a TemplateMark DOM - * along with type specific metadata. This visitor verifies - * the structure of a template with respect to an associated - * template model and annotates the TemplateMark DOM with model - * information for use in downstream tools. - */ -declare class TypeVisitor { - /** - * Visits a sub-tree and return CiceroMark DOM - * @param {*} visitor the visitor to use - * @param {*} thing the node to visit - * @param {*} [parameters] optional parameters - * @param {string} field where the children are - */ - static visitChildren(visitor: any, thing: any, parameters?: any, ...args: any[]): void; - /** - * Visits a list of nodes and return the CiceroMark DOM - * @param {*} visitor the visitor to use - * @param {*} things the list node to visit - * @param {*} [parameters] optional parameters - */ - static visitNodes(visitor: any, things: any, parameters?: any): void; - /** - * Get the type information for a property - * @param {*} property the propety - * @param {*} parameters the configuration parameters - * @returns {*} the information about the next model element (property or declaration) - */ - static nextModel(property: any, parameters: any): any; - /** - * Visit a node - * @param {*} thing the object being visited - * @param {*} parameters the parameters - */ - visit(thing: any, parameters: any): void; -} diff --git a/packages/markdown-template/types/lib/datetimeutil.d.ts b/packages/markdown-template/types/lib/datetimeutil.d.ts deleted file mode 100644 index fa8edb2b..00000000 --- a/packages/markdown-template/types/lib/datetimeutil.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Ensures there is a proper current time - * - * @param {string} currentTime - the definition of 'now' - * @returns {object} if valid, the dayjs object for the current time - */ -export function setCurrentTime(currentTime: string): object; diff --git a/packages/markdown-template/types/lib/errorutil.d.ts b/packages/markdown-template/types/lib/errorutil.d.ts deleted file mode 100644 index 33b5cf18..00000000 --- a/packages/markdown-template/types/lib/errorutil.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Throw a template exception for the element - * @param {string} message - the error message - * @param {object} element the AST - * @throws {TemplateException} - */ -export function _throwTemplateExceptionForElement(message: string, element: object): void; diff --git a/packages/markdown-template/types/lib/fromtemplatemarkrules.d.ts b/packages/markdown-template/types/lib/fromtemplatemarkrules.d.ts deleted file mode 100644 index 315d5e70..00000000 --- a/packages/markdown-template/types/lib/fromtemplatemarkrules.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -export function VariableDefinition(visitor: any, thing: any, children: any, parameters: any, resultString: any, resultSeq: any): void; -export function FormattedVariableDefinition(visitor: any, thing: any, children: any, parameters: any, resultString: any, resultSeq: any): void; -export function EnumVariableDefinition(visitor: any, thing: any, children: any, parameters: any, resultString: any, resultSeq: any): void; -export function FormulaDefinition(visitor: any, thing: any, children: any, parameters: any, resultString: any, resultSeq: any): void; -export function ConditionalDefinition(visitor: any, thing: any, children: any, parameters: any, resultString: any, resultSeq: any): void; -export function OptionalDefinition(visitor: any, thing: any, children: any, parameters: any, resultString: any, resultSeq: any): void; -export function WithDefinition(visitor: any, thing: any, children: any, parameters: any, resultString: any, resultSeq: any): void; -export function JoinDefinition(visitor: any, thing: any, children: any, parameters: any, resultString: any, resultSeq: any): void; -export function ListBlockDefinition(visitor: any, thing: any, children: any, parameters: any, resultString: any, resultSeq: any): void; -export function ClauseDefinition(visitor: any, thing: any, children: any, parameters: any, resultString: any, resultSeq: any): void; diff --git a/packages/markdown-template/types/lib/normalize.d.ts b/packages/markdown-template/types/lib/normalize.d.ts deleted file mode 100644 index f0afdbaf..00000000 --- a/packages/markdown-template/types/lib/normalize.d.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Prepare the text for parsing (normalizes new lines, etc) - * @param {string} input - the text - * @return {string} - the normalized text - */ -export function normalizeNLs(input: string): string; -/** - * Normalize to markdown cicero text - * @param {*} input - the CiceroMark DOM - * @return {string} - the normalized markdown cicero text - */ -export function normalizeToMarkdownCicero(input: any): string; -/** - * Normalize from markdown cicero text - * @param {string} input - the markdown cicero text - * @return {object} - the normalized CiceroMark DOM - */ -export function normalizeFromMarkdownCicero(input: string): object; diff --git a/packages/markdown-template/types/lib/templateexception.d.ts b/packages/markdown-template/types/lib/templateexception.d.ts deleted file mode 100644 index a3008454..00000000 --- a/packages/markdown-template/types/lib/templateexception.d.ts +++ /dev/null @@ -1,20 +0,0 @@ -export = TemplateException; -/** - * Exception thrown for invalid templates - * @extends BaseFileException - * @see See {@link BaseFileException} - * @class - * @memberof module:markdown-template - * @private - */ -declare class TemplateException { - /** - * Create a TemplateException - * @param {string} message - the message for the exception - * @param {string} fileLocation - the optional file location associated with the exception - * @param {string} fileName - the optional file name associated with the exception - * @param {string} fullMessageOverride - the optional pre-existing full message - * @param {string} component - the optional component which throws this error - */ - constructor(message: string, fileLocation: string, fileName: string, fullMessageOverride: string, component: string); -} diff --git a/packages/markdown-template/types/lib/templatemarkutil.d.ts b/packages/markdown-template/types/lib/templatemarkutil.d.ts deleted file mode 100644 index c5f392e1..00000000 --- a/packages/markdown-template/types/lib/templatemarkutil.d.ts +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Returns the concept for the template - * @param {object} introspector - the model introspector for this template - * @param {string} templateKind - either 'clause' or 'contract' - * @param {string} [conceptFullyQualifiedName] - the fully qualified name of the template concept - * @throws {Error} if no template model is found, or multiple template models are found - * @returns {object} the concept for the template - */ -export function findTemplateConcept(introspector: object, templateKind: string, conceptFullyQualifiedName?: string): object; -export var templateMarkManager: any; -/** - * Converts a templatemark string to a token stream - * @param {object} input the templatemark string - * @returns {object} the token stream - */ -export function templateToTokens(input: object): object; -/** - * Converts a template token strean string to an untyped TemplateMark DOM - * @param {object} tokenStream the template token stream - * @param {string} templateKind - either 'clause' or 'contract' - * @returns {object} the TemplateMark DOM - */ -export function tokensToUntypedTemplateMarkFragment(tokenStream: object): object; -/** - * Converts a template token strean string to an untyped TemplateMark DOM - * @param {object} tokenStream the template token stream - * @param {string} templateKind - either 'clause' or 'contract' - * @returns {object} the TemplateMark DOM - */ -export function tokensToUntypedTemplateMark(tokenStream: object, templateKind: string): object; -/** - * Decorate TemplateMark DOM with its types - * @param {object} template the TemplateMark DOM - * @param {object} modelManager - the modelManager for this template - * @param {string} templateKind - either 'clause' or 'contract' - * @param {string} [conceptFullyQualifiedName] - the fully qualified name of the template concept - * @returns {object} the typed TemplateMark DOM - */ -export function templateMarkTyping(template: object, modelManager: object, templateKind: string, conceptFullyQualifiedName?: string): object; -/** - * Decorate TemplateMark DOM with its types - * @param {object} template the TemplateMark DOM - * @param {object} modelManager - the modelManager for this template - * @param {string} elementType - the element type - * @returns {object} the typed TemplateMark DOM - */ -export function templateMarkTypingFromType(template: object, modelManager: object, elementType: string): object; diff --git a/packages/markdown-template/types/lib/templaterules.d.ts b/packages/markdown-template/types/lib/templaterules.d.ts deleted file mode 100644 index 5254c923..00000000 --- a/packages/markdown-template/types/lib/templaterules.d.ts +++ /dev/null @@ -1,242 +0,0 @@ -declare namespace variableRule { - let tag: string; - let leaf: boolean; - let open: boolean; - let close: boolean; - function enter(node: any, token: any, callback: any): void; - let skipEmpty: boolean; -} -declare namespace thisRule { - let tag_1: string; - export { tag_1 as tag }; - let leaf_1: boolean; - export { leaf_1 as leaf }; - let open_1: boolean; - export { open_1 as open }; - let close_1: boolean; - export { close_1 as close }; - export function enter_1(node: any, token: any, callback: any): void; - export { enter_1 as enter }; - let skipEmpty_1: boolean; - export { skipEmpty_1 as skipEmpty }; -} -declare namespace formulaRule { - let tag_2: string; - export { tag_2 as tag }; - let leaf_2: boolean; - export { leaf_2 as leaf }; - let open_2: boolean; - export { open_2 as open }; - let close_2: boolean; - export { close_2 as close }; - export function enter_2(node: any, token: any, callback: any): void; - export { enter_2 as enter }; - let skipEmpty_2: boolean; - export { skipEmpty_2 as skipEmpty }; -} -declare namespace ifOpenRule { - let tag_3: string; - export { tag_3 as tag }; - let leaf_3: boolean; - export { leaf_3 as leaf }; - let open_3: boolean; - export { open_3 as open }; - let close_3: boolean; - export { close_3 as close }; - export function enter_3(node: any, token: any, callback: any): void; - export { enter_3 as enter }; - let skipEmpty_3: boolean; - export { skipEmpty_3 as skipEmpty }; -} -declare namespace ifCloseRule { - let tag_4: string; - export { tag_4 as tag }; - let leaf_4: boolean; - export { leaf_4 as leaf }; - let open_4: boolean; - export { open_4 as open }; - let close_4: boolean; - export { close_4 as close }; - export function exit(node: any, token: any, callback: any): void; - let skipEmpty_4: boolean; - export { skipEmpty_4 as skipEmpty }; -} -declare namespace optionalOpenRule { - let tag_5: string; - export { tag_5 as tag }; - let leaf_5: boolean; - export { leaf_5 as leaf }; - let open_5: boolean; - export { open_5 as open }; - let close_5: boolean; - export { close_5 as close }; - export function enter_4(node: any, token: any, callback: any): void; - export { enter_4 as enter }; - let skipEmpty_5: boolean; - export { skipEmpty_5 as skipEmpty }; -} -declare namespace optionalCloseRule { - let tag_6: string; - export { tag_6 as tag }; - let leaf_6: boolean; - export { leaf_6 as leaf }; - let open_6: boolean; - export { open_6 as open }; - let close_6: boolean; - export { close_6 as close }; - export function exit_1(node: any, token: any, callback: any): void; - export { exit_1 as exit }; - let skipEmpty_6: boolean; - export { skipEmpty_6 as skipEmpty }; -} -declare namespace elseRule { - let tag_7: string; - export { tag_7 as tag }; - let leaf_7: boolean; - export { leaf_7 as leaf }; - let open_7: boolean; - export { open_7 as open }; - let close_7: boolean; - export { close_7 as close }; - export function enter_5(node: any, token: any, callback: any): void; - export { enter_5 as enter }; - let skipEmpty_7: boolean; - export { skipEmpty_7 as skipEmpty }; -} -declare namespace withOpenRule { - let tag_8: string; - export { tag_8 as tag }; - let leaf_8: boolean; - export { leaf_8 as leaf }; - let open_8: boolean; - export { open_8 as open }; - let close_8: boolean; - export { close_8 as close }; - export function enter_6(node: any, token: any, callback: any): void; - export { enter_6 as enter }; - let skipEmpty_8: boolean; - export { skipEmpty_8 as skipEmpty }; -} -declare namespace withCloseRule { - let tag_9: string; - export { tag_9 as tag }; - let leaf_9: boolean; - export { leaf_9 as leaf }; - let open_9: boolean; - export { open_9 as open }; - let close_9: boolean; - export { close_9 as close }; -} -declare namespace joinOpenRule { - let tag_10: string; - export { tag_10 as tag }; - let leaf_10: boolean; - export { leaf_10 as leaf }; - let open_10: boolean; - export { open_10 as open }; - let close_10: boolean; - export { close_10 as close }; - export function enter_7(node: any, token: any, callback: any): void; - export { enter_7 as enter }; - let skipEmpty_9: boolean; - export { skipEmpty_9 as skipEmpty }; -} -declare namespace joinCloseRule { - let tag_11: string; - export { tag_11 as tag }; - let leaf_11: boolean; - export { leaf_11 as leaf }; - let open_11: boolean; - export { open_11 as open }; - let close_11: boolean; - export { close_11 as close }; -} -declare namespace clauseOpenRule { - let tag_12: string; - export { tag_12 as tag }; - let leaf_12: boolean; - export { leaf_12 as leaf }; - let open_12: boolean; - export { open_12 as open }; - let close_12: boolean; - export { close_12 as close }; - export function enter_8(node: any, token: any, callback: any): void; - export { enter_8 as enter }; -} -declare namespace clauseCloseRule { - let tag_13: string; - export { tag_13 as tag }; - let leaf_13: boolean; - export { leaf_13 as leaf }; - let open_13: boolean; - export { open_13 as open }; - let close_13: boolean; - export { close_13 as close }; -} -declare namespace ulistOpenRule { - let tag_14: string; - export { tag_14 as tag }; - let leaf_14: boolean; - export { leaf_14 as leaf }; - let open_14: boolean; - export { open_14 as open }; - let close_14: boolean; - export { close_14 as close }; - export function enter_9(node: any, token: any, callback: any): void; - export { enter_9 as enter }; -} -declare namespace ulistCloseRule { - let tag_15: string; - export { tag_15 as tag }; - let leaf_15: boolean; - export { leaf_15 as leaf }; - let open_15: boolean; - export { open_15 as open }; - let close_15: boolean; - export { close_15 as close }; -} -declare namespace olistOpenRule { - let tag_16: string; - export { tag_16 as tag }; - let leaf_16: boolean; - export { leaf_16 as leaf }; - let open_16: boolean; - export { open_16 as open }; - let close_16: boolean; - export { close_16 as close }; - export function enter_10(node: any, token: any, callback: any): void; - export { enter_10 as enter }; -} -declare namespace olistCloseRule { - let tag_17: string; - export { tag_17 as tag }; - let leaf_17: boolean; - export { leaf_17 as leaf }; - let open_17: boolean; - export { open_17 as open }; - let close_17: boolean; - export { close_17 as close }; -} -export namespace inlines { - export { variableRule as variable }; - export { thisRule as this }; - export { formulaRule as formula }; - export { ifOpenRule as inline_block_if_open }; - export { ifCloseRule as inline_block_if_close }; - export { optionalOpenRule as inline_block_optional_open }; - export { optionalCloseRule as inline_block_optional_close }; - export { elseRule as inline_block_else }; - export { withOpenRule as inline_block_with_open }; - export { withCloseRule as inline_block_with_close }; - export { joinOpenRule as inline_block_join_open }; - export { joinCloseRule as inline_block_join_close }; -} -export namespace blocks { - export { clauseOpenRule as block_clause_open }; - export { clauseCloseRule as block_clause_close }; - export { ulistOpenRule as block_ulist_open }; - export { ulistCloseRule as block_ulist_close }; - export { olistOpenRule as block_olist_open }; - export { olistCloseRule as block_olist_close }; -} -export {}; diff --git a/packages/markdown-template/types/lib/toparserrules.d.ts b/packages/markdown-template/types/lib/toparserrules.d.ts deleted file mode 100644 index 6f9962fd..00000000 --- a/packages/markdown-template/types/lib/toparserrules.d.ts +++ /dev/null @@ -1,12 +0,0 @@ -export declare function EnumVariableDefinition(visitor: any, thing: any, children: any, parameters: any, resultString: any, resultSeq: any): void; -export declare function VariableDefinition(visitor: any, thing: any, children: any, parameters: any, resultString: any, resultSeq: any): void; -import FormattedVariableDefinition = VariableDefinition; -export { FormattedVariableDefinition }; -export declare function ConditionalDefinition(visitor: any, thing: any, children: any, parameters: any, resultString: any, resultSeq: any): void; -export declare function OptionalDefinition(visitor: any, thing: any, children: any, parameters: any, resultString: any, resultSeq: any): void; -export declare function JoinDefinition(visitor: any, thing: any, children: any, parameters: any, resultString: any, resultSeq: any): void; -export declare function WithDefinition(visitor: any, thing: any, children: any, parameters: any, resultString: any, resultSeq: any): void; -export declare function FormulaDefinition(visitor: any, thing: any, children: any, parameters: any, resultString: any, resultSeq: any): void; -export declare function ListBlockDefinition(visitor: any, thing: any, children: any, parameters: any, resultString: any, resultSeq: any): void; -export declare function ClauseDefinition(visitor: any, thing: any, children: any, parameters: any, resultString: any, resultSeq: any): void; -export declare function ContractDefinition(visitor: any, thing: any, children: any, parameters: any, resultString: any, resultSeq: any): void; diff --git a/packages/markdown-template/types/lib/util.d.ts b/packages/markdown-template/types/lib/util.d.ts deleted file mode 100644 index b249e951..00000000 --- a/packages/markdown-template/types/lib/util.d.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Flatten an array of array - * @param {*[]} arr the input array - * @return {*[]} the flattened array - */ -export function flatten(arr: any[]): any[]; -/** - * Returns a unique chosen name for a formula - * @param {string} code - the formula code - * @return {string} the unique name - */ -export function formulaName(code: string): string; diff --git a/packages/markdown-template/webpack.config.js b/packages/markdown-template/webpack.config.js index 43010639..a6f30b4a 100644 --- a/packages/markdown-template/webpack.config.js +++ b/packages/markdown-template/webpack.config.js @@ -14,60 +14,32 @@ 'use strict'; -let path = require('path'); +const path = require('path'); const webpack = require('webpack'); const packageJson = require('./package.json'); module.exports = { - entry: { - client: [ - './index.js' - ] - }, + entry: { client: ['./src/index.ts'] }, output: { path: path.join(__dirname, 'umd'), filename: 'markdown-template.js', - library: { - name: 'markdown-template', - type: 'umd', - }, + library: { name: 'markdown-template', type: 'umd' }, umdNamedDefine: true, }, plugins: [ - new webpack.BannerPlugin(`Markdown Transform v${packageJson.version} - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License.`), - new webpack.DefinePlugin({ - 'process.env': { - 'NODE_ENV': JSON.stringify('production') - } - }), - new webpack.IgnorePlugin({ - resourceRegExp: /^\.$/, - contextRegExp: /jsdom$/, - }) + new webpack.BannerPlugin(`Markdown Transform v${packageJson.version}`), + new webpack.DefinePlugin({ 'process.env': { 'NODE_ENV': JSON.stringify('production') } }), + // Some transitive deps reference the Node `process` global. webpack 5 no longer + // polyfills it, so we provide a minimal browser shim. + new webpack.ProvidePlugin({ process: 'process/browser' }), + new webpack.IgnorePlugin({ resourceRegExp: /^\.$/, contextRegExp: /jsdom$/ }), ], - module: { - rules: [ - { - test: /\.js$/, - include: [path.join(__dirname, 'src')], - use: ['babel-loader'] - }, - { - test: /\.ne$/, - use:['raw-loader'] - } - ] - }, resolve: { + extensions: ['.ts', '.js'], + // Transitive deps (asn1.js, parse-asn1, etc. pulled in by crypto-browserify) + // statically reference Node built-ins on code paths that are unreachable in + // the browser. Explicitly set them to `false` so webpack doesn't emit + // "Module not found" warnings. fallback: { 'fs': false, 'tls': false, @@ -76,8 +48,22 @@ module.exports = { 'os': false, 'util': false, 'url': false, + 'vm': false, 'crypto': require.resolve('crypto-browserify'), 'stream': require.resolve('stream-browserify'), - } - } -}; \ No newline at end of file + }, + }, + module: { + rules: [ + { + test: /\.ts$/, + include: [path.join(__dirname, 'src')], + exclude: /\.test\.ts$/, + use: [{ + loader: 'ts-loader', + options: { transpileOnly: true, configFile: path.join(__dirname, 'tsconfig.json') }, + }], + }, + ], + }, +}; diff --git a/packages/markdown-transform/.eslintrc.cjs b/packages/markdown-transform/.eslintrc.cjs new file mode 100644 index 00000000..464b671d --- /dev/null +++ b/packages/markdown-transform/.eslintrc.cjs @@ -0,0 +1,37 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +module.exports = { + root: true, + env: { es2022: true, node: true, jest: true }, + extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'], + parser: '@typescript-eslint/parser', + parserOptions: { ecmaVersion: 2022, sourceType: 'module' }, + plugins: ['@typescript-eslint'], + ignorePatterns: ['node_modules/', 'lib/', 'umd/', 'coverage/'], + rules: { + 'indent': ['error', 4, { 'SwitchCase': 1 }], + 'quotes': ['error', 'single', { 'avoidEscape': true, 'allowTemplateLiterals': true }], + 'semi': ['error', 'always'], + 'no-console': 'warn', + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-var-requires': 'off', + '@typescript-eslint/no-unused-vars': ['error', { 'args': 'none', 'ignoreRestSiblings': true, 'caughtErrors': 'none' }], + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/no-empty-function': 'off', + '@typescript-eslint/ban-ts-comment': 'off', + '@typescript-eslint/no-this-alias': 'off', + 'no-unused-vars': 'off', + }, +}; diff --git a/packages/markdown-transform/.eslintrc.yml b/packages/markdown-transform/.eslintrc.yml deleted file mode 100644 index ec0c5d88..00000000 --- a/packages/markdown-transform/.eslintrc.yml +++ /dev/null @@ -1,47 +0,0 @@ -env: - es6: true - node: true - mocha: true -extends: 'eslint:recommended' -parserOptions: - ecmaVersion: 12 - sourceType: 'script' -rules: - indent: - - error - - 4 - linebreak-style: - - warn - - unix - quotes: - - error - - single - semi: - - error - - always - no-unused-vars: - - error - - args: none - no-console: warn - curly: error - eqeqeq: error - no-throw-literal: error - strict: error - no-var: error - dot-notation: error - no-tabs: error - no-trailing-spaces: error - # no-use-before-define: error - no-useless-call: error - no-with: error - operator-linebreak: error - require-jsdoc: - - error - - require: - ClassDeclaration: true - MethodDefinition: true - FunctionDeclaration: true - valid-jsdoc: - - error - - requireReturn: false - yoda: error diff --git a/packages/markdown-transform/.gitignore b/packages/markdown-transform/.gitignore new file mode 100644 index 00000000..e8b2d1ae --- /dev/null +++ b/packages/markdown-transform/.gitignore @@ -0,0 +1,5 @@ +node_modules/ +/lib +/umd +coverage +.nyc_output diff --git a/packages/markdown-transform/README.md b/packages/markdown-transform/README.md index 01c03db9..859242b0 100644 --- a/packages/markdown-transform/README.md +++ b/packages/markdown-transform/README.md @@ -1,52 +1,53 @@ # Markdown Transform API -High-level API to transform markdown into different formats. +High-level API to transform markdown content between the various formats supported by this monorepo (CommonMark, CiceroMark, TemplateMark, HTML, plaintext, CiceroEdit, etc.). ## Installation ``` -npm install -g @accordproject/markdown-transform +npm install @accordproject/markdown-transform ``` +This package depends on `markdown-common`, `markdown-cicero`, `markdown-template` and `markdown-html`, which are pulled in automatically. + ## Basic Usage -``` -const transform = require('@accordproject/markdown-transform').transform; - -// convert a markdown string to an html string -// first argument is the markdown string to be converted -// second argument is the source format -// third argument is an array of destination formats to pass through -const htmlString = await transform(markdownString, 'markdown', ['html']); -console.log(htmlString); +```ts +import { transform } from '@accordproject/markdown-transform'; + +// markdown → HTML +const html = await transform(markdown, 'markdown', ['html']); ``` -Note that the call to `transform` returns a `Promise`. +The third argument is an array of destination formats. When more than one is given, the transformation is chained through each one. -The third argument to the `transform` function can be used to force the transform to visit an optional -intermediate format. For example, passing `['ciceromark_noquotes','html']` will transform the source -to `ciceromark_noquotes` followed by all the transformations necessary to transform from `ciceromark_noquotes` to `html`. +```ts +// markdown → ciceromark (unquoted variables) → HTML +const html = await transform(markdown, 'markdown', ['ciceromark_unquoted', 'html']); +``` -The example below transforms input markdown, stripping quotes from around variables, and then converts to HTML. +In CommonJS: +```js +const { transform } = require('@accordproject/markdown-transform'); ``` -const result = await transform(acceptanceCiceroEdit, 'markdown', ['ciceromark_noquotes','html']); -``` + +## Supported formats + +`markdown`, `markdown_cicero`, `markdown_template`, `commonmark_tokens`, `ciceromark_tokens`, `templatemark_tokens`, `commonmark`, `ciceromark`, `ciceromark_parsed`, `ciceromark_unquoted`, `templatemark`, `ciceroedit`, `html`, `plaintext`. + +Use `formatDescriptor(name)` to inspect a format's `fileFormat` (`utf8` / `json` / `binary`) and its outgoing edges, or `new TransformEngine(builtinTransformationGraph)` to register your own formats/transforms. ## Transformation Graph -You can generate a PlantUML state diagram for the supported transformations using the following code: +You can generate a PlantUML state diagram for the supported transformations: -``` -const generateTransformationDiagram = require('@accordproject/markdown-transform').generateTransformationDiagram; -const plantUMLStateDiagram = generateTransformationDiagram(); +```ts +import { generateTransformationDiagram } from '@accordproject/markdown-transform'; +const plantUml = generateTransformationDiagram(); ``` -The diagram below (showing all supported transformations) is automatically generated by `./scripts/generateDiagram.js`. - ![Transforms](transformations.png) ## License Accord Project source code files are made available under the Apache License, Version 2.0 (Apache-2.0), located in the LICENSE file. Accord Project documentation files are made available under the Creative Commons Attribution 4.0 International License (CC-BY-4.0), available at http://creativecommons.org/licenses/by/4.0/. - -© 2017-2019 Clause, Inc. diff --git a/packages/markdown-transform/index.js b/packages/markdown-transform/index.js deleted file mode 100755 index 3f341bdd..00000000 --- a/packages/markdown-transform/index.js +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -/** - * Export the framework and plugins - * @module markdown-transform - */ - -module.exports.formatDescriptor = require('./lib/transform').formatDescriptor; -module.exports.transform = require('./lib/transform').transform; -module.exports.TransformEngine = require('./lib/transformEngine'); -module.exports.builtinTransformationGraph = require('./lib/builtinTransforms'); diff --git a/packages/markdown-transform/jest.config.js b/packages/markdown-transform/jest.config.js index 4a8345ce..8f758039 100644 --- a/packages/markdown-transform/jest.config.js +++ b/packages/markdown-transform/jest.config.js @@ -13,187 +13,19 @@ */ 'use strict'; -// For a detailed explanation regarding each configuration property, visit: -// https://jestjs.io/docs/en/configuration.html +/** @type {import('jest').Config} */ module.exports = { - // All imported modules in your tests should be mocked automatically - // automock: false, - - // Stop running tests after `n` failures - // bail: 0, - - // Respect "browser" field in package.json when resolving modules - // browser: false, - - // The directory where Jest should store its cached dependency information - // cacheDirectory: "/private/var/folders/tv/4ljndl3s2jg90nxd8h7f3bgr0000gn/T/jest_dx", - - // Automatically clear mock calls and instances between every test + preset: 'ts-jest', + testEnvironment: 'node', clearMocks: true, - - // Indicates whether the coverage information should be collected while executing the test - // collectCoverage: false, - - // An array of glob patterns indicating a set of files for which coverage information should be collected - collectCoverageFrom: [ 'src/**/*.js' ], - - // The directory where Jest should output its coverage files + testTimeout: 30000, + testMatch: ['/src/**/*.test.ts'], + collectCoverageFrom: ['src/**/*.ts', '!src/**/*.test.ts', '!src/**/*.d.ts'], coverageDirectory: 'coverage', - - // An array of regexp pattern strings used to skip coverage collection - coveragePathIgnorePatterns: [ - '/node_modules/' - ], - - // A list of reporter names that Jest uses when writing coverage reports - coverageReporters: [ - 'json', - 'text', - 'lcov', - 'html' - ], - - // An object that configures minimum threshold enforcement for coverage results - // coverageThreshold: null, - - // A path to a custom dependency extractor - // dependencyExtractor: null, - - // Make calling deprecated APIs throw helpful error messages - // errorOnDeprecated: false, - - // Force coverage collection from ignored files using an array of glob patterns - // forceCoverageMatch: [], - - // A path to a module which exports an async function that is triggered once before all test suites - // globalSetup: null, - - // A path to a module which exports an async function that is triggered once after all test suites - // globalTeardown: null, - - // A set of global variables that need to be available in all test environments - // globals: {}, - - // An array of directory names to be searched recursively up from the requiring module's location - moduleNameMapper: { - '^axios$': 'axios/dist/axios.js' + coveragePathIgnorePatterns: ['/node_modules/'], + coverageReporters: ['json', 'text', 'lcov', 'html'], + transform: { + '^.+\\.tsx?$': ['ts-jest', { tsconfig: '/tsconfig.test.json' }], }, - - // An array of file extensions your modules use - // moduleFileExtensions: [ - // "js", - // "json", - // "jsx", - // "ts", - // "tsx", - // "node" - // ], - - // A map from regular expressions to module names that allow to stub out resources with a single module - // moduleNameMapper: {}, - - // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader - // modulePathIgnorePatterns: [], - - // Activates notifications for test results - // notify: false, - - // An enum that specifies notification mode. Requires { notify: true } - // notifyMode: "failure-change", - - // A preset that is used as a base for Jest's configuration - // preset: null, - - // Run tests from one or more projects - // projects: null, - - // Use this configuration option to add custom reporters to Jest - // reporters: undefined, - - // Automatically reset mock state between every test - // resetMocks: false, - - // Reset the module registry before running each individual test - // resetModules: false, - - // A path to a custom resolver - // resolver: null, - - // Automatically restore mock state between every test - // restoreMocks: false, - - // The root directory that Jest should scan for tests and modules within - // rootDir: null, - - // A list of paths to directories that Jest should use to search for files in - // roots: [ - // "" - // ], - - // Allows you to use a custom runner instead of Jest's default test runner - // runner: "jest-runner", - - // The paths to modules that run some code to configure or set up the testing environment before each test - // setupFiles: [], - - // A list of paths to modules that run some code to configure or set up the testing framework before each test - // setupFilesAfterEnv: [], - - // A list of paths to snapshot serializer modules Jest should use for snapshot testing - // snapshotSerializers: [], - - // The test environment that will be used for testing - testEnvironment: 'node', - - // Options that will be passed to the testEnvironment - // testEnvironmentOptions: {}, - - // Adds a location field to test results - // testLocationInResults: false, - - // The glob patterns Jest uses to detect test files - testMatch: [ - 'test/**/*.js', - ], - - // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped - // testPathIgnorePatterns: [ - // "/node_modules/" - // ], - - // The regexp pattern or array of patterns that Jest uses to detect test files - // testRegex: [], - - // This option allows the use of a custom results processor - // testResultsProcessor: null, - - // This option allows use of a custom test runner - // testRunner: "jasmine2", - - // This option sets the URL for the jsdom environment. It is reflected in properties such as location.href - // testURL: "http://localhost", - - // Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout" - // timers: "real", - - // A map from regular expressions to paths to transformers - // transform: null, - - // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation - // transformIgnorePatterns: [ - // "/node_modules/" - // ], - - // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them - // unmockedModulePathPatterns: undefined, - - // Indicates whether each individual test should be reported during the run - // verbose: null, - - // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode - // watchPathIgnorePatterns: [], - - // Whether to use watchman for file crawling - // watchman: true, }; diff --git a/packages/markdown-transform/jsdoc.json b/packages/markdown-transform/jsdoc.json deleted file mode 100644 index d55ab14a..00000000 --- a/packages/markdown-transform/jsdoc.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "tags": { - "allowUnknownTags": true, - "dictionaries": [ - "jsdoc", - "closure" - ] - }, - "source": { - "include": [ - "./src", - "./index.js" - ], - "includePattern": ".+\\.js(doc|x)?$" - }, - "plugins": [ - "plugins/markdown" - ], - "templates": { - "logoFile": "", - "cleverLinks": false, - "monospaceLinks": false, - "dateFormat": "ddd MMM Do YYYY", - "outputSourceFiles": true, - "outputSourcePath": true, - "systemName": "Accord Project Cicero SDK", - "footer": "", - "copyright": "Released under the Apache License v2.0", - "navType": "vertical", - "theme": "spacelab", - "linenums": true, - "collapseSymbols": false, - "inverseNav": true, - "protocol": "html://", - "methodHeadingReturns": false - }, - "markdown": { - "parser": "gfm", - "hardwrap": true - } -} \ No newline at end of file diff --git a/packages/markdown-transform/lib/builtinTransforms.js b/packages/markdown-transform/lib/builtinTransforms.js deleted file mode 100644 index 55eb87a2..00000000 --- a/packages/markdown-transform/lib/builtinTransforms.js +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -const ModelLoader = require('@accordproject/concerto-core').ModelLoader; -const CommonMarkTransformer = require('@accordproject/markdown-common').CommonMarkTransformer; -const CiceroMarkTransformer = require('@accordproject/markdown-cicero').CiceroMarkTransformer; -const TemplateMarkTransformer = require('@accordproject/markdown-template').TemplateMarkTransformer; -const HtmlTransformer = require('@accordproject/markdown-html').HtmlTransformer; -const transformationGraph = { - markdown_template: { - docs: 'Template markdown (string)', - fileFormat: 'utf8', - templatemark_tokens: (input, parameters, options) => { - const t = new TemplateMarkTransformer(); - return t.toTokens({ - fileName: parameters.inputFileName, - content: input - }, options); - } - }, - templatemark_tokens: { - docs: 'TemplateMark tokens (JSON)', - fileFormat: 'json', - templatemark: async (input, parameters, options) => { - const t = new TemplateMarkTransformer(); - const modelManager = await ModelLoader.loadModelManager(parameters.model, options); - return t.tokensToMarkdownTemplate(input, modelManager, parameters.templateKind, options, parameters.conceptFullyQualifiedName); - } - }, - templatemark: { - docs: 'TemplateMark DOM (JSON)', - fileFormat: 'json', - markdown_template: (input, parameters, options) => { - const t = new TemplateMarkTransformer(); - return t.toMarkdownTemplate(input); - } - }, - markdown: { - docs: 'Markdown (string)', - fileFormat: 'utf8', - commonmark_tokens: (input, parameters, options) => { - const t = new CommonMarkTransformer(); - return t.toTokens(input, options); - } - }, - commonmark_tokens: { - docs: 'Markdown tokens (JSON)', - fileFormat: 'json', - commonmark: async (input, parameters, options) => { - const t = new CommonMarkTransformer(); - return t.fromTokens(input); - } - }, - markdown_cicero: { - docs: 'Cicero markdown (string)', - fileFormat: 'utf8', - ciceromark_tokens: (input, parameters, options) => { - const t = new CiceroMarkTransformer(); - return t.toTokens(input, options); - } - }, - ciceromark_tokens: { - docs: 'CiceroMark tokens (JSON)', - fileFormat: 'json', - ciceromark: async (input, parameters, options) => { - const t = new CiceroMarkTransformer(); - return t.fromTokens(input); - } - }, - commonmark: { - docs: 'CommonMark DOM (JSON)', - fileFormat: 'json', - markdown: (input, parameters, options) => { - const t = new CommonMarkTransformer(); - return t.toMarkdown(input); - }, - ciceromark: (input, parameters, options) => { - const t = new CiceroMarkTransformer(); - return t.fromCommonMark(input, options); - }, - plaintext: (input, parameters, options) => { - const t = new CommonMarkTransformer(); - return t.toMarkdown(t.removeFormatting(input)); - } - }, - ciceromark: { - docs: 'CiceroMark DOM (JSON)', - fileFormat: 'json', - markdown_cicero: (input, parameters, options) => { - const t = new CiceroMarkTransformer(); - const inputUnwrapped = t.toCiceroMarkUnwrapped(input, options); - return t.toMarkdownCicero(inputUnwrapped); - }, - commonmark: (input, parameters, options) => { - const t = new CiceroMarkTransformer(); - return t.toCommonMark(input, options); - }, - ciceromark_parsed: (input, parameters, options) => { - return input; - } - }, - ciceromark_parsed: { - docs: 'Parsed CiceroMark DOM (JSON)', - fileFormat: 'json', - html: (input, parameters, options) => { - const t = new HtmlTransformer(); - return t.toHtml(input); - }, - ciceromark: (input, parameters, options) => { - const t = new CiceroMarkTransformer(); - return t.toCiceroMarkUnwrapped(input, options); - }, - ciceromark_unquoted: (input, parameters, options) => { - const t = new CiceroMarkTransformer(); - return t.unquote(input, options); - } - }, - plaintext: { - docs: 'Plain text (string)', - fileFormat: 'utf8', - markdown: (input, parameters, options) => { - return input; - } - }, - ciceroedit: { - docs: 'CiceroEdit (string)', - fileFormat: 'utf8', - ciceromark_parsed: (input, parameters, options) => { - const t = new CiceroMarkTransformer(); - return t.fromCiceroEdit(input, options); - } - }, - ciceromark_unquoted: { - docs: 'CiceroMark DOM (JSON) with quotes around variables removed', - fileFormat: 'json', - ciceromark_parsed: (input, parameters, options) => { - return input; - } - }, - html: { - docs: 'HTML (string)', - fileFormat: 'utf8', - ciceromark_parsed: (input, parameters, options) => { - const t = new HtmlTransformer(); - return t.toCiceroMark(input, options); - } - } -}; -module.exports = transformationGraph; \ No newline at end of file diff --git a/packages/markdown-transform/lib/transform.js b/packages/markdown-transform/lib/transform.js deleted file mode 100644 index b17cf423..00000000 --- a/packages/markdown-transform/lib/transform.js +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -const TransformEngine = require('./transformEngine'); -const builtinTransformationGraph = require('./builtinTransforms'); - -/** - * Create a new transformation engine - * - * @param {object} transformationGraph - initial transformation graph - * @return {TransformEngine} the transformation engine - */ -function createTransformationEngine(transformationGraph) { - return new TransformEngine(transformationGraph); -} -module.exports.createTransformationEngine; - -/** - * This is instantiated here for backward compatibility - * @type {TransformEngine} - */ -const builtinEngine = createTransformationEngine(builtinTransformationGraph); -module.exports.builtinEngine = builtinEngine; - -/** - * Return the format descriptor for a given format - * @param {string} format the format - * @returns {object} the descriptor for that format - */ -module.exports.formatDescriptor = format => builtinEngine.formatDescriptor(format); - -/** - * Transforms from a source format to a list of destination formats. - * @param {object|string} source the input for the transformation - * @param {string} sourceFormat the input format - * @param {string[]} destinationFormat the destination format as an array - * @param {object} [parameters] the transform parameters - * @param {object} [options] the transform options - * @returns {Promise} result of the transformation - */ -module.exports.transform = (source, sourceFormat, destinationFormat, parameters, options) => builtinEngine.transform(source, sourceFormat, destinationFormat, parameters, options); - -/** - * Converts the transformation graph into a PlantUML diagram string - * @returns {string} the PlantUML string - */ -module.exports.generateTransformationDiagram = () => builtinEngine.generateTransformationDiagram(); \ No newline at end of file diff --git a/packages/markdown-transform/lib/transformEngine.js b/packages/markdown-transform/lib/transformEngine.js deleted file mode 100644 index 8b293b15..00000000 --- a/packages/markdown-transform/lib/transformEngine.js +++ /dev/null @@ -1,283 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -const dijkstra = require('dijkstrajs'); -const find_path = dijkstra.find_path; - -/** - * Prune the graph for traversal - * @param {object} graph the input graph - * @returns {object} the raw graph for dijsktra - */ -function pruneGraph(graph) { - const result = {}; - for (const sourceKey in graph) { - result[sourceKey] = {}; - for (const targetKey in graph[sourceKey]) { - // Don't forget to remove the meta data which really isn't part of the graph - if (targetKey !== 'docs' && targetKey !== 'fileFormat') { - result[sourceKey][targetKey] = 1; - } - } - } - return result; -} - -/** - * A generic transformation engine. - * - * The format for the graph is a map (a JavaScript object) where each entry is a format, i.e., vertex in the graph with the following content: - * - * [sourceFormat]: { - * docs: // A format description - * fileFormat: // What kind of format it is (i.e., utf8, json, binary) - * [targetFormat1]: async (input, parameters, options) => { ... return result } - * [targetFormat2]: async (input, parameters, options) => { ... return result } - * ... - * } - * - * Each [targetFormat] entry defines an edge in the graph transforming [sourceFormat] to [targetFormat] - */ -class TransformEngine { - /** - * Construct the transformation engine - * @param {object} transformationGraph - the transformation graph - */ - constructor(transformationGraph) { - // Clone the graph - const { - ...graph - } = transformationGraph; - this.transformationGraph = graph; - this.refreshRawGraph(); - } - - /** - * Converts the graph of transformations into a PlantUML text string - * @returns {string} the PlantUML string - */ - generateTransformationDiagram() { - let result = `@startuml -hide empty description - -`; - const transformationGraph = this.getTransformationGraph(); - Object.keys(transformationGraph).forEach(src => { - result += `${src} : \n`; - result += `${src} : ${transformationGraph[src].docs}\n`; - Object.keys(transformationGraph[src]).forEach(dest => { - if (dest !== 'docs' && dest !== 'fileFormat') { - result += `${src} --> ${dest}\n`; - } - }); - result += '\n'; - }); - result += '@enduml'; - return result; - } - - /** - * Transforms from a source format to a single destination format or - * throws an exception if the transformation is not possible. - * - * @param {object|string} source the input for the transformation - * @param {string} sourceFormat the input format - * @param {string} destinationFormat the destination format - * @param {object} [parameters] the transform parameters - * @param {object} [options] the transform options - * @param {boolean} [options.verbose] output verbose console logs - * @returns {Promise} result of the transformation - */ - async transformToDestination(source, sourceFormat, destinationFormat, parameters, options) { - let result = source; - const transformationGraph = this.getTransformationGraph(); - const path = find_path(this.rawGraph, sourceFormat, destinationFormat); - for (let n = 0; n < path.length - 1; n++) { - const src = path[n]; - const dest = path[n + 1]; - const srcNode = transformationGraph[src]; - const destinationNode = transformationGraph[dest]; - result = await srcNode[dest](result, parameters, options); - if (options && options.verbose) { - console.log(`Converted from ${src} to ${dest}. Result:`); - if (destinationNode.fileFormat !== 'binary') { - if (typeof result === 'object') { - console.log(JSON.stringify(result, null, 2)); - } else { - console.log(result); - } - } else { - console.log(``); - } - } - } - return result; - } - - /** - * Transforms from a source format to a list of destination formats, or - * throws an exception if the transformation is not possible. - * - * @param {object|string} source the input for the transformation - * @param {string} sourceFormat the input format - * @param {string[]} destinationFormat the destination format as an array, - * the transformation are applied in order to reach all formats in the array - * @param {object} [parameters] the transform parameters - * @param {object} [options] the transform options - * @param {boolean} [options.verbose] output verbose console logs - * @returns {Promise} result of the transformation - */ - async transform(source, sourceFormat, destinationFormat, parameters, options) { - let result = source; - options = options ? options : {}; - parameters = parameters ? parameters : {}; - if (sourceFormat === 'markdown') { - options.source = source; - } - let currentSourceFormat = sourceFormat; - for (let i = 0; i < destinationFormat.length; i++) { - let destination = destinationFormat[i]; - result = await this.transformToDestination(result, currentSourceFormat, destination, parameters, options); - currentSourceFormat = destination; - } - return result; - } - - /** - * Return the format descriptor for a given format - * - * @param {string} format the format - * @return {object} the descriptor for that format - */ - formatDescriptor(format) { - const transformationGraph = this.getTransformationGraph(); - if (Object.prototype.hasOwnProperty.call(transformationGraph, format)) { - return transformationGraph[format]; - } else { - throw new Error('Unknown format: ' + format); - } - } - - /** - * Return the transformation graph - * - * @return {object} the transformation graph - */ - getTransformationGraph() { - return this.transformationGraph; - } - - /** - * Return all the available formats - * - * @return {object} the transformation graph - */ - getAllFormats() { - const transformationGraph = this.getTransformationGraph(); - return Object.keys(transformationGraph); - } - - /** - * Return all the available targets from a source formats - * - * @param {string} sourceFormat - the sourceFormat - * @return {object} the transformation graph - */ - getAllTargetFormats(sourceFormat) { - const transformationGraph = this.getTransformationGraph(); - if (!transformationGraph[sourceFormat]) { - throw new Error(`Unknown format: ${sourceFormat}`); - } - // eslint-disable-next-line no-unused-vars - const { - docs, - fileFormat, - ...targets - } = transformationGraph[sourceFormat]; - return Object.keys(targets); - } - - /** - * Add a new format - * - * @param {string} sourceFormat - the name of the source format - * @param {string} docs - the format description - * @param {string} fileFormat - the format type (either 'json', 'utf8' or 'binary') - */ - registerFormat(sourceFormat, docs, fileFormat) { - const transformationGraph = this.getTransformationGraph(); - if (transformationGraph[sourceFormat]) { - throw new Error(`Format already exists: ${sourceFormat}`); - } - transformationGraph[sourceFormat] = { - docs, - fileFormat - }; - } - - /** - * Add a new transform - * - * @param {string} sourceFormat - the name of the source format - * @param {string} targetFormat - the name of the targetFormat format - * @param {Function} transform - the transform (an async function to transform from sourceFormat to targetFormat) - */ - registerTransformation(sourceFormat, targetFormat, transform) { - const transformationGraph = this.getTransformationGraph(); - if (!transformationGraph[sourceFormat]) { - throw new Error(`Unknown format: ${sourceFormat}`); - } - if (!transformationGraph[targetFormat]) { - throw new Error(`Unknown format: ${targetFormat}`); - } - transformationGraph[sourceFormat][targetFormat] = transform; - // Rebuild the raw graph - this.refreshRawGraph(); - } - - /** - * Register a transform extension - * @param {object} extension - the transform extension, including format and transforms - */ - registerExtension(extension) { - if (extension.format) { - const { - name: sourceFormat, - docs, - fileFormat - } = extension.format; - this.registerFormat(sourceFormat, docs, fileFormat); - } - if (extension.transforms) { - for (let source in extension.transforms) { - const transforms = extension.transforms[source]; - for (let target in transforms) { - const transform = transforms[target]; - this.registerTransformation(source, target, transform); - } - } - } - } - - /** - * Refresh raw graph - * @private - */ - refreshRawGraph() { - this.rawGraph = pruneGraph(this.transformationGraph); - } -} -module.exports = TransformEngine; \ No newline at end of file diff --git a/packages/markdown-transform/package.json b/packages/markdown-transform/package.json index 6e44b6bc..3742cfba 100644 --- a/packages/markdown-transform/package.json +++ b/packages/markdown-transform/package.json @@ -1,6 +1,6 @@ { "name": "@accordproject/markdown-transform", - "version": "0.16.25", + "version": "1.0.0", "description": "API for transforming markdown data", "engines": { "node": ">=22", @@ -10,31 +10,27 @@ "access": "public" }, "files": [ - "bin", "lib", - "types", "umd" ], - "main": "index.js", + "main": "lib/index.js", "browser": "umd/markdown-transform.js", + "types": "lib/index.d.ts", + "typings": "lib/index.d.ts", "scripts": { "webpack": "webpack --config webpack.config.js --mode production", - "build": "babel src -d lib --copy-files && npm run build:types", - "build:types": "tsc", - "build:dist": "NODE_ENV=production babel src -d lib --copy-files", - "build:watch": "babel src -d lib --copy-files --watch", - "prepublishOnly": "npm run build:dist && npm run webpack", - "prepare": "npm run build", + "build": "tsc -p tsconfig.json", + "build:dist": "npm run build && npm run webpack", + "prepublishOnly": "npm run build:dist", "pretest": "npm run lint && npm run build", - "lint": "eslint .", + "lint": "eslint . --ext .ts", "postlint": "npm run licchk", "licchk": "license-check-and-add", - "test": "mocha --timeout 30000", - "test:cov": "npm run lint && nyc mocha --timeout 30000", - "jsdoc": "jsdoc -c jsdoc.json package.json", - "typescript": "jsdoc -t node_modules/tsd-jsdoc/dist -r ./src/" + "test": "jest --silent", + "test:noisy": "jest", + "test:cov": "npm run lint && npm run build && jest --coverage --silent", + "clean": "rimraf lib umd" }, - "typings": "types/index.d.ts", "repository": { "type": "git", "url": "git+https://github.com/accordproject/markdown-transform.git", @@ -53,33 +49,24 @@ }, "devDependencies": { "@accordproject/concerto-core": "^4.1.3", - "@babel/cli": "7.25.9", - "@babel/core": "7.26.0", - "@babel/preset-env": "7.16.11", - "@babel/register": "7.25.9", - "axios": "^1.15.2", - "babel-loader": "9.2.1", - "babel-plugin-istanbul": "7.0.0", + "@types/jest": "^29.5.12", + "@types/node": "^20.11.30", + "@typescript-eslint/eslint-plugin": "^7.4.0", + "@typescript-eslint/parser": "^7.4.0", "browserify-zlib": "^0.2.0", - "chai": "4.3.6", - "chai-as-promised": "7.1.1", - "chai-string": "^1.5.0", - "chai-things": "0.2.0", "crypto-browserify": "3.12.1", "eslint": "8.57.1", "https-browserify": "^1.0.0", - "jsdoc": "^4.0.4", + "jest": "^29.7.0", "license-check-and-add": "2.3.6", - "mocha": "10.8.2", - "nyc": "17.1.0", - "plantuml-encoder": "^1.4.0", - "raw-loader": "^4.0.2", + "rimraf": "^5.0.5", "stream-browserify": "3.0.0", "stream-http": "^3.2.0", - "tsd-jsdoc": "^2.5.0", + "ts-jest": "^29.1.2", + "ts-loader": "^9.5.1", + "typescript": "^5.9.3", "webpack": "^5.104.1", - "webpack-cli": "5.1.4", - "typescript": "^5.9.3" + "webpack-cli": "5.1.4" }, "peerDependencies": { "@accordproject/concerto-core": "^4.1.3" @@ -90,17 +77,15 @@ "@accordproject/markdown-html": "*", "@accordproject/markdown-template": "*", "dijkstrajs": "^1.0.3", - "jszip": "^3.10.1" + "jszip": "^3.10.1", + "process": "^0.11.10" }, "license-check-and-add-config": { - "folder": "./lib", + "folder": "./src", "license": "header.txt", "exact_paths_method": "EXCLUDE", "exact_paths": [ - "externalModels/.npmignore", - "externalModels/.gitignore", "coverage", - "index.d.ts", "./system", "LICENSE", "node_modules", @@ -118,7 +103,7 @@ ], "insert_license": false, "license_formats": { - "js|njk|pegjs|cto|acl|qry": { + "ts|tsx|js|njk|pegjs|cto|acl|qry": { "prepend": "/*", "append": " */", "eachLine": { @@ -134,27 +119,5 @@ "file": "header.md" } } - }, - "nyc": { - "produce-source-map": "true", - "sourceMap": "inline", - "reporter": [ - "lcov", - "text-summary", - "html", - "json" - ], - "include": [ - "lib/**/*.js" - ], - "exclude": [ - "scripts/**/*.js" - ], - "all": true, - "check-coverage": true, - "statements": 90, - "branches": 88, - "functions": 88, - "lines": 90 } } diff --git a/packages/markdown-transform/src/builtinTransforms.js b/packages/markdown-transform/src/builtinTransforms.ts similarity index 62% rename from packages/markdown-transform/src/builtinTransforms.js rename to packages/markdown-transform/src/builtinTransforms.ts index 7aabaa59..487108b2 100644 --- a/packages/markdown-transform/src/builtinTransforms.js +++ b/packages/markdown-transform/src/builtinTransforms.ts @@ -12,28 +12,27 @@ * limitations under the License. */ -'use strict'; +// eslint-disable-next-line @typescript-eslint/no-var-requires +const { ModelLoader } = require('@accordproject/concerto-core'); +import { CommonMarkTransformer } from '@accordproject/markdown-common'; +import { CiceroMarkTransformer } from '@accordproject/markdown-cicero'; +import { TemplateMarkTransformer } from '@accordproject/markdown-template'; +import { HtmlTransformer } from '@accordproject/markdown-html'; +import type { TransformationGraph } from './transformEngine'; -const ModelLoader = require('@accordproject/concerto-core').ModelLoader; - -const CommonMarkTransformer = require('@accordproject/markdown-common').CommonMarkTransformer; -const CiceroMarkTransformer = require('@accordproject/markdown-cicero').CiceroMarkTransformer; -const TemplateMarkTransformer = require('@accordproject/markdown-template').TemplateMarkTransformer; -const HtmlTransformer = require('@accordproject/markdown-html').HtmlTransformer; - -const transformationGraph = { +const transformationGraph: TransformationGraph = { markdown_template: { docs: 'Template markdown (string)', fileFormat: 'utf8', - templatemark_tokens: (input, parameters, options) => { + templatemark_tokens: (input: any, parameters: any) => { const t = new TemplateMarkTransformer(); - return t.toTokens({ fileName:parameters.inputFileName, content:input }, options); + return t.toTokens({ fileName: parameters.inputFileName, content: input }); }, }, templatemark_tokens: { docs: 'TemplateMark tokens (JSON)', fileFormat: 'json', - templatemark: async (input, parameters, options) => { + templatemark: async (input: any, parameters: any, options: any) => { const t = new TemplateMarkTransformer(); const modelManager = await ModelLoader.loadModelManager(parameters.model, options); return t.tokensToMarkdownTemplate(input, modelManager, parameters.templateKind, options, parameters.conceptFullyQualifiedName); @@ -42,7 +41,7 @@ const transformationGraph = { templatemark: { docs: 'TemplateMark DOM (JSON)', fileFormat: 'json', - markdown_template: (input, parameters, options) => { + markdown_template: (input: any) => { const t = new TemplateMarkTransformer(); return t.toMarkdownTemplate(input); }, @@ -50,15 +49,15 @@ const transformationGraph = { markdown: { docs: 'Markdown (string)', fileFormat: 'utf8', - commonmark_tokens: (input, parameters, options) => { + commonmark_tokens: (input: string) => { const t = new CommonMarkTransformer(); - return t.toTokens(input, options); + return t.toTokens(input); }, }, commonmark_tokens: { docs: 'Markdown tokens (JSON)', fileFormat: 'json', - commonmark: async (input, parameters, options) => { + commonmark: async (input: any) => { const t = new CommonMarkTransformer(); return t.fromTokens(input); }, @@ -66,15 +65,15 @@ const transformationGraph = { markdown_cicero: { docs: 'Cicero markdown (string)', fileFormat: 'utf8', - ciceromark_tokens: (input, parameters, options) => { + ciceromark_tokens: (input: string) => { const t = new CiceroMarkTransformer(); - return t.toTokens(input, options); + return t.toTokens(input); }, }, ciceromark_tokens: { docs: 'CiceroMark tokens (JSON)', fileFormat: 'json', - ciceromark: async (input, parameters, options) => { + ciceromark: async (input: any) => { const t = new CiceroMarkTransformer(); return t.fromTokens(input); }, @@ -82,15 +81,15 @@ const transformationGraph = { commonmark: { docs: 'CommonMark DOM (JSON)', fileFormat: 'json', - markdown: (input, parameters, options) => { + markdown: (input: any) => { const t = new CommonMarkTransformer(); return t.toMarkdown(input); }, - ciceromark: (input, parameters, options) => { + ciceromark: (input: any) => { const t = new CiceroMarkTransformer(); - return t.fromCommonMark(input, options); + return t.fromCommonMark(input); }, - plaintext: (input, parameters, options) => { + plaintext: (input: any) => { const t = new CommonMarkTransformer(); return t.toMarkdown(t.removeFormatting(input)); }, @@ -98,65 +97,59 @@ const transformationGraph = { ciceromark: { docs: 'CiceroMark DOM (JSON)', fileFormat: 'json', - markdown_cicero: (input, parameters, options) => { + markdown_cicero: (input: any, _parameters: any, options: any) => { const t = new CiceroMarkTransformer(); - const inputUnwrapped = t.toCiceroMarkUnwrapped(input,options); + const inputUnwrapped = t.toCiceroMarkUnwrapped(input, options); return t.toMarkdownCicero(inputUnwrapped); }, - commonmark: (input, parameters, options) => { + commonmark: (input: any, _parameters: any, options: any) => { const t = new CiceroMarkTransformer(); return t.toCommonMark(input, options); }, - ciceromark_parsed: (input, parameters, options) => { - return input; - } + ciceromark_parsed: (input: any) => input, }, ciceromark_parsed: { docs: 'Parsed CiceroMark DOM (JSON)', fileFormat: 'json', - html: (input, parameters, options) => { + html: (input: any) => { const t = new HtmlTransformer(); return t.toHtml(input); }, - ciceromark: (input, parameters, options) => { + ciceromark: (input: any, _parameters: any, options: any) => { const t = new CiceroMarkTransformer(); return t.toCiceroMarkUnwrapped(input, options); }, - ciceromark_unquoted: (input, parameters, options) => { + ciceromark_unquoted: (input: any) => { const t = new CiceroMarkTransformer(); - return t.unquote(input, options); + return t.unquote(input); }, }, plaintext: { docs: 'Plain text (string)', fileFormat: 'utf8', - markdown: (input, parameters, options) => { - return input; - }, + markdown: (input: any) => input, }, ciceroedit: { docs: 'CiceroEdit (string)', fileFormat: 'utf8', - ciceromark_parsed: (input, parameters, options) => { + ciceromark_parsed: (input: string) => { const t = new CiceroMarkTransformer(); - return t.fromCiceroEdit(input, options); + return t.fromCiceroEdit(input); }, }, ciceromark_unquoted: { docs: 'CiceroMark DOM (JSON) with quotes around variables removed', fileFormat: 'json', - ciceromark_parsed: (input, parameters, options) => { - return input; - } + ciceromark_parsed: (input: any) => input, }, html: { docs: 'HTML (string)', fileFormat: 'utf8', - ciceromark_parsed: (input, parameters, options) => { + ciceromark_parsed: (input: string) => { const t = new HtmlTransformer(); - return t.toCiceroMark(input, options); - } - } + return t.toCiceroMark(input); + }, + }, }; -module.exports = transformationGraph; +export default transformationGraph; diff --git a/packages/markdown-transform/src/index.ts b/packages/markdown-transform/src/index.ts new file mode 100644 index 00000000..b86c3d40 --- /dev/null +++ b/packages/markdown-transform/src/index.ts @@ -0,0 +1,33 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { formatDescriptor, transform, generateTransformationDiagram } from './transform'; +import { TransformEngine } from './transformEngine'; +import builtinTransformationGraph from './builtinTransforms'; + +export { + formatDescriptor, + transform, + generateTransformationDiagram, + TransformEngine, + builtinTransformationGraph, +}; + +export default { + formatDescriptor, + transform, + generateTransformationDiagram, + TransformEngine, + builtinTransformationGraph, +}; diff --git a/packages/markdown-transform/src/transform.js b/packages/markdown-transform/src/transform.js deleted file mode 100644 index 1e5d8f16..00000000 --- a/packages/markdown-transform/src/transform.js +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -const TransformEngine = require('./transformEngine'); -const builtinTransformationGraph = require('./builtinTransforms'); - -/** - * Create a new transformation engine - * - * @param {object} transformationGraph - initial transformation graph - * @return {TransformEngine} the transformation engine - */ -function createTransformationEngine(transformationGraph) { - return new TransformEngine(transformationGraph); -} - -module.exports.createTransformationEngine; - -/** - * This is instantiated here for backward compatibility - * @type {TransformEngine} - */ -const builtinEngine = createTransformationEngine(builtinTransformationGraph); - -module.exports.builtinEngine = builtinEngine; - -/** - * Return the format descriptor for a given format - * @param {string} format the format - * @returns {object} the descriptor for that format - */ -module.exports.formatDescriptor = (format) => builtinEngine.formatDescriptor(format); - -/** - * Transforms from a source format to a list of destination formats. - * @param {object|string} source the input for the transformation - * @param {string} sourceFormat the input format - * @param {string[]} destinationFormat the destination format as an array - * @param {object} [parameters] the transform parameters - * @param {object} [options] the transform options - * @returns {Promise} result of the transformation - */ -module.exports.transform = ( - source, - sourceFormat, - destinationFormat, - parameters, - options -) => builtinEngine.transform(source, sourceFormat, destinationFormat, parameters, options); - -/** - * Converts the transformation graph into a PlantUML diagram string - * @returns {string} the PlantUML string - */ -module.exports.generateTransformationDiagram = () => builtinEngine.generateTransformationDiagram(); diff --git a/packages/markdown-transform/test/transform.js b/packages/markdown-transform/src/transform.test.ts similarity index 55% rename from packages/markdown-transform/test/transform.js rename to packages/markdown-transform/src/transform.test.ts index 55bdd65c..4108f089 100644 --- a/packages/markdown-transform/test/transform.js +++ b/packages/markdown-transform/src/transform.test.ts @@ -12,69 +12,43 @@ * limitations under the License. */ -'use strict'; +import * as fs from 'fs'; +import * as path from 'path'; +import { CommonMarkModel } from '@accordproject/markdown-common'; +import { transform, generateTransformationDiagram, formatDescriptor } from './transform'; -const chai = require('chai'); -const fs = require('fs'); -const path = require('path'); -chai.use(require('chai-string')); - -chai.should(); -chai.use(require('chai-things')); -chai.use(require('chai-as-promised')); - -const {CommonMarkModel} = require('@accordproject/markdown-common'); - -const transform = require('../lib/transform').transform; -const generateTransformationDiagram = require('../lib/transform').generateTransformationDiagram; -const formatDescriptor = require('../lib/transform').formatDescriptor; - -/** - * Prepare the text for parsing (normalizes new lines, etc) - * @param {string} input - the text for the clause - * @return {string} - the normalized text for the clause - */ -function normalizeNLs(input) { - // we replace all \r and \n with \n - let text = input.replace(/\r/gm,''); - return text; +function normalizeNLs(input: string): string { + return input.replace(/\r/gm, ''); } -/** - * Load models - * @param {string} dir - a directory - * @return {*} the list of model files - */ -function loadModels(dir) { +function loadModels(dir: string): string[] { const files = fs.readdirSync(dir); const ctoFiles = files.filter((file) => path.extname(file) === '.cto'); - const ctoPaths = ctoFiles.map((file) => path.join(dir, file)); - return ctoPaths; + return ctoFiles.map((file) => path.join(dir, file)); } -// Acceptance test -const acceptanceGrammarFile = path.resolve(__dirname, 'data/acceptance', 'grammar.tem.md'); +const dataDir = path.resolve(__dirname, '..', 'test', 'data'); +const acceptanceGrammarFile = path.resolve(dataDir, 'acceptance', 'grammar.tem.md'); const acceptanceGrammar = normalizeNLs(fs.readFileSync(acceptanceGrammarFile, 'utf8')); -const acceptanceGrammarTokens = JSON.parse(fs.readFileSync(path.resolve(__dirname, 'data/acceptance', 'grammar_tokens.json'), 'utf8')); -const acceptanceModelDir = path.resolve(__dirname, 'data/acceptance'); -const acceptanceTemplateMark = JSON.parse(fs.readFileSync(path.resolve(__dirname, 'data/acceptance', 'grammar.json'), 'utf8')); -const acceptanceMarkdown = normalizeNLs(fs.readFileSync(path.resolve(__dirname, 'data/acceptance', 'sample.md'), 'utf8')); -const acceptanceMarkdownCicero = normalizeNLs(fs.readFileSync(path.resolve(__dirname, 'data/acceptance', 'sample_cicero.md'), 'utf8')); -const acceptanceCiceroEdit = fs.readFileSync(path.resolve(__dirname, 'data/acceptance', 'ciceroedit.md'), 'utf8'); -const acceptanceCommonMark = JSON.parse(fs.readFileSync(path.resolve(__dirname, 'data/acceptance', 'commonmark.json'), 'utf8')); -const acceptanceCiceroMark = JSON.parse(fs.readFileSync(path.resolve(__dirname, 'data/acceptance', 'ciceromark.json'), 'utf8')); -const acceptanceCiceroMarkParsed = JSON.parse(fs.readFileSync(path.resolve(__dirname, 'data/acceptance', 'ciceromark_parsed.json'), 'utf8')); -const acceptanceCiceroMarkUnwrapped = JSON.parse(fs.readFileSync(path.resolve(__dirname, 'data/acceptance', 'ciceromark_unwrapped.json'), 'utf8')); -const acceptanceCiceroMarkUnquoted = JSON.parse(fs.readFileSync(path.resolve(__dirname, 'data/acceptance', 'ciceromark_unquoted.json'), 'utf8')); -const acceptancePlainText = normalizeNLs(fs.readFileSync(path.resolve(__dirname, 'data/acceptance', 'sample.txt'), 'utf8')); -const acceptanceHtml = normalizeNLs(fs.readFileSync(path.resolve(__dirname, 'data/acceptance', 'sample.html'), 'utf8')); - -// Sample test -const sampleHtml = fs.readFileSync(path.resolve(__dirname, 'data/sample', 'sample.html'), 'utf8'); +const acceptanceGrammarTokens = JSON.parse(fs.readFileSync(path.resolve(dataDir, 'acceptance', 'grammar_tokens.json'), 'utf8')); +const acceptanceModelDir = path.resolve(dataDir, 'acceptance'); +const acceptanceTemplateMark = JSON.parse(fs.readFileSync(path.resolve(dataDir, 'acceptance', 'grammar.json'), 'utf8')); +const acceptanceMarkdown = normalizeNLs(fs.readFileSync(path.resolve(dataDir, 'acceptance', 'sample.md'), 'utf8')); +const acceptanceMarkdownCicero = normalizeNLs(fs.readFileSync(path.resolve(dataDir, 'acceptance', 'sample_cicero.md'), 'utf8')); +const acceptanceCiceroEdit = fs.readFileSync(path.resolve(dataDir, 'acceptance', 'ciceroedit.md'), 'utf8'); +const acceptanceCommonMark = JSON.parse(fs.readFileSync(path.resolve(dataDir, 'acceptance', 'commonmark.json'), 'utf8')); +const acceptanceCiceroMark = JSON.parse(fs.readFileSync(path.resolve(dataDir, 'acceptance', 'ciceromark.json'), 'utf8')); +const acceptanceCiceroMarkParsed = JSON.parse(fs.readFileSync(path.resolve(dataDir, 'acceptance', 'ciceromark_parsed.json'), 'utf8')); +const acceptanceCiceroMarkUnwrapped = JSON.parse(fs.readFileSync(path.resolve(dataDir, 'acceptance', 'ciceromark_unwrapped.json'), 'utf8')); +const acceptanceCiceroMarkUnquoted = JSON.parse(fs.readFileSync(path.resolve(dataDir, 'acceptance', 'ciceromark_unquoted.json'), 'utf8')); +const acceptancePlainText = normalizeNLs(fs.readFileSync(path.resolve(dataDir, 'acceptance', 'sample.txt'), 'utf8')); +const acceptanceHtml = normalizeNLs(fs.readFileSync(path.resolve(dataDir, 'acceptance', 'sample.html'), 'utf8')); + +const sampleHtml = fs.readFileSync(path.resolve(dataDir, 'sample', 'sample.html'), 'utf8'); describe('#acceptance', () => { - let parameters; - before(async () => { + let parameters: any; + beforeAll(() => { const models = loadModels(acceptanceModelDir); parameters = { inputFileName: acceptanceGrammar, template: acceptanceGrammar, model: models, templateKind: 'contract' }; }); @@ -82,125 +56,110 @@ describe('#acceptance', () => { describe('#template', () => { it('markdown_template -> templatemark_tokens', async () => { const result = await transform(acceptanceGrammar, 'markdown_template', ['templatemark_tokens']); - // markdown-it seems to keep some non-JSON stuff around so we roundtrip to JSON for comparison - JSON.parse(JSON.stringify(result)).should.deep.equal(acceptanceGrammarTokens); + expect(JSON.parse(JSON.stringify(result))).toEqual(acceptanceGrammarTokens); }); it('markdown_template -> templatemark', async () => { const result = await transform(acceptanceGrammar, 'markdown_template', ['templatemark'], parameters); - result.should.deep.equal(acceptanceTemplateMark); + expect(result).toEqual(acceptanceTemplateMark); }); it('templatemark -> markdown_template', async () => { const result = await transform(acceptanceTemplateMark, 'templatemark', ['markdown_template'], parameters); - result.should.deep.equal(acceptanceGrammar); + expect(result).toEqual(acceptanceGrammar); }); }); describe('#markdown', () => { it('markdown -> commonmark', async () => { const result = await transform(acceptanceMarkdown, 'markdown', ['commonmark']); - result.should.deep.equal(acceptanceCommonMark); + expect(result).toEqual(acceptanceCommonMark); }); it('markdown -> commonmark (verbose)', async () => { - const result = await transform(acceptanceMarkdown, 'markdown', ['commonmark'], {}, {verbose: true}); - result.should.deep.equal(acceptanceCommonMark); + const result = await transform(acceptanceMarkdown, 'markdown', ['commonmark'], {}, { verbose: true }); + expect(result).toEqual(acceptanceCommonMark); }); }); describe('#markdown_cicero', () => { it('markdown_cicero -> ciceromark', async () => { const result = await transform(acceptanceMarkdownCicero, 'markdown_cicero', ['ciceromark']); - result.should.deep.equal(acceptanceCiceroMark); + expect(result).toEqual(acceptanceCiceroMark); }); - it('markdown -> commonmark (verbose)', async () => { - const result = await transform(acceptanceMarkdownCicero, 'markdown_cicero', ['ciceromark'], {}, {verbose: true}); - result.should.deep.equal(acceptanceCiceroMark); + it('markdown_cicero -> ciceromark (verbose)', async () => { + const result = await transform(acceptanceMarkdownCicero, 'markdown_cicero', ['ciceromark'], {}, { verbose: true }); + expect(result).toEqual(acceptanceCiceroMark); }); }); describe('#commonmark', () => { it('commonmark -> markdown', async () => { const result = await transform(acceptanceCommonMark, 'commonmark', ['markdown'], {}, {}); - result.should.equal(acceptanceMarkdown); + expect(result).toBe(acceptanceMarkdown); }); it('commonmark -> plaintext', async () => { const result = await transform(acceptanceCommonMark, 'commonmark', ['plaintext'], {}, {}); - result.should.equal(acceptancePlainText); + expect(result).toBe(acceptancePlainText); }); it('commonmark -> ciceromark', async () => { const result = await transform(acceptanceCommonMark, 'commonmark', ['ciceromark'], {}, {}); - result.should.deep.equal(acceptanceCommonMark); + expect(result).toEqual(acceptanceCommonMark); }); }); describe('#plaintext', () => { it('plaintext -> markdown', async () => { const result = await transform(acceptancePlainText, 'plaintext', ['markdown'], {}, {}); - result.should.equal(acceptancePlainText); + expect(result).toBe(acceptancePlainText); }); }); describe('#ciceromark', () => { it('ciceromark -> markdown_cicero', async () => { const result = await transform(acceptanceCiceroMark, 'ciceromark', ['markdown_cicero'], {}, {}); - result.should.equal(acceptanceMarkdownCicero); + expect(result).toBe(acceptanceMarkdownCicero); }); it('ciceromark -> commonmark', async () => { const result = await transform(acceptanceCiceroMarkParsed, 'ciceromark', ['commonmark'], {}, {}); - result.$class.should.equal(`${CommonMarkModel.NAMESPACE}.Document`); + expect(result.$class).toBe(`${CommonMarkModel.NAMESPACE}.Document`); }); - }); describe('#ciceromark_parsed', () => { it('ciceromark_parsed -> ciceromark_unquoted', async () => { const result = await transform(acceptanceCiceroMarkParsed, 'ciceromark_parsed', ['ciceromark_unquoted'], {}, {}); - result.should.deep.equal(acceptanceCiceroMarkUnquoted); + expect(result).toEqual(acceptanceCiceroMarkUnquoted); }); it('ciceromark_parsed -> html', async () => { const result = await transform(acceptanceCiceroMarkParsed, 'ciceromark_parsed', ['html'], {}, {}); - result.should.equal(acceptanceHtml); + expect(result).toBe(acceptanceHtml); }); it('ciceromark_parsed -> html (verbose)', async () => { - const result = await transform(acceptanceCiceroMarkParsed, 'ciceromark_parsed', ['html'], {}, {verbose: true}); - result.should.equal(acceptanceHtml); + const result = await transform(acceptanceCiceroMarkParsed, 'ciceromark_parsed', ['html'], {}, { verbose: true }); + expect(result).toBe(acceptanceHtml); }); }); describe('#ciceroedit', () => { it('ciceroedit -> ciceromark', async () => { const result = await transform(acceptanceCiceroEdit, 'ciceroedit', ['ciceromark'], {}, {}); - result.should.deep.equal(acceptanceCiceroMarkUnwrapped); + expect(result).toEqual(acceptanceCiceroMarkUnwrapped); }); }); describe('#multisteps', () => { it('ciceromark -> ciceromark_unquoted -> html', async () => { - const result = await transform(acceptanceCiceroMarkParsed, 'ciceromark', ['ciceromark_unquoted','html'], {}, {}); - result.should.startWith(''); - result.should.not.contain('"Party A"'); + const result = await transform(acceptanceCiceroMarkParsed, 'ciceromark', ['ciceromark_unquoted', 'html'], {}, {}); + expect(result.startsWith('')).toBe(true); + expect(result).not.toContain('"Party A"'); }); - - }); -}); - -describe('#template1', () => { - // eslint-disable-next-line no-unused-vars - let parameters; - before(async () => { - const grammarFile = './test/data/template1/grammar.tem.md'; - const grammar = fs.readFileSync(grammarFile, 'utf8'); - const modelDir = './test/data/template1'; - const models = loadModels(modelDir); - parameters = { inputFileName: grammarFile, template: grammar, model: models, templateKind: 'clause' }; }); }); @@ -208,7 +167,7 @@ describe('#sample', () => { describe('#html', () => { it('html -> ciceromark', async () => { const result = await transform(sampleHtml, 'html', ['ciceromark'], {}, {}); - result.$class.should.equal(`${CommonMarkModel.NAMESPACE}.Document`); + expect(result.$class).toBe(`${CommonMarkModel.NAMESPACE}.Document`); }); }); }); @@ -216,17 +175,17 @@ describe('#sample', () => { describe('#generateTransformationDiagram', () => { it('converts graph to PlantUML diagram', () => { const result = generateTransformationDiagram(); - result.trim().should.startWith('@startuml'); + expect(result.trim().startsWith('@startuml')).toBe(true); }); }); describe('#formatDescriptor', () => { it('Lookup valid format', () => { const result = formatDescriptor('commonmark'); - result.fileFormat.should.equal('json'); + expect(result.fileFormat).toBe('json'); }); it('Lookup invalid format', () => { - (() => formatDescriptor('foobar')).should.throw('Unknown format: foobar'); + expect(() => formatDescriptor('foobar')).toThrow('Unknown format: foobar'); }); }); diff --git a/packages/markdown-transform/src/transform.ts b/packages/markdown-transform/src/transform.ts new file mode 100644 index 00000000..2b69cacf --- /dev/null +++ b/packages/markdown-transform/src/transform.ts @@ -0,0 +1,49 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { TransformEngine, TransformationGraph } from './transformEngine'; +import builtinTransformationGraph from './builtinTransforms'; + +/** + * Create a new transformation engine + */ +export function createTransformationEngine(transformationGraph: TransformationGraph): TransformEngine { + return new TransformEngine(transformationGraph); +} + +/** + * Backwards-compatible singleton engine for the builtin transformation graph. + */ +export const builtinEngine = createTransformationEngine(builtinTransformationGraph); + +/** + * Return the format descriptor for a given format + */ +export const formatDescriptor = (format: string) => builtinEngine.formatDescriptor(format); + +/** + * Transforms from a source format to a list of destination formats. + */ +export const transform = ( + source: any, + sourceFormat: string, + destinationFormat: string[], + parameters?: any, + options?: any, +) => builtinEngine.transform(source, sourceFormat, destinationFormat, parameters, options); + +/** + * Converts the transformation graph into a PlantUML diagram string + */ +export const generateTransformationDiagram = () => builtinEngine.generateTransformationDiagram(); diff --git a/packages/markdown-transform/src/transformEngine.js b/packages/markdown-transform/src/transformEngine.js deleted file mode 100644 index 405a58b3..00000000 --- a/packages/markdown-transform/src/transformEngine.js +++ /dev/null @@ -1,280 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -const dijkstra = require('dijkstrajs'); -const find_path = dijkstra.find_path; - -/** - * Prune the graph for traversal - * @param {object} graph the input graph - * @returns {object} the raw graph for dijsktra - */ -function pruneGraph(graph) { - const result = {}; - for (const sourceKey in graph) { - result[sourceKey] = {}; - for (const targetKey in graph[sourceKey]) { - // Don't forget to remove the meta data which really isn't part of the graph - if (targetKey !== 'docs' && targetKey !== 'fileFormat') { - result[sourceKey][targetKey] = 1; - } - } - } - return result; -} - -/** - * A generic transformation engine. - * - * The format for the graph is a map (a JavaScript object) where each entry is a format, i.e., vertex in the graph with the following content: - * - * [sourceFormat]: { - * docs: // A format description - * fileFormat: // What kind of format it is (i.e., utf8, json, binary) - * [targetFormat1]: async (input, parameters, options) => { ... return result } - * [targetFormat2]: async (input, parameters, options) => { ... return result } - * ... - * } - * - * Each [targetFormat] entry defines an edge in the graph transforming [sourceFormat] to [targetFormat] - */ -class TransformEngine { - /** - * Construct the transformation engine - * @param {object} transformationGraph - the transformation graph - */ - constructor(transformationGraph) { - // Clone the graph - const { ...graph } = transformationGraph; - this.transformationGraph = graph; - this.refreshRawGraph(); - } - - /** - * Converts the graph of transformations into a PlantUML text string - * @returns {string} the PlantUML string - */ - generateTransformationDiagram() { - let result = `@startuml -hide empty description - -`; - const transformationGraph = this.getTransformationGraph(); - Object.keys(transformationGraph).forEach(src => { - result += `${src} : \n`; - result += `${src} : ${transformationGraph[src].docs}\n`; - Object.keys(transformationGraph[src]).forEach(dest => { - if(dest !== 'docs' && dest !== 'fileFormat') { - result += `${src} --> ${dest}\n`; - } - }); - result += '\n'; - }); - - result += '@enduml'; - return result; - } - - /** - * Transforms from a source format to a single destination format or - * throws an exception if the transformation is not possible. - * - * @param {object|string} source the input for the transformation - * @param {string} sourceFormat the input format - * @param {string} destinationFormat the destination format - * @param {object} [parameters] the transform parameters - * @param {object} [options] the transform options - * @param {boolean} [options.verbose] output verbose console logs - * @returns {Promise} result of the transformation - */ - async transformToDestination(source, sourceFormat, destinationFormat, parameters, options) { - let result = source; - const transformationGraph = this.getTransformationGraph(); - - const path = find_path(this.rawGraph, sourceFormat, destinationFormat); - for(let n=0; n < path.length-1; n++) { - const src = path[n]; - const dest = path[n+1]; - const srcNode = transformationGraph[src]; - const destinationNode = transformationGraph[dest]; - result = await srcNode[dest](result,parameters,options); - if(options && options.verbose) { - console.log(`Converted from ${src} to ${dest}. Result:`); - if(destinationNode.fileFormat !== 'binary') { - if(typeof result === 'object') { - console.log(JSON.stringify(result, null, 2)); - } else { - console.log(result); - } - } - else { - console.log(``); - } - } - } - - return result; - } - - /** - * Transforms from a source format to a list of destination formats, or - * throws an exception if the transformation is not possible. - * - * @param {object|string} source the input for the transformation - * @param {string} sourceFormat the input format - * @param {string[]} destinationFormat the destination format as an array, - * the transformation are applied in order to reach all formats in the array - * @param {object} [parameters] the transform parameters - * @param {object} [options] the transform options - * @param {boolean} [options.verbose] output verbose console logs - * @returns {Promise} result of the transformation - */ - async transform(source, sourceFormat, destinationFormat, parameters, options) { - let result = source; - options = options ? options : {}; - parameters = parameters ? parameters : {}; - if (sourceFormat === 'markdown') { - options.source = source; - } - - let currentSourceFormat = sourceFormat; - - for(let i=0; i < destinationFormat.length; i++) { - let destination = destinationFormat[i]; - result = await this.transformToDestination(result, currentSourceFormat, destination, parameters, options); - currentSourceFormat = destination; - } - return result; - } - - /** - * Return the format descriptor for a given format - * - * @param {string} format the format - * @return {object} the descriptor for that format - */ - formatDescriptor(format) { - const transformationGraph = this.getTransformationGraph(); - if (Object.prototype.hasOwnProperty.call(transformationGraph,format)) { - return transformationGraph[format]; - } else { - throw new Error('Unknown format: ' + format); - } - } - - /** - * Return the transformation graph - * - * @return {object} the transformation graph - */ - getTransformationGraph() { - return this.transformationGraph; - } - - /** - * Return all the available formats - * - * @return {object} the transformation graph - */ - getAllFormats() { - const transformationGraph = this.getTransformationGraph(); - return Object.keys(transformationGraph); - } - - /** - * Return all the available targets from a source formats - * - * @param {string} sourceFormat - the sourceFormat - * @return {object} the transformation graph - */ - getAllTargetFormats(sourceFormat) { - const transformationGraph = this.getTransformationGraph(); - if (!transformationGraph[sourceFormat]) { - throw new Error(`Unknown format: ${sourceFormat}`); - } - // eslint-disable-next-line no-unused-vars - const { docs, fileFormat, ...targets } = transformationGraph[sourceFormat]; - return Object.keys(targets); - } - - /** - * Add a new format - * - * @param {string} sourceFormat - the name of the source format - * @param {string} docs - the format description - * @param {string} fileFormat - the format type (either 'json', 'utf8' or 'binary') - */ - registerFormat(sourceFormat, docs, fileFormat) { - const transformationGraph = this.getTransformationGraph(); - if (transformationGraph[sourceFormat]) { - throw new Error(`Format already exists: ${sourceFormat}`); - } - transformationGraph[sourceFormat] = { - docs, - fileFormat - }; - } - - /** - * Add a new transform - * - * @param {string} sourceFormat - the name of the source format - * @param {string} targetFormat - the name of the targetFormat format - * @param {Function} transform - the transform (an async function to transform from sourceFormat to targetFormat) - */ - registerTransformation(sourceFormat, targetFormat, transform) { - const transformationGraph = this.getTransformationGraph(); - if (!transformationGraph[sourceFormat]) { - throw new Error(`Unknown format: ${sourceFormat}`); - } - if (!transformationGraph[targetFormat]) { - throw new Error(`Unknown format: ${targetFormat}`); - } - transformationGraph[sourceFormat][targetFormat] = transform; - // Rebuild the raw graph - this.refreshRawGraph(); - } - - /** - * Register a transform extension - * @param {object} extension - the transform extension, including format and transforms - */ - registerExtension(extension) { - if (extension.format) { - const { name: sourceFormat, docs, fileFormat } = extension.format; - this.registerFormat(sourceFormat, docs, fileFormat); - } - if (extension.transforms) { - for (let source in extension.transforms) { - const transforms = extension.transforms[source]; - for (let target in transforms) { - const transform = transforms[target]; - this.registerTransformation(source, target, transform); - } - } - } - } - - /** - * Refresh raw graph - * @private - */ - refreshRawGraph() { - this.rawGraph = pruneGraph(this.transformationGraph); - } -} - -module.exports = TransformEngine; diff --git a/packages/markdown-transform/test/transformEngine.js b/packages/markdown-transform/src/transformEngine.test.ts similarity index 60% rename from packages/markdown-transform/test/transformEngine.js rename to packages/markdown-transform/src/transformEngine.test.ts index ac02dccb..226f1a2e 100644 --- a/packages/markdown-transform/test/transformEngine.js +++ b/packages/markdown-transform/src/transformEngine.test.ts @@ -12,72 +12,56 @@ * limitations under the License. */ -'use strict'; +import * as fs from 'fs'; +import * as path from 'path'; +import { TransformEngine } from './transformEngine'; +import builtinTransformationGraph from './builtinTransforms'; -const chai = require('chai'); -const fs = require('fs'); -const path = require('path'); -chai.use(require('chai-string')); - -chai.should(); -chai.use(require('chai-things')); -chai.use(require('chai-as-promised')); - -const { TransformEngine, builtinTransformationGraph } = require('..'); - -/** - * Prepare the text for parsing (normalizes new lines, etc) - * @param {string} input - the text for the clause - * @return {string} - the normalized text for the clause - */ -function normalizeNLs(input) { - // we replace all \r and \n with \n - let text = input.replace(/\r/gm,''); - return text; +function normalizeNLs(input: string): string { + return input.replace(/\r/gm, ''); } -// A sample extension const wordcount = { format: { name: 'wordcount', docs: 'A number of words', - fileFormat: 'utf8' + fileFormat: 'utf8', }, transforms: { plaintext: { - wordcount: ((input, parameters, options) => { + wordcount: (input: string) => { const count = input.split(' ').length; return '' + count; - }), - } - } + }, + }, + }, }; -const acceptanceMarkdown = normalizeNLs(fs.readFileSync(path.resolve(__dirname, 'data/acceptance', 'sample.md'), 'utf8')); -const acceptanceCommonMark = JSON.parse(fs.readFileSync(path.resolve(__dirname, 'data/acceptance', 'commonmark.json'), 'utf8')); +const acceptanceMarkdown = normalizeNLs(fs.readFileSync(path.resolve(__dirname, '..', 'test', 'data', 'acceptance', 'sample.md'), 'utf8')); +const acceptanceCommonMark = JSON.parse(fs.readFileSync(path.resolve(__dirname, '..', 'test', 'data', 'acceptance', 'commonmark.json'), 'utf8')); describe('#transformationEngine', () => { describe('#create', () => { it('should create a new transformation engine', () => { const engine = new TransformEngine(builtinTransformationGraph); - engine.getAllFormats().length.should.equal(14); + expect(engine.getAllFormats().length).toBe(14); }); }); describe('#introspect', () => { it('should introspect the existing transforms', () => { const engine = new TransformEngine(builtinTransformationGraph); - engine.getAllFormats().length.should.equal(14); + expect(engine.getAllFormats().length).toBe(14); const format = engine.formatDescriptor('commonmark'); - format.fileFormat.should.equal('json'); + expect(format.fileFormat).toBe('json'); const targets = engine.getAllTargetFormats('commonmark'); - targets.should.deep.equal(['markdown', 'ciceromark', 'plaintext']); + expect(targets).toEqual(['markdown', 'ciceromark', 'plaintext']); }); it('should throw for a non existing format', () => { const engine = new TransformEngine(builtinTransformationGraph); - (() => engine.formatDescriptor('foo')).should.throw('Unknown format: foo'); - (() => engine.getAllTargetFormats('foo')).should.throw('Unknown format: foo'); + expect(() => engine.formatDescriptor('foo')).toThrow('Unknown format: foo'); + expect(() => engine.getAllTargetFormats('foo')).toThrow('Unknown format: foo'); }); }); @@ -85,37 +69,37 @@ describe('#transformationEngine', () => { it('should transform between two valid formats', async () => { const engine = new TransformEngine(builtinTransformationGraph); const result = await engine.transform(acceptanceMarkdown, 'markdown', ['commonmark']); - result.should.deep.equal(acceptanceCommonMark); + expect(result).toEqual(acceptanceCommonMark); }); }); describe('#extension', () => { - it('should create new format and transform', async () => { + it('should create new format and transform', () => { const engine = new TransformEngine(builtinTransformationGraph); engine.registerExtension(wordcount); - engine.getAllFormats().length.should.equal(15); + expect(engine.getAllFormats().length).toBe(15); }); it('should transform between an existing and new format', async () => { const engine = new TransformEngine(builtinTransformationGraph); engine.registerExtension(wordcount); const result = await engine.transform(acceptanceMarkdown, 'markdown', ['wordcount']); - result.should.equal('97'); + expect(result).toBe('97'); }); it('should throw when adding an existing format', () => { const engine = new TransformEngine(builtinTransformationGraph); - (() => engine.registerFormat('commonmark', 'another commonmark', 'not text')).should.throw('Format already exists: commonmark'); + expect(() => engine.registerFormat('commonmark', 'another commonmark', 'not text')).toThrow('Format already exists: commonmark'); }); it('should throw when creating a transform for a source that does not exist', () => { const engine = new TransformEngine(builtinTransformationGraph); - (() => engine.registerTransformation('foo', 'plaintext', (() => true))).should.throw('Unknown format: foo'); + expect(() => engine.registerTransformation('foo', 'plaintext', () => true)).toThrow('Unknown format: foo'); }); it('should throw when creating a transform for a target that does not exist', () => { const engine = new TransformEngine(builtinTransformationGraph); - (() => engine.registerTransformation('plaintext', 'foo', (() => true))).should.throw('Unknown format: foo'); + expect(() => engine.registerTransformation('plaintext', 'foo', () => true)).toThrow('Unknown format: foo'); }); }); }); diff --git a/packages/markdown-transform/src/transformEngine.ts b/packages/markdown-transform/src/transformEngine.ts new file mode 100644 index 00000000..ed8ac7fc --- /dev/null +++ b/packages/markdown-transform/src/transformEngine.ts @@ -0,0 +1,216 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// eslint-disable-next-line @typescript-eslint/no-var-requires +const dijkstra = require('dijkstrajs'); +const find_path = dijkstra.find_path; + +export interface FormatNode { + docs?: string; + fileFormat?: string; + [targetFormat: string]: any; +} + +export type TransformationGraph = Record; + +/** + * Prune the graph for traversal + */ +function pruneGraph(graph: TransformationGraph): Record> { + const result: Record> = {}; + for (const sourceKey in graph) { + result[sourceKey] = {}; + for (const targetKey in graph[sourceKey]) { + if (targetKey !== 'docs' && targetKey !== 'fileFormat') { + result[sourceKey][targetKey] = 1; + } + } + } + return result; +} + +/** + * A generic transformation engine. + */ +export class TransformEngine { + transformationGraph: TransformationGraph; + rawGraph: Record>; + + constructor(transformationGraph: TransformationGraph) { + const { ...graph } = transformationGraph; + this.transformationGraph = graph; + this.rawGraph = {}; + this.refreshRawGraph(); + } + + /** + * Converts the graph of transformations into a PlantUML text string + */ + generateTransformationDiagram(): string { + let result = `@startuml +hide empty description + +`; + const transformationGraph = this.getTransformationGraph(); + Object.keys(transformationGraph).forEach((src) => { + result += `${src} : \n`; + result += `${src} : ${transformationGraph[src].docs}\n`; + Object.keys(transformationGraph[src]).forEach((dest) => { + if (dest !== 'docs' && dest !== 'fileFormat') { + result += `${src} --> ${dest}\n`; + } + }); + result += '\n'; + }); + + result += '@enduml'; + return result; + } + + /** + * Transforms from a source format to a single destination format + */ + async transformToDestination( + source: any, + sourceFormat: string, + destinationFormat: string, + parameters?: any, + options?: { verbose?: boolean }, + ): Promise { + let result = source; + const transformationGraph = this.getTransformationGraph(); + + const path = find_path(this.rawGraph, sourceFormat, destinationFormat); + for (let n = 0; n < path.length - 1; n++) { + const src = path[n]; + const dest = path[n + 1]; + const srcNode = transformationGraph[src]; + const destinationNode = transformationGraph[dest]; + result = await srcNode[dest](result, parameters, options); + if (options && options.verbose) { + console.log(`Converted from ${src} to ${dest}. Result:`); + if (destinationNode.fileFormat !== 'binary') { + if (typeof result === 'object') { + console.log(JSON.stringify(result, null, 2)); + } else { + console.log(result); + } + } else { + console.log(``); + } + } + } + + return result; + } + + /** + * Transforms from a source format to a list of destination formats + */ + async transform( + source: any, + sourceFormat: string, + destinationFormat: string[], + parameters?: any, + options?: { verbose?: boolean; source?: any }, + ): Promise { + let result = source; + options = options ? options : {}; + parameters = parameters ? parameters : {}; + if (sourceFormat === 'markdown') { + options.source = source; + } + + let currentSourceFormat = sourceFormat; + + for (let i = 0; i < destinationFormat.length; i++) { + const destination = destinationFormat[i]; + result = await this.transformToDestination(result, currentSourceFormat, destination, parameters, options); + currentSourceFormat = destination; + } + return result; + } + + /** + * Return the format descriptor for a given format + */ + formatDescriptor(format: string): FormatNode { + const transformationGraph = this.getTransformationGraph(); + if (Object.prototype.hasOwnProperty.call(transformationGraph, format)) { + return transformationGraph[format]; + } else { + throw new Error('Unknown format: ' + format); + } + } + + getTransformationGraph(): TransformationGraph { + return this.transformationGraph; + } + + getAllFormats(): string[] { + return Object.keys(this.getTransformationGraph()); + } + + getAllTargetFormats(sourceFormat: string): string[] { + const transformationGraph = this.getTransformationGraph(); + if (!transformationGraph[sourceFormat]) { + throw new Error(`Unknown format: ${sourceFormat}`); + } + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { docs, fileFormat, ...targets } = transformationGraph[sourceFormat]; + return Object.keys(targets); + } + + registerFormat(sourceFormat: string, docs: string, fileFormat: string): void { + const transformationGraph = this.getTransformationGraph(); + if (transformationGraph[sourceFormat]) { + throw new Error(`Format already exists: ${sourceFormat}`); + } + transformationGraph[sourceFormat] = { docs, fileFormat }; + } + + registerTransformation(sourceFormat: string, targetFormat: string, transform: any): void { + const transformationGraph = this.getTransformationGraph(); + if (!transformationGraph[sourceFormat]) { + throw new Error(`Unknown format: ${sourceFormat}`); + } + if (!transformationGraph[targetFormat]) { + throw new Error(`Unknown format: ${targetFormat}`); + } + transformationGraph[sourceFormat][targetFormat] = transform; + this.refreshRawGraph(); + } + + registerExtension(extension: any): void { + if (extension.format) { + const { name: sourceFormat, docs, fileFormat } = extension.format; + this.registerFormat(sourceFormat, docs, fileFormat); + } + if (extension.transforms) { + for (const source in extension.transforms) { + const transforms = extension.transforms[source]; + for (const target in transforms) { + const transform = transforms[target]; + this.registerTransformation(source, target, transform); + } + } + } + } + + private refreshRawGraph(): void { + this.rawGraph = pruneGraph(this.transformationGraph); + } +} + +export default TransformEngine; diff --git a/packages/markdown-transform/transformations.png b/packages/markdown-transform/transformations.png index 77a45e40..62996632 100644 Binary files a/packages/markdown-transform/transformations.png and b/packages/markdown-transform/transformations.png differ diff --git a/packages/markdown-transform/tsconfig.json b/packages/markdown-transform/tsconfig.json index d2e666ea..fb3e9f82 100644 --- a/packages/markdown-transform/tsconfig.json +++ b/packages/markdown-transform/tsconfig.json @@ -1,10 +1,11 @@ { + "extends": "../../tsconfig.base.json", "compilerOptions": { - "allowJs": true, + "rootDir": "src", + "outDir": "lib", "declaration": true, - "emitDeclarationOnly": true, - "outDir": "types", - "strict": false + "sourceMap": true }, - "include": ["index.js", "lib/**/*.js"] + "include": ["src/**/*.ts"], + "exclude": ["src/**/*.test.ts", "lib", "node_modules"] } diff --git a/packages/markdown-transform/tsconfig.test.json b/packages/markdown-transform/tsconfig.test.json new file mode 100644 index 00000000..58810225 --- /dev/null +++ b/packages/markdown-transform/tsconfig.test.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "rootDir": ".", + "noEmit": true + }, + "include": ["src/**/*.ts"], + "exclude": ["lib", "node_modules"] +} diff --git a/packages/markdown-transform/types/index.d.ts b/packages/markdown-transform/types/index.d.ts deleted file mode 100644 index 562550ef..00000000 --- a/packages/markdown-transform/types/index.d.ts +++ /dev/null @@ -1,81 +0,0 @@ -export const formatDescriptor: (format: string) => object; -export const transform: (source: object | string, sourceFormat: string, destinationFormat: string[], parameters?: object, options?: object) => Promise; -export const TransformEngine: typeof import("./lib/transformEngine"); -export const builtinTransformationGraph: { - markdown_template: { - docs: string; - fileFormat: string; - templatemark_tokens: (input: any, parameters: any, options: any) => object; - }; - templatemark_tokens: { - docs: string; - fileFormat: string; - templatemark: (input: any, parameters: any, options: any) => Promise; - }; - templatemark: { - docs: string; - fileFormat: string; - markdown_template: (input: any, parameters: any, options: any) => string; - }; - markdown: { - docs: string; - fileFormat: string; - commonmark_tokens: (input: any, parameters: any, options: any) => object[]; - }; - commonmark_tokens: { - docs: string; - fileFormat: string; - commonmark: (input: any, parameters: any, options: any) => Promise; - }; - markdown_cicero: { - docs: string; - fileFormat: string; - ciceromark_tokens: (input: any, parameters: any, options: any) => object[]; - }; - ciceromark_tokens: { - docs: string; - fileFormat: string; - ciceromark: (input: any, parameters: any, options: any) => Promise; - }; - commonmark: { - docs: string; - fileFormat: string; - markdown: (input: any, parameters: any, options: any) => string; - ciceromark: (input: any, parameters: any, options: any) => import("@accordproject/markdown-common/types/model/commonmark").IDocument; - plaintext: (input: any, parameters: any, options: any) => string; - }; - ciceromark: { - docs: string; - fileFormat: string; - markdown_cicero: (input: any, parameters: any, options: any) => string; - commonmark: (input: any, parameters: any, options: any) => import("@accordproject/markdown-common/types/model/commonmark").IDocument; - ciceromark_parsed: (input: any, parameters: any, options: any) => any; - }; - ciceromark_parsed: { - docs: string; - fileFormat: string; - html: (input: any, parameters: any, options: any) => string; - ciceromark: (input: any, parameters: any, options: any) => import("@accordproject/markdown-common/types/model/commonmark").IDocument; - ciceromark_unquoted: (input: any, parameters: any, options: any) => import("@accordproject/markdown-common/types/model/commonmark").IDocument; - }; - plaintext: { - docs: string; - fileFormat: string; - markdown: (input: any, parameters: any, options: any) => any; - }; - ciceroedit: { - docs: string; - fileFormat: string; - ciceromark_parsed: (input: any, parameters: any, options: any) => import("@accordproject/markdown-common/types/model/commonmark").IDocument; - }; - ciceromark_unquoted: { - docs: string; - fileFormat: string; - ciceromark_parsed: (input: any, parameters: any, options: any) => any; - }; - html: { - docs: string; - fileFormat: string; - ciceromark_parsed: (input: any, parameters: any, options: any) => object; - }; -}; diff --git a/packages/markdown-transform/types/lib/builtinTransforms.d.ts b/packages/markdown-transform/types/lib/builtinTransforms.d.ts deleted file mode 100644 index 4c09c76e..00000000 --- a/packages/markdown-transform/types/lib/builtinTransforms.d.ts +++ /dev/null @@ -1,113 +0,0 @@ -export declare namespace markdown_template { - let docs: string; - let fileFormat: string; - function templatemark_tokens(input: any, parameters: any, options: any): object; -} -export declare namespace templatemark_tokens_1 { - let docs_1: string; - export { docs_1 as docs }; - let fileFormat_1: string; - export { fileFormat_1 as fileFormat }; - export function templatemark(input: any, parameters: any, options: any): Promise; -} -export declare namespace templatemark_1 { - let docs_2: string; - export { docs_2 as docs }; - let fileFormat_2: string; - export { fileFormat_2 as fileFormat }; - export function markdown_template_1(input: any, parameters: any, options: any): string; - export { markdown_template_1 as markdown_template }; -} -export declare namespace markdown { - let docs_3: string; - export { docs_3 as docs }; - let fileFormat_3: string; - export { fileFormat_3 as fileFormat }; - export function commonmark_tokens(input: any, parameters: any, options: any): object[]; -} -export declare namespace commonmark_tokens_1 { - let docs_4: string; - export { docs_4 as docs }; - let fileFormat_4: string; - export { fileFormat_4 as fileFormat }; - export function commonmark(input: any, parameters: any, options: any): Promise; -} -export declare namespace markdown_cicero { - let docs_5: string; - export { docs_5 as docs }; - let fileFormat_5: string; - export { fileFormat_5 as fileFormat }; - export function ciceromark_tokens(input: any, parameters: any, options: any): object[]; -} -export declare namespace ciceromark_tokens_1 { - let docs_6: string; - export { docs_6 as docs }; - let fileFormat_6: string; - export { fileFormat_6 as fileFormat }; - export function ciceromark(input: any, parameters: any, options: any): Promise; -} -export declare namespace commonmark_1 { - let docs_7: string; - export { docs_7 as docs }; - let fileFormat_7: string; - export { fileFormat_7 as fileFormat }; - export function markdown_1(input: any, parameters: any, options: any): string; - export { markdown_1 as markdown }; - export function ciceromark_1(input: any, parameters: any, options: any): import("@accordproject/markdown-common/types/model/commonmark").IDocument; - export { ciceromark_1 as ciceromark }; - export function plaintext(input: any, parameters: any, options: any): string; -} -export declare namespace ciceromark_2 { - let docs_8: string; - export { docs_8 as docs }; - let fileFormat_8: string; - export { fileFormat_8 as fileFormat }; - export function markdown_cicero_1(input: any, parameters: any, options: any): string; - export { markdown_cicero_1 as markdown_cicero }; - export function commonmark_2(input: any, parameters: any, options: any): import("@accordproject/markdown-common/types/model/commonmark").IDocument; - export { commonmark_2 as commonmark }; - export function ciceromark_parsed(input: any, parameters: any, options: any): any; -} -export declare namespace ciceromark_parsed_1 { - let docs_9: string; - export { docs_9 as docs }; - let fileFormat_9: string; - export { fileFormat_9 as fileFormat }; - export function html(input: any, parameters: any, options: any): string; - export function ciceromark_3(input: any, parameters: any, options: any): import("@accordproject/markdown-common/types/model/commonmark").IDocument; - export { ciceromark_3 as ciceromark }; - export function ciceromark_unquoted(input: any, parameters: any, options: any): import("@accordproject/markdown-common/types/model/commonmark").IDocument; -} -export declare namespace plaintext_1 { - let docs_10: string; - export { docs_10 as docs }; - let fileFormat_10: string; - export { fileFormat_10 as fileFormat }; - export function markdown_2(input: any, parameters: any, options: any): any; - export { markdown_2 as markdown }; -} -export declare namespace ciceroedit { - let docs_11: string; - export { docs_11 as docs }; - let fileFormat_11: string; - export { fileFormat_11 as fileFormat }; - export function ciceromark_parsed_2(input: any, parameters: any, options: any): import("@accordproject/markdown-common/types/model/commonmark").IDocument; - export { ciceromark_parsed_2 as ciceromark_parsed }; -} -export declare namespace ciceromark_unquoted_1 { - let docs_12: string; - export { docs_12 as docs }; - let fileFormat_12: string; - export { fileFormat_12 as fileFormat }; - export function ciceromark_parsed_3(input: any, parameters: any, options: any): any; - export { ciceromark_parsed_3 as ciceromark_parsed }; -} -export declare namespace html_1 { - let docs_13: string; - export { docs_13 as docs }; - let fileFormat_13: string; - export { fileFormat_13 as fileFormat }; - export function ciceromark_parsed_4(input: any, parameters: any, options: any): object; - export { ciceromark_parsed_4 as ciceromark_parsed }; -} -export { templatemark_tokens_1 as templatemark_tokens, templatemark_1 as templatemark, commonmark_tokens_1 as commonmark_tokens, ciceromark_tokens_1 as ciceromark_tokens, commonmark_1 as commonmark, ciceromark_2 as ciceromark, ciceromark_parsed_1 as ciceromark_parsed, plaintext_1 as plaintext, ciceromark_unquoted_1 as ciceromark_unquoted, html_1 as html }; diff --git a/packages/markdown-transform/types/lib/transform.d.ts b/packages/markdown-transform/types/lib/transform.d.ts deleted file mode 100644 index 5ab0d696..00000000 --- a/packages/markdown-transform/types/lib/transform.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -export function formatDescriptor(format: string): object; -export function transform(source: object | string, sourceFormat: string, destinationFormat: string[], parameters?: object, options?: object): Promise; -export function generateTransformationDiagram(): string; -/** - * This is instantiated here for backward compatibility - * @type {TransformEngine} - */ -export const builtinEngine: TransformEngine; -import TransformEngine = require("./transformEngine"); diff --git a/packages/markdown-transform/types/lib/transformEngine.d.ts b/packages/markdown-transform/types/lib/transformEngine.d.ts deleted file mode 100644 index 232566c8..00000000 --- a/packages/markdown-transform/types/lib/transformEngine.d.ts +++ /dev/null @@ -1,113 +0,0 @@ -export = TransformEngine; -/** - * A generic transformation engine. - * - * The format for the graph is a map (a JavaScript object) where each entry is a format, i.e., vertex in the graph with the following content: - * - * [sourceFormat]: { - * docs: // A format description - * fileFormat: // What kind of format it is (i.e., utf8, json, binary) - * [targetFormat1]: async (input, parameters, options) => { ... return result } - * [targetFormat2]: async (input, parameters, options) => { ... return result } - * ... - * } - * - * Each [targetFormat] entry defines an edge in the graph transforming [sourceFormat] to [targetFormat] - */ -declare class TransformEngine { - /** - * Construct the transformation engine - * @param {object} transformationGraph - the transformation graph - */ - constructor(transformationGraph: object); - transformationGraph: any; - /** - * Converts the graph of transformations into a PlantUML text string - * @returns {string} the PlantUML string - */ - generateTransformationDiagram(): string; - /** - * Transforms from a source format to a single destination format or - * throws an exception if the transformation is not possible. - * - * @param {object|string} source the input for the transformation - * @param {string} sourceFormat the input format - * @param {string} destinationFormat the destination format - * @param {object} [parameters] the transform parameters - * @param {object} [options] the transform options - * @param {boolean} [options.verbose] output verbose console logs - * @returns {Promise} result of the transformation - */ - transformToDestination(source: object | string, sourceFormat: string, destinationFormat: string, parameters?: object, options?: { - verbose?: boolean; - }): Promise; - /** - * Transforms from a source format to a list of destination formats, or - * throws an exception if the transformation is not possible. - * - * @param {object|string} source the input for the transformation - * @param {string} sourceFormat the input format - * @param {string[]} destinationFormat the destination format as an array, - * the transformation are applied in order to reach all formats in the array - * @param {object} [parameters] the transform parameters - * @param {object} [options] the transform options - * @param {boolean} [options.verbose] output verbose console logs - * @returns {Promise} result of the transformation - */ - transform(source: object | string, sourceFormat: string, destinationFormat: string[], parameters?: object, options?: { - verbose?: boolean; - }): Promise; - /** - * Return the format descriptor for a given format - * - * @param {string} format the format - * @return {object} the descriptor for that format - */ - formatDescriptor(format: string): object; - /** - * Return the transformation graph - * - * @return {object} the transformation graph - */ - getTransformationGraph(): object; - /** - * Return all the available formats - * - * @return {object} the transformation graph - */ - getAllFormats(): object; - /** - * Return all the available targets from a source formats - * - * @param {string} sourceFormat - the sourceFormat - * @return {object} the transformation graph - */ - getAllTargetFormats(sourceFormat: string): object; - /** - * Add a new format - * - * @param {string} sourceFormat - the name of the source format - * @param {string} docs - the format description - * @param {string} fileFormat - the format type (either 'json', 'utf8' or 'binary') - */ - registerFormat(sourceFormat: string, docs: string, fileFormat: string): void; - /** - * Add a new transform - * - * @param {string} sourceFormat - the name of the source format - * @param {string} targetFormat - the name of the targetFormat format - * @param {Function} transform - the transform (an async function to transform from sourceFormat to targetFormat) - */ - registerTransformation(sourceFormat: string, targetFormat: string, transform: Function): void; - /** - * Register a transform extension - * @param {object} extension - the transform extension, including format and transforms - */ - registerExtension(extension: object): void; - /** - * Refresh raw graph - * @private - */ - private refreshRawGraph; - rawGraph: any; -} diff --git a/packages/markdown-transform/webpack.config.js b/packages/markdown-transform/webpack.config.js index ba2ae87b..d5e168e4 100644 --- a/packages/markdown-transform/webpack.config.js +++ b/packages/markdown-transform/webpack.config.js @@ -14,60 +14,35 @@ 'use strict'; -let path = require('path'); +const path = require('path'); const webpack = require('webpack'); const packageJson = require('./package.json'); module.exports = { - entry: { - client: [ - './index.js' - ] - }, + entry: { client: ['./src/index.ts'] }, output: { path: path.join(__dirname, 'umd'), filename: 'markdown-transform.js', - library: { - name: 'markdown-transform', - type: 'umd', - }, + library: { name: 'markdown-transform', type: 'umd' }, umdNamedDefine: true, }, plugins: [ - new webpack.BannerPlugin(`Markdown Transform v${packageJson.version} - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License.`), - new webpack.DefinePlugin({ - 'process.env': { - 'NODE_ENV': JSON.stringify('production') - } - }), - new webpack.IgnorePlugin({ - resourceRegExp: /^\.$/, - contextRegExp: /jsdom$/, - }) + new webpack.BannerPlugin(`Markdown Transform v${packageJson.version}`), + new webpack.DefinePlugin({ 'process.env': { 'NODE_ENV': JSON.stringify('production') } }), + // Some transitive deps reference the Node `process` global directly. webpack 5 + // no longer polyfills this, so we provide a minimal browser shim. + new webpack.ProvidePlugin({ process: 'process/browser' }), + // jsdom is reachable through markdown-html, but its `require('jsdom')` is gated + // by `typeof DOMParser === 'undefined'`, which is false in browsers. + new webpack.IgnorePlugin({ resourceRegExp: /^\.$/, contextRegExp: /jsdom$/ }), ], - module: { - rules: [ - { - test: /\.js$/, - include: [path.join(__dirname, 'src')], - use: ['babel-loader'] - }, - { - test: /\.ne$/, - use:['raw-loader'] - } - ] - }, resolve: { + extensions: ['.ts', '.js'], + alias: { jsdom: false }, + // Transitive deps (asn1.js, parse-asn1, etc. pulled in by crypto-browserify) + // statically reference Node built-ins on code paths that are unreachable in + // the browser. Explicitly set them to `false` so webpack doesn't emit + // "Module not found" warnings. fallback: { 'fs': false, 'tls': false, @@ -77,11 +52,25 @@ module.exports = { 'util': false, 'url': false, 'assert': false, + 'vm': false, 'crypto': require.resolve('crypto-browserify'), 'stream': require.resolve('stream-browserify'), 'http': require.resolve('stream-http'), 'https': require.resolve('https-browserify'), 'zlib': require.resolve('browserify-zlib'), - } - } -}; \ No newline at end of file + }, + }, + module: { + rules: [ + { + test: /\.ts$/, + include: [path.join(__dirname, 'src')], + exclude: /\.test\.ts$/, + use: [{ + loader: 'ts-loader', + options: { transpileOnly: true, configFile: path.join(__dirname, 'tsconfig.json') }, + }], + }, + ], + }, +}; diff --git a/scripts/external/Models.hbs b/scripts/external/Models.hbs index 334345c9..d81f5bf1 100644 --- a/scripts/external/Models.hbs +++ b/scripts/external/Models.hbs @@ -12,12 +12,10 @@ * limitations under the License. */ -'use strict'; +export const NAMESPACE = '{{ namespace }}'; -const NAMESPACE = '{{ namespace }}'; - -const MODEL = ` +export const MODEL = ` {{{ model }}} `; -module.exports = { NAMESPACE, MODEL }; +export default { NAMESPACE, MODEL }; diff --git a/scripts/external/getExternalModels.js b/scripts/external/getExternalModels.js index b34d1885..076c5195 100644 --- a/scripts/external/getExternalModels.js +++ b/scripts/external/getExternalModels.js @@ -75,12 +75,14 @@ function buildExternalModels() { //console.log('contextArray --- ' + JSON.stringify(contextArray)); contextArray.forEach(function(context) { - // Only create a corresponding JS file if the js field exists + // Only create a corresponding TS file if the js field exists if (context.js) { + const outDir = path.join(scriptDir, context.js); + mkdirp.sync(outDir); const result = template(context); - const buildModelsJs = path.join(scriptDir,context.js,context.name + '.js'); - console.log('Creating: ' + buildModelsJs); - fs.writeFileSync(buildModelsJs,result); + const buildModelsTs = path.join(outDir, context.name + '.ts'); + console.log('Creating: ' + buildModelsTs); + fs.writeFileSync(buildModelsTs, result); } }); } diff --git a/scripts/external/models.json b/scripts/external/models.json index c473ba5c..b409cfc1 100644 --- a/scripts/external/models.json +++ b/scripts/external/models.json @@ -4,18 +4,18 @@ { "name": "CommonMarkModel", "namespace" : "org.accordproject.commonmark@0.5.0", "from": "https://models.accordproject.org/markdown/commonmark@0.5.0.cto", - "js": "packages/markdown-common/lib/externalModels" }, + "js": "packages/markdown-common/src/externalModels" }, { "name": "ConcertoMetaModel", "namespace" : "concerto.metamodel@1.0.0", "from": "https://models.accordproject.org/concerto/metamodel@1.0.0.cto", - "js": "packages/markdown-common/lib/externalModels" }, + "js": "packages/markdown-common/src/externalModels" }, { "name": "CiceroMarkModel", "namespace" : "org.accordproject.ciceromark@0.6.0", "from": "https://models.accordproject.org/markdown/ciceromark@0.6.0.cto", - "js": "packages/markdown-common/lib/externalModels" }, + "js": "packages/markdown-common/src/externalModels" }, { "name": "TemplateMarkModel", "namespace" : "org.accordproject.templatemark@0.5.0", "from": "https://models.accordproject.org/markdown/templatemark@0.5.0.cto", - "js": "packages/markdown-common/lib/externalModels" } + "js": "packages/markdown-common/src/externalModels" } ] } \ No newline at end of file diff --git a/tsconfig.base.json b/tsconfig.base.json new file mode 100644 index 00000000..fa291609 --- /dev/null +++ b/tsconfig.base.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "commonjs", + "moduleResolution": "node", + "lib": ["ES2020", "DOM"], + "declaration": true, + "sourceMap": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "resolveJsonModule": true, + "forceConsistentCasingInFileNames": true, + "skipLibCheck": true, + "strict": false, + "noImplicitAny": false, + "noImplicitThis": false, + "strictNullChecks": false, + "useUnknownInCatchVariables": false + } +}