Skip to content

Commit

Permalink
feat: migrate emphasis (#12963)
Browse files Browse the repository at this point in the history
| 📖 [PR App][pr] | 🎫 Resolve CX-1135, RM-11085 |
| :-------------: | :-----------------: |

## 🧰 Changes

Fixes incompatable emphasis during the migration.

I [fixed this](#988) in
`@readme/mdx`, but I think it'll be nicer to keep the migration specific
stuff in the main app.

## 💁 Customer Impact

<!-- PLEASE DESCRIBE IF THIS CHANGE AFFECTS CUSTOMERS AND HOW -->
<!-- This is extremely useful for Tony and Marc writing the changelog!
-->

## 🧬 QA & Testing

- [ ] insert the string `*the recommended initial action is to**initiate
a[reversal operation
(rollback)](https://docs.jupico.com/reference/ccrollback)**. *` into a
page
- [ ] note the bold and italics
- [ ] migrate the project
- [ ] confirm that the bold and italics render the same in the dash and
hub

<!-- -->

- [Broken on next][next].
- [Working in this PR][pr]!

[next]: https://next.readme.ninja
[pr]: https://readme-pr-12963.readme.ninja
[ui]: https://readme-pr-12963.readme.ninja/ui

<!-- Uncomment and unescape this if you don't want a PR app! -->
<!-- \[skip preview\] -->
  • Loading branch information
kellyjosephprice authored Oct 11, 2024
0 parents commit ea55754
Show file tree
Hide file tree
Showing 2 changed files with 162 additions and 0 deletions.
55 changes: 55 additions & 0 deletions emphasis.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import type { $TSFixMe } from '@readme/iso';
import type { Emphasis, Parent, Root, Strong, Text } from 'mdast';

import visit from 'unist-util-visit';

const strongTest = (node: any): node is Emphasis | Strong => ['emphasis', 'strong'].includes(node.type);

const addSpaceBefore = (index: number, parent: Parent) => {
if (!(index > 0 && parent.children[index - 1])) return;

const prev = parent.children[index - 1];
// @ts-ignore - I think this is also a dependency versioning issue
if (!('value' in prev) || prev.value.endsWith(' ') || prev.type === 'escape') return;

parent.children.splice(index, 0, { type: 'text', value: ' ' });
};

const addSpaceAfter = (index: number, parent: Parent) => {
if (!(index < parent.children.length - 1 && parent.children[index + 1])) return;

const nextChild = parent.children[index + 1];
if (!('value' in nextChild) || nextChild.value.startsWith(' ')) return;

parent.children.splice(index + 1, 0, { type: 'text', value: ' ' });
};

const trimEmphasis = (node: Emphasis | Strong, index: number, parent: Parent) => {
let trimmed = false;

// @ts-expect-error: the current version of visit is before the package
// types/mdast was created
visit(node, 'text', (child: Text) => {
const newValue = child.value.trim();

if (newValue !== child.value) {
trimmed = true;
child.value = newValue;
}
});

if (trimmed) {
addSpaceBefore(index, parent);
addSpaceAfter(index, parent);
}
};

const emphasisTransfomer = () => (tree: Root) => {
// @ts-expect-error: the current version of visit is before the package
// types/mdast was created
visit(tree, strongTest, trimEmphasis as $TSFixMe);

return tree;
};

export default emphasisTransfomer;
107 changes: 107 additions & 0 deletions index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import type { $TSFixMe } from '@readme/iso';
import type { Code, InlineCode, Root, Table, TableCell, TableRow } from 'mdast';
import type { VFile } from 'vfile';

import * as rdmd from '@readme/markdown';
import visit, { SKIP } from 'unist-util-visit';

import emphasisTransfomer from './emphasis';

const magicIndex = (i: number, j: number) => `${i === 0 ? 'h' : `${i - 1}`}-${j}`;

// @note: This regex is detect malformed lists that were created by the
// markdown editor. Consider the following markdown:
//
// ```
// * item 1
// * item 2
// * item 3
// ```
//
// This is a perfectly valid list. But when you put that text into a table
// cell, the editor does **bad** things. After a save and load cycle, it gets
// converted to this:
//
// ```
// \_ item 1
// \_ item 2
// \* item 3
// ```
//
// The following regex attempts to detect this pattern, and we'll convert it to
// something more standard.
const psuedoListRegex = /^(?<ws>[ \t]*)\\?[*_]\s*(?<item>.*)$/gm;

const migrateTableCells = (vfile: VFile) => (table: Table) => {
let json;
try {
const { position } = table;

if (position) {
json = JSON.parse(
vfile
.toString()
.slice(position.start.offset, position.end.offset)
.replace(/.*\[block:parameters\](.*)\[\/block\].*/s, '$1'),
);
}
} catch (err) {
/**
* This failure case is already handled by the following logic. Plus,
* because it's being handled internally, there's no way for our
* migration script to catch the error or keep track of it, and it just
* ends up blowing up the output logs.
*/
// console.error(err);
}

// @ts-expect-error: the current version of visit is before the package
// types/mdast was created
visit(table, 'tableRow', (row: TableRow, i: number) => {
// @ts-expect-error: the current version of visit is before the package
// types/mdast was created
visit(row, 'tableCell', (cell: TableCell, j: number) => {
let children = cell.children;

if (json && json.data[magicIndex(i, j)]) {
const string = json.data[magicIndex(i, j)].replace(psuedoListRegex, '$1- $2');

children = rdmd.mdast(string).children;
}

// eslint-disable-next-line no-param-reassign
cell.children = children.length > 1 ? children : ([{ type: 'paragraph', children }] as $TSFixMe);

return SKIP;
});

return SKIP;
});

// @ts-expect-error: the current version of visit is before the package
// types/mdast was created
visit(table, 'inlineCode', (code: Code | InlineCode) => {
if (code.value.includes('\n')) {
// eslint-disable-next-line no-param-reassign
code.type = 'code';
}
});
};

const compatability =
() =>
(tree: Root, vfile: VFile): Root => {
// @ts-expect-error: the current version of visit is before the package
// types/mdast was created
visit(tree, 'table', migrateTableCells(vfile));

return tree;
};

export const compatParser = (doc: string): Root => {
const proc = rdmd.processor().use(compatability).use(emphasisTransfomer);
const tree = proc.parse(doc);
proc.runSync(tree, doc);

return tree;
};

0 comments on commit ea55754

Please sign in to comment.