-
Notifications
You must be signed in to change notification settings - Fork 462
Feature/modernize v4 #176
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Feature/modernize v4 #176
Conversation
- Add TypeScript, ESLint, Jest, Babel, and react-native-builder-bob configs - Update package.json with modern deps (markdown-it ^14, React 18+, RN 0.73+) - Remove prop-types, react-native-fit-image, chokidar, fs-extra - Replace .npmignore with files field in package.json - Add CJS + ESM + TypeScript build targets via builder-bob
- Convert all 13 utility files from JS to TS/TSX - Create shared types.ts with ASTNode, RenderFunction, RenderRules, MarkdownStyles - Add 11 test suites with 47 unit tests covering all utilities - All tests pass, TypeScript compiles cleanly
- Convert styles, AstRenderer, parser, renderRules, plugins to TypeScript - Remove react-native-fit-image dependency, use RN Image instead - Remove broken PropTypes import from react in AstRenderer - Add 4 test suites (AstRenderer, parser, renderRules, PluginContainer) - Total: 15 test suites, 69 tests all passing
- Replace class component with functional component using useMemo hooks - Remove deprecated componentWillMount and componentWillReceiveProps - Remove PropTypes, use TypeScript MarkdownProps interface instead - Delete hand-written index.d.ts (auto-generated by builder-bob now) - Export types: ASTNode, RenderFunction, RenderRules, MarkdownStyles, MarkdownProps - Set allowJs: false in tsconfig (all source is now TypeScript) - Add 25 Markdown component tests - Total: 16 test suites, 94 tests all passing
- Fix all ESLint errors across source and test files - Add test file overrides in ESLint config (lenient on any in tests) - Disable react/prop-types rule (TypeScript handles this) - Fix unused React import and prefer-spread in index.tsx - Add integration test with 3 snapshot tests - Fix types path in package.json for builder-bob output - Bob build now produces clean CJS + ESM + TypeScript output - Total: 17 test suites, 99 tests all passing
Remove old example app, bin/ build scripts, export.json debug artifact, and legacy README.v1.3.6.md. Add CHANGELOG.md documenting all v4 breaking changes. Rewrite README.md with TypeScript examples, migration guide, and updated compatibility table.
Expo TypeScript app with markdown showcase covering headings, bold, italic, strikethrough, links, code blocks, blockquotes, tables, lists, and horizontal rules. Configured Metro to resolve the local library. Run with: cd example && npm start
Add default lineHeight fallback in Platform.select for web platform. Fix Metro config to use extraNodeModules instead of file:.. symlink to resolve the library, avoiding main field resolution errors.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Modernizes the library for a v4 release by migrating the implementation from JavaScript to TypeScript, updating the React/React Native + markdown-it baselines, and switching the package build pipeline to react-native-builder-bob with a refreshed Expo-based example app and a new Jest test suite.
Changes:
- Convert core renderer/parser/utilities from JS (+ hand-written d.ts) to TypeScript with generated type declarations.
- Update packaging/build/test tooling (bob outputs, tsconfigs, Jest, ESLint/Prettier) and adjust package entry points.
- Refresh default render rules (remove
react-native-fit-image) and replace the legacy RN example with an Expo example.
Reviewed changes
Copilot reviewed 133 out of 150 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| tsconfig.json | Base TS config for strict checking and JSX runtime. |
| tsconfig.build.json | Build-time TS overrides for emitting declarations/output. |
| src/types.ts | Introduces shared public types (ASTNode, render rule types, styles). |
| src/lib/util/tokensToAST.ts | TS port of token→AST conversion with typed token input. |
| src/lib/util/tokensToAST.js | Removes legacy JS implementation. |
| src/lib/util/stringToTokens.ts | TS port of string→markdown-it token parsing. |
| src/lib/util/stringToTokens.js | Removes legacy JS implementation. |
| src/lib/util/splitTextNonTextNodes.ts | TS port of React element splitting helper. |
| src/lib/util/splitTextNonTextNodes.js | Removes legacy JS implementation. |
| src/lib/util/openUrl.ts | Adds TS typing for URL opener utility. |
| src/lib/util/hasParents.ts | TS port of parent-check helper. |
| src/lib/util/hasParents.js | Removes legacy JS implementation. |
| src/lib/util/groupTextTokens.ts | TS port of text-group token wrapper. |
| src/lib/util/groupTextTokens.js | Removes legacy JS implementation. |
| src/lib/util/getUniqueID.ts | TS port of unique ID generator. |
| src/lib/util/getUniqueID.js | Removes legacy JS implementation. |
| src/lib/util/getTokenTypeByToken.ts | TS port of markdown-it token type normalization. |
| src/lib/util/getTokenTypeByToken.js | Removes legacy JS implementation. |
| src/lib/util/flattenInlineTokens.ts | TS port of inline token flattener. |
| src/lib/util/flattenInlineTokens.js | Removes legacy JS implementation. |
| src/lib/util/cleanupTokens.ts | TS port of token cleanup + link→blocklink conversion. |
| src/lib/util/cleanupTokens.js | Removes legacy JS implementation. |
| src/lib/util/applyStyle.tsx | TS port of style application helper for rendered children. |
| src/lib/util/applyStyle.js | Removes legacy JS implementation. |
| src/lib/util/Token.ts | TS port of Token class used by grouping utilities. |
| src/lib/util/Token.js | Removes legacy JS implementation. |
| src/lib/styles.ts | Updates default styles (list alignment + Platform.select defaults). |
| src/lib/renderRules.tsx | TS render rules; removes FitImage usage in favor of RN Image. |
| src/lib/renderRules.js | Removes legacy JS render rules. |
| src/lib/plugin/blockPlugin.ts | TS port of block container plugin. |
| src/lib/plugin/blockPlugin.js | Removes legacy JS plugin. |
| src/lib/plugin/PluginContainer.ts | TS port of plugin container helper. |
| src/lib/plugin/PluginContainer.js | Removes legacy JS helper. |
| src/lib/parser.ts | TS parser pipeline wiring (tokens→cleanup→group→AST→renderer). |
| src/lib/parser.js | Removes legacy JS parser. |
| src/lib/data/PlatformEnum.ts | TS platform enum with literal types. |
| src/lib/data/PlatformEnum.js | Removes legacy JS enum. |
| src/lib/AstRenderer.tsx | TS renderer class + typed render pipeline. |
| src/lib/AstRenderer.js | Removes legacy JS renderer. |
| src/index.tsx | Replaces class component with hook-based functional Markdown component; re-exports/types. |
| src/index.js | Removes legacy entrypoint. |
| src/index.d.ts | Removes hand-written types (replaced by generated declarations). |
| src/tests/util/tokensToAST.test.ts | Adds unit coverage for token→AST conversion. |
| src/tests/util/stringToTokens.test.ts | Adds unit coverage for markdown-it parsing wrapper. |
| src/tests/util/splitTextNonTextNodes.test.ts | Adds unit coverage for node splitting helper. |
| src/tests/util/hasParents.test.ts | Adds unit coverage for parent type checks. |
| src/tests/util/groupTextTokens.test.ts | Adds unit coverage for token grouping behavior. |
| src/tests/util/getUniqueID.test.ts | Adds unit coverage for unique ID generator. |
| src/tests/util/getTokenTypeByToken.test.ts | Adds unit coverage for token type normalization. |
| src/tests/util/flattenInlineTokens.test.ts | Adds unit coverage for inline flattening. |
| src/tests/util/cleanupTokens.test.ts | Adds unit coverage for token cleanup/blocklink conversion. |
| src/tests/util/applyStyle.test.tsx | Adds unit coverage for applyStyle behavior. |
| src/tests/util/Token.test.ts | Adds unit coverage for Token class. |
| src/tests/renderRules.test.tsx | Adds renderRules tests with react-test-renderer. |
| src/tests/plugin/PluginContainer.test.ts | Adds PluginContainer tests. |
| src/tests/parser.test.ts | Adds parser pipeline tests. |
| src/tests/integration.test.tsx | Adds snapshot-style integration coverage. |
| src/tests/Markdown.test.tsx | Adds component-level tests for new hook-based Markdown. |
| src/tests/AstRenderer.test.tsx | Adds AstRenderer tests. |
| package.json | Updates to v4 alpha, new entry points, bob build config, deps/devDeps, scripts. |
| jest.config.js | Adds Jest configuration for TS/TSX tests and coverage. |
| example/tsconfig.json | Adds TS config for the Expo example. |
| example/src/pluginRules.js | Removes legacy example rule file. |
| example/src/customMarkdownStyle.js | Removes legacy example style file. |
| example/src/copyAllCheckboxPlugin.js | Removes legacy example copy source. |
| example/src/copyAll.js | Removes legacy example copy source. |
| example/src/copy/linkedimg.js | Removes legacy example copy source. |
| example/src/copy/all.js | Removes legacy example copy source. |
| example/src/code.js | Removes legacy example copy source. |
| example/react-native-markdown-renderer/lib/util/stringToTokens.js | Removes vendored legacy library copy inside example. |
| example/react-native-markdown-renderer/lib/util/splitTextNonTextNodes.js | Removes vendored legacy library copy inside example. |
| example/react-native-markdown-renderer/lib/util/openUrl.js | Removes vendored legacy library copy inside example. |
| example/react-native-markdown-renderer/lib/util/hasParents.js | Removes vendored legacy library copy inside example. |
| example/react-native-markdown-renderer/lib/util/getUniqueID.js | Removes vendored legacy library copy inside example. |
| example/react-native-markdown-renderer/lib/util/getTokenTypeByToken.js | Removes vendored legacy library copy inside example. |
| example/react-native-markdown-renderer/lib/util/flattenInlineTokens.js | Removes vendored legacy library copy inside example. |
| example/react-native-markdown-renderer/lib/util/applyStyle.js | Removes vendored legacy library copy inside example. |
| example/react-native-markdown-renderer/lib/util/Token.js | Removes vendored legacy library copy inside example. |
| example/react-native-markdown-renderer/lib/styles.js | Removes vendored legacy library copy inside example. |
| example/react-native-markdown-renderer/lib/renderRules.js | Removes vendored legacy library copy inside example. |
| example/react-native-markdown-renderer/lib/plugin/blockPlugin.js | Removes vendored legacy library copy inside example. |
| example/react-native-markdown-renderer/lib/plugin/PluginContainer.js | Removes vendored legacy library copy inside example. |
| example/react-native-markdown-renderer/lib/parser.js | Removes vendored legacy library copy inside example. |
| example/react-native-markdown-renderer/lib/data/PlatformEnum.js | Removes vendored legacy library copy inside example. |
| example/react-native-markdown-renderer/lib/AstRenderer.js | Removes vendored legacy library copy inside example. |
| example/react-native-markdown-renderer/index.js | Removes vendored legacy library entry inside example. |
| example/react-native-markdown-renderer/index.d.ts | Removes vendored legacy library types inside example. |
| example/package.json | Converts example app to Expo-managed setup with updated dependencies. |
| example/metro.config.js | Adds Expo Metro config for local library development. |
| example/ios/exampleTests/exampleTests.m | Removes legacy native iOS test harness. |
| example/ios/exampleTests/Info.plist | Removes legacy native iOS test plist. |
| example/ios/example/main.m | Removes legacy native iOS entrypoint. |
| example/ios/example/Info.plist | Removes legacy native iOS app plist. |
| example/ios/example/Images.xcassets/Contents.json | Removes legacy iOS assets metadata. |
| example/ios/example/Images.xcassets/AppIcon.appiconset/Contents.json | Removes legacy iOS icon set metadata. |
| example/ios/example/Base.lproj/LaunchScreen.xib | Removes legacy iOS launch screen. |
| example/ios/example/AppDelegate.m | Removes legacy iOS AppDelegate implementation. |
| example/ios/example/AppDelegate.h | Removes legacy iOS AppDelegate header. |
| example/ios/example.xcodeproj/xcshareddata/xcschemes/example.xcscheme | Removes legacy Xcode scheme. |
| example/ios/example.xcodeproj/xcshareddata/xcschemes/example-tvOS.xcscheme | Removes legacy tvOS scheme. |
| example/ios/example-tvOSTests/Info.plist | Removes legacy tvOS test plist. |
| example/ios/example-tvOS/Info.plist | Removes legacy tvOS app plist. |
| example/index.ts | Adds Expo entrypoint registering root component. |
| example/index.js | Removes legacy RN CLI entrypoint. |
| example/app.json | Converts to Expo app config. |
| example/android/settings.gradle | Removes legacy Android project settings. |
| example/android/keystores/debug.keystore.properties | Removes legacy debug keystore props. |
| example/android/keystores/BUCK | Removes legacy Buck config. |
| example/android/gradlew.bat | Removes legacy Gradle wrapper script. |
| example/android/gradlew | Removes legacy Gradle wrapper script. |
| example/android/gradle/wrapper/gradle-wrapper.properties | Removes legacy wrapper properties. |
| example/android/gradle/wrapper/gradle-wrapper.jar | Removes legacy wrapper jar. |
| example/android/gradle.properties | Removes legacy Gradle properties. |
| example/android/build.gradle | Removes legacy top-level Gradle build. |
| example/android/app/src/main/res/values/styles.xml | Removes legacy Android resources. |
| example/android/app/src/main/res/values/strings.xml | Removes legacy Android resources. |
| example/android/app/src/main/java/com/example/MainApplication.java | Removes legacy Android application class. |
| example/android/app/src/main/java/com/example/MainActivity.java | Removes legacy Android activity. |
| example/android/app/src/main/AndroidManifest.xml | Removes legacy Android manifest. |
| example/android/app/proguard-rules.pro | Removes legacy Proguard rules. |
| example/android/app/build.gradle | Removes legacy Android app Gradle build. |
| example/android/app/BUCK | Removes legacy Buck config. |
| example/App.tsx | Adds new Expo example UI demonstrating markdown rendering. |
| example/App.js | Removes legacy example app implementation. |
| example/.watchmanconfig | Removes legacy Watchman config. |
| example/.gitignore | Updates ignore rules for Expo + generated native folders. |
| example/.gitattributes | Removes legacy gitattributes. |
| example/.flowconfig | Removes legacy Flow config. |
| example/.buckconfig | Removes legacy Buck config. |
| example/.babelrc | Removes legacy Babel config. |
| bin/watch.js | Removes legacy dev watch/copy script. |
| bin/test.js | Removes legacy debug script. |
| bin/example.js | Removes legacy embedded example markdown. |
| bin/data.json | Removes legacy debug output artifact. |
| bin/copy-to-run.js | Removes legacy copy script. |
| babel.config.js | Adds root Babel config for Jest/React Native preset. |
| README.v1.3.6.md | Removes legacy archived README. |
| README.md | Rewrites docs for v4 (TS, new props/types, migration section). |
| CHANGELOG.md | Adds v4 alpha changelog entry. |
| .npmignore | Removes .npmignore in favor of package.json files. |
| .gitignore | Updates ignores for build output and TS artifacts. |
| .eslintrc.js | Adds ESLint configuration for TS/React/React Hooks. |
Comments suppressed due to low confidence (1)
src/lib/util/cleanupTokens.ts:44
- Similar to
flattenInlineTokens, this loop drainsstackviashift(), which is O(n) per element and can become quadratic. Prefer a simple while-pop pattern (LIFO) or iterate overstackby index, depending on the intended ordering.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| export interface BlockPluginOptions { | ||
| marker?: string; | ||
| marker_end?: string; | ||
| validate?: (params: string) => boolean; | ||
| render?: (tokens: any[], idx: number, options: any, env: any, self: any) => any; | ||
| } | ||
|
|
||
| export default function blockPlugin(md: any, name: string, options?: BlockPluginOptions): void { | ||
| function validateDefault(params: string): boolean { | ||
| return params.trim().split(' ', 2)[0] === name; | ||
| } | ||
|
|
||
| function renderDefault(tokens: any[], idx: number, _options: any, env: any, self: any): any { | ||
| return self.renderToken(tokens, idx, _options, env, self); | ||
| } | ||
|
|
||
| const opts = options || {}; | ||
|
|
||
| const min_markers = 1; | ||
| const marker_str = opts.marker || `[${name}]`; | ||
| const marker_end_str = opts.marker_end || `[/${name}]`; | ||
| const marker_char = marker_str.charCodeAt(0); | ||
| const marker_len = marker_str.length; | ||
| const marker_end_len = marker_end_str.length; | ||
|
|
||
| const validate = opts.validate || validateDefault; | ||
| const render = opts.render || renderDefault; | ||
|
|
Copilot
AI
Feb 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BlockPluginOptions.validate is defined and a validate function is selected, but the container rule never calls it, so options.validate has no effect. Either invoke validate(params) before accepting the opening marker (and return false when it fails), or remove the option to avoid a misleading API.
| md.block.ruler.before('fence', 'container_checklist', container, { | ||
| alt: ['paragraph', 'reference', 'blockquote', 'list'], | ||
| }); |
Copilot
AI
Feb 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The block rule is registered under the fixed name 'container_checklist', which will collide/override if blockPlugin is used more than once for different container names. Use a rule key that includes name (e.g., container_${name}) so multiple containers can coexist.
| if (curr.type === 'inline' && curr.children && curr.children.length > 0) { | ||
| const children = flattenTokens(curr.children as T[]); | ||
| while (children.length) { | ||
| acc.push(children.shift()!); | ||
| } |
Copilot
AI
Feb 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This implementation repeatedly uses children.shift() inside a loop. shift() is O(n), so this becomes quadratic for large inline token lists. Prefer iterating with an index / for..of and pushing directly, or acc.push(...flattenTokens(curr.children)) to keep it linear.
| const marker_len = marker_str.length; | ||
| const marker_end_len = marker_end_str.length; | ||
|
|
||
| const validate = opts.validate || validateDefault; |
Copilot
AI
Feb 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused variable validate.
No description provided.