@astryxdesign/core 0.6.2 → 0.6.3-canary.0788c49
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +81 -0
- package/dist/BottomSheet/BottomSheet.d.ts +1 -1
- package/dist/BottomSheet/BottomSheet.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheet.js +3 -1
- package/dist/BottomSheet/BottomSheetPanel.d.ts +7 -5
- package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheetPanel.js +56 -19
- package/dist/Button/Button.d.ts.map +1 -1
- package/dist/Button/Button.js +13 -28
- package/dist/Chat/ChatComposer.d.ts.map +1 -1
- package/dist/Chat/ChatComposer.js +10 -5
- package/dist/Chat/ChatComposerDrawer.d.ts +19 -5
- package/dist/Chat/ChatComposerDrawer.d.ts.map +1 -1
- package/dist/Chat/ChatComposerDrawer.js +48 -23
- package/dist/Chat/ChatComposerInput.d.ts +11 -3
- package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
- package/dist/Chat/ChatComposerInput.js +63 -16
- package/dist/Chat/ChatDictationButton.d.ts +3 -2
- package/dist/Chat/ChatDictationButton.d.ts.map +1 -1
- package/dist/Chat/ChatDictationButton.js +6 -4
- package/dist/Chat/ChatLayout.d.ts +19 -0
- package/dist/Chat/ChatLayout.d.ts.map +1 -1
- package/dist/Chat/ChatLayout.js +19 -0
- package/dist/Chat/ChatLayoutScrollButton.d.ts.map +1 -1
- package/dist/Chat/ChatLayoutScrollButton.js +44 -34
- package/dist/Chat/ChatMessageBubble.d.ts +2 -0
- package/dist/Chat/ChatMessageBubble.d.ts.map +1 -1
- package/dist/Chat/ChatMessageBubble.js +5 -3
- package/dist/Chat/ChatMessageList.d.ts +2 -1
- package/dist/Chat/ChatMessageList.d.ts.map +1 -1
- package/dist/Chat/ChatMessageList.js +4 -3
- package/dist/Chat/ChatMessageMetadata.d.ts +5 -4
- package/dist/Chat/ChatMessageMetadata.d.ts.map +1 -1
- package/dist/Chat/ChatMessageMetadata.js +12 -9
- package/dist/Chat/ChatSendButton.d.ts +2 -1
- package/dist/Chat/ChatSendButton.d.ts.map +1 -1
- package/dist/Chat/ChatSendButton.js +6 -4
- package/dist/Chat/index.d.ts +1 -1
- package/dist/Chat/index.d.ts.map +1 -1
- package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
- package/dist/CheckboxInput/CheckboxInput.js +12 -2
- package/dist/Citation/Citation.d.ts +1 -1
- package/dist/Citation/Citation.d.ts.map +1 -1
- package/dist/Citation/Citation.js +3 -2
- package/dist/CodeBlock/highlightRanges.d.ts.map +1 -1
- package/dist/CodeBlock/highlightRanges.js +14 -3
- package/dist/Collapsible/Collapsible.d.ts.map +1 -1
- package/dist/Collapsible/Collapsible.js +6 -1
- package/dist/CommandPalette/CommandPaletteInput.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPaletteInput.js +1 -1
- package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
- package/dist/ComplexSelector/ComplexSelector.js +1 -1
- package/dist/DateInput/DateInput.d.ts.map +1 -1
- package/dist/DateInput/DateInput.js +4 -4
- package/dist/DateInput/NativeDateField.d.ts.map +1 -1
- package/dist/DateInput/NativeDateField.js +16 -16
- package/dist/DateInput/TouchDateField.d.ts.map +1 -1
- package/dist/DateInput/TouchDateField.js +2 -2
- package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
- package/dist/DateRangeInput/DateRangeInput.js +4 -4
- package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.js +8 -8
- package/dist/DateTimeInput/TouchDateTimeField.d.ts.map +1 -1
- package/dist/DateTimeInput/TouchDateTimeField.js +4 -4
- package/dist/DateTimeInput/nativePickerSegmentStyles.d.ts +9 -3
- package/dist/DateTimeInput/nativePickerSegmentStyles.d.ts.map +1 -1
- package/dist/DateTimeInput/nativePickerSegmentStyles.js +3 -3
- package/dist/Dialog/DialogHeader.d.ts +8 -1
- package/dist/Dialog/DialogHeader.d.ts.map +1 -1
- package/dist/Dialog/DialogHeader.js +13 -4
- package/dist/Field/InputClearButton.d.ts +4 -2
- package/dist/Field/InputClearButton.d.ts.map +1 -1
- package/dist/Field/InputClearButton.js +4 -2
- package/dist/Field/PanelSearchInput.d.ts.map +1 -1
- package/dist/Field/PanelSearchInput.js +1 -1
- package/dist/FileInput/FileInput.d.ts.map +1 -1
- package/dist/FileInput/FileInput.js +7 -7
- package/dist/Icon/Icon.d.ts +6 -31
- package/dist/Icon/Icon.d.ts.map +1 -1
- package/dist/Icon/Icon.js +8 -71
- package/dist/Icon/IconDefaultSizeContext.d.ts +4 -0
- package/dist/Icon/IconDefaultSizeContext.d.ts.map +1 -0
- package/dist/Icon/IconDefaultSizeContext.js +18 -0
- package/dist/Icon/IconSize.stylex.d.ts +65 -0
- package/dist/Icon/IconSize.stylex.d.ts.map +1 -0
- package/dist/Icon/IconSize.stylex.js +75 -0
- package/dist/Item/Item.d.ts +1 -1
- package/dist/Item/Item.d.ts.map +1 -1
- package/dist/Item/Item.js +4 -3
- package/dist/Kbd/Kbd.d.ts +5 -3
- package/dist/Kbd/Kbd.d.ts.map +1 -1
- package/dist/Kbd/Kbd.js +36 -42
- package/dist/Link/Link.d.ts.map +1 -1
- package/dist/Link/Link.js +6 -2
- package/dist/Link/useLinkComponent.d.ts +5 -15
- package/dist/Link/useLinkComponent.d.ts.map +1 -1
- package/dist/Link/useLinkComponent.js +34 -51
- package/dist/List/List.d.ts +19 -1
- package/dist/List/List.d.ts.map +1 -1
- package/dist/List/List.js +9 -2
- package/dist/List/ListContext.d.ts +1 -0
- package/dist/List/ListContext.d.ts.map +1 -1
- package/dist/List/ListContext.js +2 -1
- package/dist/List/ListItem.d.ts +1 -1
- package/dist/List/ListItem.d.ts.map +1 -1
- package/dist/List/ListItem.js +9 -2
- package/dist/Markdown/Markdown.d.ts +8 -2
- package/dist/Markdown/Markdown.d.ts.map +1 -1
- package/dist/Markdown/Markdown.fr23.bench.d.ts +2 -0
- package/dist/Markdown/Markdown.fr23.bench.d.ts.map +1 -0
- package/dist/Markdown/Markdown.fr23.bench.js +150 -0
- package/dist/Markdown/Markdown.fr23.sampling.d.ts +2 -0
- package/dist/Markdown/Markdown.fr23.sampling.d.ts.map +1 -0
- package/dist/Markdown/Markdown.fr23.sampling.js +43 -0
- package/dist/Markdown/Markdown.js +262 -109
- package/dist/Markdown/ast.d.ts +162 -0
- package/dist/Markdown/ast.d.ts.map +1 -0
- package/dist/Markdown/ast.js +74 -0
- package/dist/Markdown/index.d.ts +5 -3
- package/dist/Markdown/index.d.ts.map +1 -1
- package/dist/Markdown/index.js +3 -2
- package/dist/Markdown/parser/index.d.ts +10 -0
- package/dist/Markdown/parser/index.d.ts.map +1 -0
- package/dist/Markdown/parser/index.js +10 -0
- package/dist/Markdown/parser.d.ts +126 -58
- package/dist/Markdown/parser.d.ts.map +1 -1
- package/dist/Markdown/parser.js +726 -177
- package/dist/Markdown/plugins/frontmatter.d.ts +32 -0
- package/dist/Markdown/plugins/frontmatter.d.ts.map +1 -0
- package/dist/Markdown/plugins/frontmatter.js +132 -0
- package/dist/Markdown/plugins/index.d.ts +17 -0
- package/dist/Markdown/plugins/index.d.ts.map +1 -0
- package/dist/Markdown/plugins/index.js +14 -0
- package/dist/Markdown/plugins/protocol.d.ts +191 -0
- package/dist/Markdown/plugins/protocol.d.ts.map +1 -0
- package/dist/Markdown/plugins/protocol.js +957 -0
- package/dist/Markdown/plugins/semanticFence.d.ts +36 -0
- package/dist/Markdown/plugins/semanticFence.d.ts.map +1 -0
- package/dist/Markdown/plugins/semanticFence.js +213 -0
- package/dist/Markdown/plugins/softBreaks.d.ts +3 -0
- package/dist/Markdown/plugins/softBreaks.d.ts.map +1 -0
- package/dist/Markdown/plugins/softBreaks.js +172 -0
- package/dist/Markdown/plugins/sourceDecoration.d.ts +76 -0
- package/dist/Markdown/plugins/sourceDecoration.d.ts.map +1 -0
- package/dist/Markdown/plugins/sourceDecoration.js +393 -0
- package/dist/Markdown/plugins/textTransform.d.ts +30 -0
- package/dist/Markdown/plugins/textTransform.d.ts.map +1 -0
- package/dist/Markdown/plugins/textTransform.js +374 -0
- package/dist/Markdown/remark.d.ts +196 -0
- package/dist/Markdown/remark.d.ts.map +1 -0
- package/dist/Markdown/remark.js +1062 -0
- package/dist/Markdown/url.d.ts +6 -0
- package/dist/Markdown/url.d.ts.map +1 -0
- package/dist/Markdown/url.js +31 -0
- package/dist/Markdown/utils.d.ts +4 -2
- package/dist/Markdown/utils.d.ts.map +1 -1
- package/dist/Markdown/utils.js +2 -1
- package/dist/MetadataList/MetadataList.js +3 -3
- package/dist/NumberInput/NumberInput.d.ts.map +1 -1
- package/dist/NumberInput/NumberInput.js +4 -4
- package/dist/Outline/index.d.ts +1 -0
- package/dist/Outline/index.d.ts.map +1 -1
- package/dist/Outline/parseOutlineFromMarkdown.d.ts +7 -1
- package/dist/Outline/parseOutlineFromMarkdown.d.ts.map +1 -1
- package/dist/Outline/parseOutlineFromMarkdown.js +14 -8
- package/dist/Outline/useOutlineFromMarkdown.d.ts +3 -1
- package/dist/Outline/useOutlineFromMarkdown.d.ts.map +1 -1
- package/dist/Outline/useOutlineFromMarkdown.js +8 -3
- package/dist/PowerSearch/PowerSearchEditPopover.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearchEditPopover.js +46 -30
- package/dist/PowerSearch/PowerSearchValueEditor.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearchValueEditor.js +5 -0
- package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
- package/dist/ProgressBar/ProgressBar.js +36 -31
- package/dist/RadioList/RadioListItem.d.ts.map +1 -1
- package/dist/RadioList/RadioListItem.js +13 -1
- package/dist/ScrollableArea/ScrollableArea.d.ts +1 -0
- package/dist/ScrollableArea/ScrollableArea.d.ts.map +1 -1
- package/dist/ScrollableArea/ScrollableArea.js +1 -0
- package/dist/SegmentedControl/SegmentedControl.d.ts +1 -1
- package/dist/SegmentedControl/SegmentedControl.d.ts.map +1 -1
- package/dist/SegmentedControl/SegmentedControl.js +1 -0
- package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
- package/dist/SegmentedControl/SegmentedControlItem.js +5 -5
- package/dist/Selector/Selector.d.ts +9 -7
- package/dist/Selector/Selector.d.ts.map +1 -1
- package/dist/Selector/Selector.js +16 -7
- package/dist/SideNav/SideNav.d.ts +2 -1
- package/dist/SideNav/SideNav.d.ts.map +1 -1
- package/dist/SideNav/SideNav.js +9 -3
- package/dist/Slider/Slider.d.ts.map +1 -1
- package/dist/Slider/Slider.js +28 -12
- package/dist/Spinner/Spinner.d.ts.map +1 -1
- package/dist/Spinner/Spinner.js +23 -7
- package/dist/Switch/Switch.d.ts.map +1 -1
- package/dist/Switch/Switch.js +11 -0
- package/dist/TabList/Tab.d.ts +1 -1
- package/dist/TabList/Tab.d.ts.map +1 -1
- package/dist/TabList/Tab.js +20 -7
- package/dist/Table/Table.d.ts +1 -0
- package/dist/Table/Table.d.ts.map +1 -1
- package/dist/Table/Table.js +29 -13
- package/dist/TextArea/TextArea.d.ts.map +1 -1
- package/dist/TextArea/TextArea.js +1 -1
- package/dist/TextInput/TextInput.d.ts.map +1 -1
- package/dist/TextInput/TextInput.js +2 -2
- package/dist/TimeInput/TimeInput.d.ts.map +1 -1
- package/dist/TimeInput/TimeInput.js +4 -4
- package/dist/Timer/Timer.d.ts +49 -0
- package/dist/Timer/Timer.d.ts.map +1 -0
- package/dist/Timer/Timer.js +158 -0
- package/dist/Timer/index.d.ts +9 -0
- package/dist/Timer/index.d.ts.map +1 -0
- package/dist/Timer/index.js +10 -0
- package/dist/ToggleButton/ToggleButton.d.ts +9 -6
- package/dist/ToggleButton/ToggleButton.d.ts.map +1 -1
- package/dist/ToggleButton/ToggleButton.js +15 -27
- package/dist/TopNav/TopNavMenu.d.ts +1 -1
- package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
- package/dist/TopNav/TopNavMenu.js +3 -2
- package/dist/TreeList/TreeList.d.ts.map +1 -1
- package/dist/TreeList/TreeList.js +3 -0
- package/dist/TreeList/TreeListItem.d.ts +6 -1
- package/dist/TreeList/TreeListItem.d.ts.map +1 -1
- package/dist/TreeList/TreeListItem.js +12 -2
- package/dist/TreeList/TreeListTypes.d.ts +8 -1
- package/dist/TreeList/TreeListTypes.d.ts.map +1 -1
- package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
- package/dist/Typeahead/BaseTypeahead.js +16 -7
- package/dist/astryx.css +27 -11
- package/dist/hooks/scrollKeyboardDelegation.d.ts +3 -0
- package/dist/hooks/scrollKeyboardDelegation.d.ts.map +1 -0
- package/dist/hooks/scrollKeyboardDelegation.js +146 -0
- package/dist/hooks/useClickableContainer.d.ts +10 -1
- package/dist/hooks/useClickableContainer.d.ts.map +1 -1
- package/dist/hooks/useClickableContainer.js +15 -4
- package/dist/hooks/useScrollableArea.d.ts +6 -2
- package/dist/hooks/useScrollableArea.d.ts.map +1 -1
- package/dist/hooks/useScrollableArea.js +17 -5
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/theme/declarationBoundary.d.ts +17 -0
- package/dist/theme/declarationBoundary.d.ts.map +1 -0
- package/dist/theme/declarationBoundary.js +343 -0
- package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
- package/dist/theme/derivedVarRegistry.js +4 -0
- package/dist/theme/generateThemeRules.d.ts +13 -7
- package/dist/theme/generateThemeRules.d.ts.map +1 -1
- package/dist/theme/generateThemeRules.js +140 -34
- package/dist/utils/interactionOverlay.stylex.d.ts +8 -0
- package/dist/utils/interactionOverlay.stylex.d.ts.map +1 -1
- package/dist/utils/interactionOverlay.stylex.js +9 -0
- package/dist/utils/safeUrl.d.ts +11 -0
- package/dist/utils/safeUrl.d.ts.map +1 -0
- package/dist/utils/safeUrl.js +78 -0
- package/locales/af-ZA.json +530 -1
- package/locales/ar-SA.json +490 -1
- package/locales/ca-ES.json +514 -1
- package/locales/cs-CZ.json +498 -1
- package/locales/da-DK.json +522 -1
- package/locales/de-DE.json +522 -1
- package/locales/el-GR.json +498 -1
- package/locales/es-ES.json +510 -1
- package/locales/fi-FI.json +498 -1
- package/locales/fr-FR.json +534 -1
- package/locales/he-IL.json +498 -1
- package/locales/hu-HU.json +498 -1
- package/locales/it-IT.json +510 -1
- package/locales/ja-JP.json +490 -1
- package/locales/ko-KR.json +498 -1
- package/locales/nl-NL.json +534 -1
- package/locales/no-NO.json +510 -1
- package/locales/pl-PL.json +506 -1
- package/locales/pt-BR.json +506 -1
- package/locales/pt-PT.json +506 -1
- package/locales/ro-RO.json +506 -1
- package/locales/ru-RU.json +498 -1
- package/locales/sr-SP.json +498 -1
- package/locales/sv-SE.json +502 -1
- package/locales/tr-TR.json +498 -1
- package/locales/uk-UA.json +498 -1
- package/locales/vi-VN.json +502 -1
- package/locales/zh-CN.json +490 -1
- package/locales/zh-TW.json +490 -1
- package/package.json +24 -4
- package/src/BottomSheet/BottomSheet.doc.mjs +8 -1
- package/src/BottomSheet/BottomSheet.spec.md +46 -20
- package/src/BottomSheet/BottomSheet.test.tsx +6 -3
- package/src/BottomSheet/BottomSheet.tsx +3 -1
- package/src/BottomSheet/BottomSheetKeyboard.test.tsx +195 -0
- package/src/BottomSheet/BottomSheetPanel.test.tsx +11 -1
- package/src/BottomSheet/BottomSheetPanel.tsx +49 -16
- package/src/BottomSheet/__tests__/BottomSheetKeyboard.a11y.browser.spec.ts +344 -0
- package/src/Button/Button.doc.mjs +5 -5
- package/src/Button/Button.tsx +15 -13
- package/src/Button/__tests__/Button.a11y.chromium.spec.ts +17 -1
- package/src/Button/__tests__/Button.a11y.known-failures.ts +0 -29
- package/src/Button/__tests__/Button.a11y.renders.tsx +9 -2
- package/src/Button/__tests__/Button.a11y.states.ts +9 -0
- package/src/Chat/ChatComposer.doc.mjs +14 -0
- package/src/Chat/ChatComposer.spec.md +230 -0
- package/src/Chat/ChatComposer.test.tsx +57 -0
- package/src/Chat/ChatComposer.tsx +21 -9
- package/src/Chat/ChatComposerDrawer.doc.mjs +63 -5
- package/src/Chat/ChatComposerDrawer.spec.md +267 -0
- package/src/Chat/ChatComposerDrawer.test.tsx +81 -12
- package/src/Chat/ChatComposerDrawer.tsx +38 -9
- package/src/Chat/ChatComposerInput.doc.mjs +114 -26
- package/src/Chat/ChatComposerInput.spec.md +185 -0
- package/src/Chat/ChatComposerInput.test.tsx +113 -3
- package/src/Chat/ChatComposerInput.tsx +78 -13
- package/src/Chat/ChatComposerTokenElement.spec.md +152 -0
- package/src/Chat/ChatComposerTokenElement.test.tsx +85 -0
- package/src/Chat/ChatDictationButton.doc.mjs +3 -3
- package/src/Chat/ChatDictationButton.spec.md +160 -0
- package/src/Chat/ChatDictationButton.test.tsx +92 -27
- package/src/Chat/ChatDictationButton.tsx +8 -6
- package/src/Chat/ChatLayout.doc.mjs +12 -4
- package/src/Chat/ChatLayout.spec.md +271 -0
- package/src/Chat/ChatLayout.test.tsx +28 -0
- package/src/Chat/ChatLayout.tsx +19 -0
- package/src/Chat/ChatLayoutScrollButton.spec.md +338 -0
- package/src/Chat/ChatLayoutScrollButton.test.tsx +31 -0
- package/src/Chat/ChatLayoutScrollButton.tsx +31 -14
- package/src/Chat/ChatMessageBubble.doc.mjs +28 -15
- package/src/Chat/ChatMessageBubble.spec.md +263 -0
- package/src/Chat/ChatMessageBubble.test.tsx +93 -0
- package/src/Chat/ChatMessageBubble.tsx +8 -3
- package/src/Chat/ChatMessageList.spec.md +222 -0
- package/src/Chat/ChatMessageList.test.tsx +64 -2
- package/src/Chat/ChatMessageList.tsx +4 -3
- package/src/Chat/ChatMessageMetadata.doc.mjs +57 -3
- package/src/Chat/ChatMessageMetadata.spec.md +220 -0
- package/src/Chat/ChatMessageMetadata.test.tsx +34 -0
- package/src/Chat/ChatMessageMetadata.tsx +13 -13
- package/src/Chat/ChatSendButton.doc.mjs +32 -0
- package/src/Chat/ChatSendButton.spec.md +227 -0
- package/src/Chat/ChatSendButton.test.tsx +128 -8
- package/src/Chat/ChatSendButton.tsx +6 -4
- package/src/Chat/__tests__/ChatLayoutScrollButton.a11y.chromium.spec.ts +514 -0
- package/src/Chat/__tests__/ChatLayoutScrollButtonSurface.a11y.chromium.spec.ts +740 -0
- package/src/Chat/__tests__/ChatMessageBubble.a11y.chromium.spec.ts +457 -0
- package/src/Chat/__tests__/ChatMessageBubble.auditProvenance.test.ts +91 -0
- package/src/Chat/__tests__/ChatMessageBubble.auditProvenance.ts +91 -0
- package/src/Chat/__tests__/ChatMessageList.a11y.chromium.spec.ts +456 -0
- package/src/Chat/__tests__/ChatMessageMetadata.a11y.chromium.spec.ts +413 -0
- package/src/Chat/__tests__/ChatSendButton.a11y.chromium.spec.ts +1522 -0
- package/src/Chat/index.ts +1 -0
- package/src/CheckboxInput/CheckboxInput.doc.mjs +11 -0
- package/src/CheckboxInput/CheckboxInput.test.tsx +34 -0
- package/src/CheckboxInput/CheckboxInput.tsx +21 -1
- package/src/Citation/Citation.doc.mjs +2 -2
- package/src/Citation/Citation.test.tsx +17 -0
- package/src/Citation/Citation.tsx +4 -2
- package/src/ClickableCard/ClickableCard.doc.mjs +2 -2
- package/src/ClickableCard/ClickableCard.test.tsx +102 -5
- package/src/CodeBlock/highlightRanges.test.ts +143 -1
- package/src/CodeBlock/highlightRanges.ts +15 -5
- package/src/Collapsible/Collapsible.doc.mjs +11 -0
- package/src/Collapsible/Collapsible.test.tsx +21 -0
- package/src/Collapsible/Collapsible.tsx +5 -0
- package/src/CommandPalette/CommandPaletteInput.tsx +6 -1
- package/src/ComplexSelector/ComplexSelector.tsx +6 -1
- package/src/DateInput/DateInput.tsx +6 -1
- package/src/DateInput/NativeDateField.tsx +24 -8
- package/src/DateInput/TouchDateField.tsx +6 -2
- package/src/DateRangeInput/DateRangeInput.tsx +6 -1
- package/src/DateTimeInput/DateTimeInput.tsx +6 -1
- package/src/DateTimeInput/TouchDateTimeField.tsx +6 -1
- package/src/DateTimeInput/nativePickerSegmentStyles.ts +24 -8
- package/src/Dialog/Dialog.doc.mjs +2 -0
- package/src/Dialog/DialogHeader.doc.mjs +22 -0
- package/src/Dialog/DialogHeader.test.tsx +167 -3
- package/src/Dialog/DialogHeader.tsx +37 -6
- package/src/Dialog/modules/DialogHeader.spec.md +101 -42
- package/src/DropdownMenu/DropdownMenu.test.tsx +5 -2
- package/src/Field/InputClearButton.test.tsx +17 -3
- package/src/Field/InputClearButton.tsx +5 -2
- package/src/Field/PanelSearchInput.tsx +5 -2
- package/src/FieldStatus/__tests__/StatusMessage.a11y.states.ts +1 -1
- package/src/FileInput/FileInput.tsx +11 -2
- package/src/Icon/Icon.doc.mjs +7 -5
- package/src/Icon/Icon.spec.md +81 -40
- package/src/Icon/Icon.test.tsx +6 -3
- package/src/Icon/Icon.tsx +20 -68
- package/src/Icon/IconDefaultSizeContext.ts +23 -0
- package/src/Icon/IconSize.stylex.ts +70 -0
- package/src/IconButton/IconButton.doc.mjs +3 -3
- package/src/IconButton/IconButton.test.tsx +40 -0
- package/src/Item/Item.doc.mjs +6 -2
- package/src/Item/Item.tsx +9 -3
- package/src/Kbd/Kbd.doc.mjs +3 -3
- package/src/Kbd/Kbd.test.tsx +57 -1
- package/src/Kbd/Kbd.tsx +57 -37
- package/src/Layout/LayoutFooter.doc.mjs +17 -0
- package/src/Link/Link.doc.mjs +17 -4
- package/src/Link/Link.test.tsx +24 -0
- package/src/Link/Link.tsx +5 -0
- package/src/Link/LinkProvider.doc.mjs +2 -2
- package/src/Link/__tests__/Link.navigation.a11y.chromium.spec.ts +261 -0
- package/src/Link/useLinkComponent.test.tsx +333 -0
- package/src/Link/useLinkComponent.ts +29 -57
- package/src/List/List.doc.mjs +6 -0
- package/src/List/List.spec.md +200 -0
- package/src/List/List.test.tsx +117 -1
- package/src/List/List.tsx +22 -2
- package/src/List/ListContext.tsx +3 -1
- package/src/List/ListItem.doc.mjs +2 -2
- package/src/List/ListItem.source-build.test.mjs +41 -0
- package/src/List/ListItem.tsx +26 -2
- package/src/Markdown/Markdown.doc.mjs +300 -2
- package/src/Markdown/Markdown.fr23.bench.ts +174 -0
- package/src/Markdown/Markdown.fr23.perf.test.ts +249 -0
- package/src/Markdown/Markdown.fr23.sampling.ts +49 -0
- package/src/Markdown/Markdown.helpers.perf.test.ts +489 -0
- package/src/Markdown/Markdown.public.test.ts +130 -3
- package/src/Markdown/Markdown.renderBoundary.test.tsx +72 -0
- package/src/Markdown/Markdown.spec.md +210 -45
- package/src/Markdown/Markdown.test.tsx +87 -3
- package/src/Markdown/Markdown.tsx +414 -135
- package/src/Markdown/ast.test.ts +307 -0
- package/src/Markdown/ast.ts +337 -0
- package/src/Markdown/incremental.test.ts +43 -0
- package/src/Markdown/index.ts +37 -1
- package/src/Markdown/modules/remark.spec.md +269 -0
- package/src/Markdown/modules/softBreaks.spec.md +137 -0
- package/src/Markdown/parser/index.ts +64 -0
- package/src/Markdown/parser.public.test.ts +103 -0
- package/src/Markdown/parser.test.ts +38 -6
- package/src/Markdown/parser.ts +1221 -242
- package/src/Markdown/plugins/frontmatter.test.tsx +125 -0
- package/src/Markdown/plugins/frontmatter.ts +198 -0
- package/src/Markdown/plugins/index.ts +54 -0
- package/src/Markdown/plugins/protocol.test.tsx +1022 -0
- package/src/Markdown/plugins/protocol.ts +1618 -0
- package/src/Markdown/plugins/semanticFence.test.tsx +546 -0
- package/src/Markdown/plugins/semanticFence.ts +424 -0
- package/src/Markdown/plugins/softBreaks.test.tsx +172 -0
- package/src/Markdown/plugins/softBreaks.ts +169 -0
- package/src/Markdown/plugins/sourceDecoration.test.tsx +799 -0
- package/src/Markdown/plugins/sourceDecoration.ts +539 -0
- package/src/Markdown/plugins/textTransform.test.ts +467 -0
- package/src/Markdown/plugins/textTransform.ts +554 -0
- package/src/Markdown/remark.test.tsx +1877 -0
- package/src/Markdown/remark.ts +1574 -0
- package/src/Markdown/url.ts +36 -0
- package/src/Markdown/utils.ts +13 -1
- package/src/MetadataList/MetadataList.test.tsx +4 -2
- package/src/MetadataList/MetadataList.tsx +3 -3
- package/src/MultiSelector/MultiSelector.test.tsx +1 -35
- package/src/MultiSelector/__tests__/MultiSelector.listbox.a11y.test.tsx +128 -0
- package/src/NumberInput/NumberInput.tsx +6 -1
- package/src/Outline/Outline.doc.mjs +7 -5
- package/src/Outline/Outline.spec.md +1 -1
- package/src/Outline/index.ts +1 -0
- package/src/Outline/modules/parseOutlineFromMarkdown.spec.md +142 -0
- package/src/Outline/parseOutlineFromMarkdown.ts +40 -10
- package/src/Outline/useOutlineFromMarkdown.ts +15 -3
- package/src/PowerSearch/PowerSearch.doc.mjs +2 -2
- package/src/PowerSearch/PowerSearchEditPopover.test.tsx +150 -1
- package/src/PowerSearch/PowerSearchEditPopover.tsx +51 -28
- package/src/PowerSearch/PowerSearchValueEditor.test.tsx +51 -0
- package/src/PowerSearch/PowerSearchValueEditor.tsx +4 -0
- package/src/ProgressBar/ProgressBar.test.tsx +29 -11
- package/src/ProgressBar/ProgressBar.tsx +59 -72
- package/src/RadioList/RadioList.doc.mjs +11 -0
- package/src/RadioList/RadioList.test.tsx +32 -0
- package/src/RadioList/RadioListItem.tsx +25 -1
- package/src/ScrollableArea/ScrollableArea.tsx +1 -0
- package/src/ScrollableArea/modules/useScrollableArea.spec.md +50 -22
- package/src/SegmentedControl/SegmentedControl.doc.mjs +2 -2
- package/src/SegmentedControl/SegmentedControl.test.tsx +61 -0
- package/src/SegmentedControl/SegmentedControl.tsx +2 -1
- package/src/SegmentedControl/SegmentedControlItem.tsx +6 -9
- package/src/Selector/Selector.doc.mjs +3 -3
- package/src/Selector/Selector.spec.md +51 -49
- package/src/Selector/Selector.test.tsx +61 -55
- package/src/Selector/Selector.tsx +34 -15
- package/src/Selector/__tests__/Listbox.a11y.chromium.spec.ts +130 -0
- package/src/Selector/__tests__/Listbox.a11y.dom.ts +38 -0
- package/src/Selector/__tests__/Listbox.a11y.inventory.test.ts +63 -0
- package/src/Selector/__tests__/Listbox.a11y.known-failures.ts +37 -0
- package/src/Selector/__tests__/Listbox.a11y.states.ts +232 -0
- package/src/Selector/__tests__/Selector.a11y.chromium.spec.ts +45 -0
- package/src/Selector/__tests__/Selector.listbox.a11y.test.tsx +121 -0
- package/src/SideNav/SideNav.doc.mjs +1 -1
- package/src/SideNav/SideNav.test.tsx +10 -0
- package/src/SideNav/SideNav.tsx +14 -2
- package/src/Slider/Slider.doc.mjs +17 -3
- package/src/Slider/Slider.test.tsx +221 -0
- package/src/Slider/Slider.tsx +40 -9
- package/src/Spinner/Spinner.test.tsx +4 -0
- package/src/Spinner/Spinner.tsx +31 -18
- package/src/Switch/Switch.doc.mjs +11 -0
- package/src/Switch/Switch.test.tsx +16 -0
- package/src/Switch/Switch.tsx +28 -0
- package/src/TabList/Tab.tsx +35 -7
- package/src/TabList/TabList.doc.mjs +11 -0
- package/src/TabList/TabList.spec.md +204 -0
- package/src/TabList/TabList.test.tsx +66 -0
- package/src/TabList/__tests__/Tabs.a11y.known-failures.ts +1 -34
- package/src/Table/Table.doc.mjs +1 -1
- package/src/Table/Table.spec.md +54 -40
- package/src/Table/Table.test.tsx +83 -10
- package/src/Table/Table.tsx +30 -22
- package/src/Table/__tests__/TableScroll.a11y.chromium.spec.ts +137 -0
- package/src/TextArea/TextArea.tsx +6 -1
- package/src/TextInput/TextInput.test.tsx +20 -1
- package/src/TextInput/TextInput.tsx +6 -1
- package/src/TimeInput/TimeInput.tsx +6 -1
- package/src/Timer/Timer.doc.mjs +183 -0
- package/src/Timer/Timer.spec.md +215 -0
- package/src/Timer/Timer.test.tsx +302 -0
- package/src/Timer/Timer.tsx +258 -0
- package/src/Timer/index.ts +11 -0
- package/src/ToggleButton/ToggleButton.doc.mjs +3 -3
- package/src/ToggleButton/ToggleButton.test.tsx +299 -4
- package/src/ToggleButton/ToggleButton.tsx +26 -33
- package/src/ToggleButton/ToggleButtonGroup.doc.mjs +3 -3
- package/src/ToggleButton/__tests__/ToggleButton.a11y.chromium.spec.ts +209 -0
- package/src/Token/Token.doc.mjs +2 -2
- package/src/Tokenizer/Tokenizer.spec.md +142 -75
- package/src/TopNav/TopNavMenu.doc.mjs +2 -2
- package/src/TopNav/TopNavMenu.test.tsx +30 -0
- package/src/TopNav/TopNavMenu.tsx +7 -2
- package/src/TreeList/TreeList.doc.mjs +3 -3
- package/src/TreeList/TreeList.spec.md +123 -75
- package/src/TreeList/TreeList.test.tsx +29 -0
- package/src/TreeList/TreeList.tsx +3 -0
- package/src/TreeList/TreeListItem.tsx +15 -1
- package/src/TreeList/TreeListTypes.ts +11 -1
- package/src/Typeahead/BaseTypeahead.spec.md +4 -3
- package/src/Typeahead/BaseTypeahead.tsx +21 -7
- package/src/Typeahead/Typeahead.test.tsx +53 -0
- package/src/__tests__/PressedState.a11y.chromium.spec.ts +813 -0
- package/src/__tests__/pressState.ts +93 -0
- package/src/hooks/scrollKeyboardDelegation.test.ts +155 -0
- package/src/hooks/scrollKeyboardDelegation.ts +233 -0
- package/src/hooks/useClickableContainer.test.tsx +236 -0
- package/src/hooks/useClickableContainer.ts +20 -4
- package/src/hooks/useScrollableArea.doc.mjs +15 -3
- package/src/hooks/useScrollableArea.test.tsx +59 -1
- package/src/hooks/useScrollableArea.ts +34 -10
- package/src/index.ts +3 -0
- package/src/inputFontFloor.test.ts +106 -0
- package/src/tailwind-theme.css +0 -6
- package/src/theme/Theme.test.tsx +32 -0
- package/src/theme/declarationBoundary.test.ts +126 -0
- package/src/theme/declarationBoundary.ts +347 -0
- package/src/theme/derivedVarRegistry.test.ts +7 -0
- package/src/theme/derivedVarRegistry.ts +1 -0
- package/src/theme/generateThemeRules.test.ts +526 -1
- package/src/theme/generateThemeRules.ts +228 -50
- package/src/theme/tokenValueCompat.test.ts +4 -1
- package/src/utils/interactionOverlay.stylex.ts +20 -0
- package/src/utils/safeUrl.test.ts +88 -0
- package/src/utils/safeUrl.ts +85 -0
package/src/Markdown/parser.ts
CHANGED
|
@@ -2,45 +2,79 @@
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* @file parser.ts
|
|
5
|
-
* @input Markdown string
|
|
6
|
-
* @output
|
|
7
|
-
* helpers
|
|
8
|
-
*
|
|
9
|
-
* Outline
|
|
10
|
-
* @position Core parser; consumed by Markdown.tsx and Outline
|
|
5
|
+
* @input Markdown string, released parse options, and optional ordered plugins
|
|
6
|
+
* @output Canonical MDAST-aligned nodes for public/server consumers plus unchanged
|
|
7
|
+
* released parser-node projections; shared heading slug helpers
|
|
8
|
+
* @position Core parser and compatibility boundary; consumed by public parser entry,
|
|
9
|
+
* Markdown, and Outline
|
|
11
10
|
*/
|
|
12
11
|
|
|
12
|
+
import {
|
|
13
|
+
getMarkdownAstLegacyCodeLanguage,
|
|
14
|
+
markMarkdownAstLegacyCodeLanguage,
|
|
15
|
+
} from './ast';
|
|
16
|
+
import type {
|
|
17
|
+
MarkdownAstBlockContent,
|
|
18
|
+
MarkdownAstList,
|
|
19
|
+
MarkdownAstListItem,
|
|
20
|
+
MarkdownAstPhrasingContent,
|
|
21
|
+
MarkdownAstPosition,
|
|
22
|
+
MarkdownAstRoot,
|
|
23
|
+
MarkdownAstTableCell,
|
|
24
|
+
MarkdownAstTableRow,
|
|
25
|
+
} from './ast';
|
|
26
|
+
import {
|
|
27
|
+
applyMarkdownTransforms,
|
|
28
|
+
freezeMarkdownPluginData,
|
|
29
|
+
isMarkdownPluginData,
|
|
30
|
+
prepareMarkdownPlugins,
|
|
31
|
+
reportMarkdownPluginFailure,
|
|
32
|
+
} from './plugins/protocol';
|
|
33
|
+
import type {
|
|
34
|
+
MarkdownExtensionNode,
|
|
35
|
+
MarkdownExtensionsOf,
|
|
36
|
+
MarkdownPluginData,
|
|
37
|
+
MarkdownPluginEntry,
|
|
38
|
+
PreparedMarkdownPlugins,
|
|
39
|
+
PreparedSyntaxContribution,
|
|
40
|
+
} from './plugins/protocol';
|
|
41
|
+
import {isSafeMarkdownParserUrl} from './url';
|
|
42
|
+
|
|
13
43
|
// ---------------------------------------------------------------------------
|
|
14
44
|
// Types
|
|
15
45
|
// ---------------------------------------------------------------------------
|
|
16
46
|
|
|
17
47
|
/** Nodes returned by default and legacy parser calls. */
|
|
18
|
-
export type InlineNode =
|
|
48
|
+
export type InlineNode<Extension extends MarkdownExtensionNode = never> =
|
|
19
49
|
| {type: 'text'; content: string}
|
|
20
|
-
| {type: 'bold'; children: InlineNode[]}
|
|
21
|
-
| {type: 'italic'; children: InlineNode[]}
|
|
22
|
-
| {type: 'strikethrough'; children: InlineNode[]}
|
|
50
|
+
| {type: 'bold'; children: InlineNode<Extension>[]}
|
|
51
|
+
| {type: 'italic'; children: InlineNode<Extension>[]}
|
|
52
|
+
| {type: 'strikethrough'; children: InlineNode<Extension>[]}
|
|
23
53
|
| {type: 'code'; content: string}
|
|
24
|
-
| {type: 'link'; href: string; children: InlineNode[]}
|
|
54
|
+
| {type: 'link'; href: string; children: InlineNode<Extension>[]}
|
|
25
55
|
| {type: 'image'; src: string; alt: string}
|
|
26
56
|
| {type: 'citation'; sourceId: string}
|
|
27
|
-
| {type: 'break'}
|
|
57
|
+
| {type: 'break'}
|
|
58
|
+
| Extract<Extension, {display: 'inline'}>;
|
|
28
59
|
|
|
29
60
|
/** The additional inline node returned only when parsing with `math: true`. */
|
|
30
61
|
export type MathInlineNode = {type: 'math'; value: string};
|
|
31
62
|
|
|
32
63
|
/** Nodes returned by an explicitly math-enabled inline parse. */
|
|
33
|
-
export type InlineNodeWithMath
|
|
64
|
+
export type InlineNodeWithMath<
|
|
65
|
+
Extension extends MarkdownExtensionNode = never,
|
|
66
|
+
> =
|
|
34
67
|
| {type: 'text'; content: string}
|
|
35
|
-
| {type: 'bold'; children: InlineNodeWithMath[]}
|
|
36
|
-
| {type: 'italic'; children: InlineNodeWithMath[]}
|
|
37
|
-
| {type: 'strikethrough'; children: InlineNodeWithMath[]}
|
|
68
|
+
| {type: 'bold'; children: InlineNodeWithMath<Extension>[]}
|
|
69
|
+
| {type: 'italic'; children: InlineNodeWithMath<Extension>[]}
|
|
70
|
+
| {type: 'strikethrough'; children: InlineNodeWithMath<Extension>[]}
|
|
38
71
|
| {type: 'code'; content: string}
|
|
39
72
|
| MathInlineNode
|
|
40
|
-
| {type: 'link'; href: string; children: InlineNodeWithMath[]}
|
|
73
|
+
| {type: 'link'; href: string; children: InlineNodeWithMath<Extension>[]}
|
|
41
74
|
| {type: 'image'; src: string; alt: string}
|
|
42
75
|
| {type: 'citation'; sourceId: string}
|
|
43
|
-
| {type: 'break'}
|
|
76
|
+
| {type: 'break'}
|
|
77
|
+
| Extract<Extension, {display: 'inline'}>;
|
|
44
78
|
|
|
45
79
|
type BlockMetadata = {
|
|
46
80
|
/**
|
|
@@ -50,11 +84,15 @@ type BlockMetadata = {
|
|
|
50
84
|
range?: SourceRange;
|
|
51
85
|
};
|
|
52
86
|
|
|
53
|
-
type LegacyBlockNodeKind =
|
|
54
|
-
| {
|
|
55
|
-
|
|
87
|
+
type LegacyBlockNodeKind<Extension extends MarkdownExtensionNode = never> =
|
|
88
|
+
| {
|
|
89
|
+
type: 'heading';
|
|
90
|
+
level: 1 | 2 | 3 | 4 | 5 | 6;
|
|
91
|
+
children: InlineNode<Extension>[];
|
|
92
|
+
}
|
|
93
|
+
| {type: 'paragraph'; children: InlineNode<Extension>[]}
|
|
56
94
|
| {type: 'codeblock'; language: string; content: string}
|
|
57
|
-
| {type: 'blockquote'; children: BlockNode[]}
|
|
95
|
+
| {type: 'blockquote'; children: BlockNode<Extension>[]}
|
|
58
96
|
| {
|
|
59
97
|
type: 'list';
|
|
60
98
|
ordered: boolean;
|
|
@@ -62,33 +100,35 @@ type LegacyBlockNodeKind =
|
|
|
62
100
|
/** Ordered-list marker delimiter ('.' or ')'). Undefined for bullets. */
|
|
63
101
|
delimiter?: '.' | ')';
|
|
64
102
|
loose?: boolean;
|
|
65
|
-
items: ListItemNode[];
|
|
103
|
+
items: ListItemNode<Extension>[];
|
|
66
104
|
}
|
|
67
105
|
| {
|
|
68
106
|
type: 'table';
|
|
69
|
-
headers: TableCellNode[];
|
|
107
|
+
headers: TableCellNode<Extension>[];
|
|
70
108
|
alignments: TableAlignment[];
|
|
71
|
-
rows: TableCellNode[][];
|
|
109
|
+
rows: TableCellNode<Extension>[][];
|
|
72
110
|
}
|
|
73
111
|
| {type: 'hr'}
|
|
74
|
-
| {type: 'image'; src: string; alt: string}
|
|
112
|
+
| {type: 'image'; src: string; alt: string}
|
|
113
|
+
| Extract<Extension, {display: 'block'}>;
|
|
75
114
|
|
|
76
115
|
/** Blocks returned by default and legacy parser calls. */
|
|
77
|
-
export type BlockNode =
|
|
116
|
+
export type BlockNode<Extension extends MarkdownExtensionNode = never> =
|
|
117
|
+
LegacyBlockNodeKind<Extension> & BlockMetadata;
|
|
78
118
|
|
|
79
119
|
/** The additional block returned only when parsing with `math: true`. */
|
|
80
120
|
export type MathBlockNode = {type: 'math'; value: string} & BlockMetadata;
|
|
81
121
|
|
|
82
|
-
type MathEnabledBlockNodeKind =
|
|
122
|
+
type MathEnabledBlockNodeKind<Extension extends MarkdownExtensionNode = never> =
|
|
83
123
|
| {
|
|
84
124
|
type: 'heading';
|
|
85
125
|
level: 1 | 2 | 3 | 4 | 5 | 6;
|
|
86
|
-
children: InlineNodeWithMath[];
|
|
126
|
+
children: InlineNodeWithMath<Extension>[];
|
|
87
127
|
}
|
|
88
|
-
| {type: 'paragraph'; children: InlineNodeWithMath[]}
|
|
128
|
+
| {type: 'paragraph'; children: InlineNodeWithMath<Extension>[]}
|
|
89
129
|
| {type: 'codeblock'; language: string; content: string}
|
|
90
130
|
| MathBlockNode
|
|
91
|
-
| {type: 'blockquote'; children: BlockNodeWithMath[]}
|
|
131
|
+
| {type: 'blockquote'; children: BlockNodeWithMath<Extension>[]}
|
|
92
132
|
| {
|
|
93
133
|
type: 'list';
|
|
94
134
|
ordered: boolean;
|
|
@@ -96,19 +136,21 @@ type MathEnabledBlockNodeKind =
|
|
|
96
136
|
/** Ordered-list marker delimiter ('.' or ')'). Undefined for bullets. */
|
|
97
137
|
delimiter?: '.' | ')';
|
|
98
138
|
loose?: boolean;
|
|
99
|
-
items: ListItemNodeWithMath[];
|
|
139
|
+
items: ListItemNodeWithMath<Extension>[];
|
|
100
140
|
}
|
|
101
141
|
| {
|
|
102
142
|
type: 'table';
|
|
103
|
-
headers: TableCellNodeWithMath[];
|
|
143
|
+
headers: TableCellNodeWithMath<Extension>[];
|
|
104
144
|
alignments: TableAlignment[];
|
|
105
|
-
rows: TableCellNodeWithMath[][];
|
|
145
|
+
rows: TableCellNodeWithMath<Extension>[][];
|
|
106
146
|
}
|
|
107
147
|
| {type: 'hr'}
|
|
108
|
-
| {type: 'image'; src: string; alt: string}
|
|
148
|
+
| {type: 'image'; src: string; alt: string}
|
|
149
|
+
| Extract<Extension, {display: 'block'}>;
|
|
109
150
|
|
|
110
151
|
/** Blocks returned by an explicitly math-enabled block parse. */
|
|
111
|
-
export type BlockNodeWithMath =
|
|
152
|
+
export type BlockNodeWithMath<Extension extends MarkdownExtensionNode = never> =
|
|
153
|
+
MathEnabledBlockNodeKind<Extension> & BlockMetadata;
|
|
112
154
|
|
|
113
155
|
/**
|
|
114
156
|
* Where a block sits in the source string handed to `parseMarkdown`:
|
|
@@ -121,15 +163,288 @@ export type BlockNodeWithMath = MathEnabledBlockNodeKind & BlockMetadata;
|
|
|
121
163
|
*/
|
|
122
164
|
export type SourceRange = {readonly start: number; readonly end: number};
|
|
123
165
|
|
|
124
|
-
export type ListItemNode =
|
|
125
|
-
|
|
166
|
+
export type ListItemNode<Extension extends MarkdownExtensionNode = never> = {
|
|
167
|
+
checked?: boolean;
|
|
168
|
+
children: BlockNode<Extension>[];
|
|
169
|
+
};
|
|
170
|
+
type ListItemNodeWithMath<Extension extends MarkdownExtensionNode = never> = {
|
|
126
171
|
checked?: boolean;
|
|
127
|
-
children: BlockNodeWithMath[];
|
|
172
|
+
children: BlockNodeWithMath<Extension>[];
|
|
173
|
+
};
|
|
174
|
+
export type TableCellNode<Extension extends MarkdownExtensionNode = never> = {
|
|
175
|
+
children: InlineNode<Extension>[];
|
|
176
|
+
};
|
|
177
|
+
type TableCellNodeWithMath<Extension extends MarkdownExtensionNode = never> = {
|
|
178
|
+
children: InlineNodeWithMath<Extension>[];
|
|
128
179
|
};
|
|
129
|
-
export type TableCellNode = {children: InlineNode[]};
|
|
130
|
-
type TableCellNodeWithMath = {children: InlineNodeWithMath[]};
|
|
131
180
|
export type TableAlignment = 'left' | 'center' | 'right' | null;
|
|
132
181
|
|
|
182
|
+
type RuntimeExtensionNode =
|
|
183
|
+
| MarkdownExtensionNode<string, string, MarkdownPluginData, 'inline'>
|
|
184
|
+
| MarkdownExtensionNode<string, string, MarkdownPluginData, 'block'>;
|
|
185
|
+
type RuntimeInlineNode = InlineNodeWithMath<RuntimeExtensionNode>;
|
|
186
|
+
type RuntimeBlockNode = BlockNodeWithMath<RuntimeExtensionNode>;
|
|
187
|
+
|
|
188
|
+
type LegacyProjectionCache = WeakMap<object, object>;
|
|
189
|
+
|
|
190
|
+
function projectRange(
|
|
191
|
+
position: MarkdownAstPosition | undefined,
|
|
192
|
+
): BlockMetadata {
|
|
193
|
+
const start = position?.start.offset;
|
|
194
|
+
const end = position?.end.offset;
|
|
195
|
+
return start == null || end == null ? {} : {range: {start, end}};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Canonical nodes always carry Core-authored positions; the released block
|
|
200
|
+
* shape exposes them as `range` only when the caller passed `sourceRanges`,
|
|
201
|
+
* so this projection stays byte-for-byte what it has always returned.
|
|
202
|
+
*
|
|
203
|
+
* An extension node is the one kind the projection passes through instead of
|
|
204
|
+
* rebuilding field by field, so it is the one kind that could carry a
|
|
205
|
+
* canonical `position` out into released output. Strip it unless the caller
|
|
206
|
+
* asked for provenance, in which case the node keeps exactly the `position`
|
|
207
|
+
* it has always exposed.
|
|
208
|
+
*/
|
|
209
|
+
function projectExtensionNode<Node extends RuntimeExtensionNode>(
|
|
210
|
+
node: Node,
|
|
211
|
+
withRanges: boolean,
|
|
212
|
+
): Node {
|
|
213
|
+
if (withRanges || node.position === undefined) {
|
|
214
|
+
return node;
|
|
215
|
+
}
|
|
216
|
+
const {position: _canonicalPosition, ...released} = node;
|
|
217
|
+
return released as Node;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function projectInlineNode(
|
|
221
|
+
node: MarkdownAstPhrasingContent<RuntimeExtensionNode>,
|
|
222
|
+
cache: LegacyProjectionCache,
|
|
223
|
+
withRanges: boolean,
|
|
224
|
+
): RuntimeInlineNode {
|
|
225
|
+
const cached = cache.get(node);
|
|
226
|
+
if (cached != null) {
|
|
227
|
+
return cached as RuntimeInlineNode;
|
|
228
|
+
}
|
|
229
|
+
let projected: RuntimeInlineNode;
|
|
230
|
+
switch (node.type) {
|
|
231
|
+
case 'text':
|
|
232
|
+
projected = {type: 'text', content: node.value};
|
|
233
|
+
break;
|
|
234
|
+
case 'strong':
|
|
235
|
+
projected = {
|
|
236
|
+
type: 'bold',
|
|
237
|
+
children: node.children.map(child =>
|
|
238
|
+
projectInlineNode(child, cache, withRanges),
|
|
239
|
+
),
|
|
240
|
+
};
|
|
241
|
+
break;
|
|
242
|
+
case 'emphasis':
|
|
243
|
+
projected = {
|
|
244
|
+
type: 'italic',
|
|
245
|
+
children: node.children.map(child =>
|
|
246
|
+
projectInlineNode(child, cache, withRanges),
|
|
247
|
+
),
|
|
248
|
+
};
|
|
249
|
+
break;
|
|
250
|
+
case 'delete':
|
|
251
|
+
projected = {
|
|
252
|
+
type: 'strikethrough',
|
|
253
|
+
children: node.children.map(child =>
|
|
254
|
+
projectInlineNode(child, cache, withRanges),
|
|
255
|
+
),
|
|
256
|
+
};
|
|
257
|
+
break;
|
|
258
|
+
case 'inlineCode':
|
|
259
|
+
projected = {type: 'code', content: node.value};
|
|
260
|
+
break;
|
|
261
|
+
case 'inlineMath':
|
|
262
|
+
projected = {type: 'math', value: node.value};
|
|
263
|
+
break;
|
|
264
|
+
case 'link':
|
|
265
|
+
projected = {
|
|
266
|
+
type: 'link',
|
|
267
|
+
href: node.url,
|
|
268
|
+
children: node.children.map(child =>
|
|
269
|
+
projectInlineNode(child, cache, withRanges),
|
|
270
|
+
),
|
|
271
|
+
};
|
|
272
|
+
break;
|
|
273
|
+
case 'image':
|
|
274
|
+
projected = {type: 'image', src: node.url, alt: node.alt};
|
|
275
|
+
break;
|
|
276
|
+
case 'citation':
|
|
277
|
+
projected = {type: 'citation', sourceId: node.sourceId};
|
|
278
|
+
break;
|
|
279
|
+
case 'break':
|
|
280
|
+
projected = {type: 'break'};
|
|
281
|
+
break;
|
|
282
|
+
case 'extension':
|
|
283
|
+
projected = projectExtensionNode(node, withRanges);
|
|
284
|
+
break;
|
|
285
|
+
}
|
|
286
|
+
cache.set(node, projected);
|
|
287
|
+
return projected;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function projectTableCell(
|
|
291
|
+
node: MarkdownAstTableCell<RuntimeExtensionNode>,
|
|
292
|
+
cache: LegacyProjectionCache,
|
|
293
|
+
withRanges: boolean,
|
|
294
|
+
): TableCellNodeWithMath<RuntimeExtensionNode> {
|
|
295
|
+
const cached = cache.get(node);
|
|
296
|
+
if (cached != null) {
|
|
297
|
+
return cached as TableCellNodeWithMath<RuntimeExtensionNode>;
|
|
298
|
+
}
|
|
299
|
+
const projected = {
|
|
300
|
+
children: node.children.map(child =>
|
|
301
|
+
projectInlineNode(child, cache, withRanges),
|
|
302
|
+
),
|
|
303
|
+
};
|
|
304
|
+
cache.set(node, projected);
|
|
305
|
+
return projected;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function projectListItem(
|
|
309
|
+
node: MarkdownAstListItem<RuntimeExtensionNode>,
|
|
310
|
+
cache: LegacyProjectionCache,
|
|
311
|
+
withRanges: boolean,
|
|
312
|
+
): ListItemNodeWithMath<RuntimeExtensionNode> {
|
|
313
|
+
const cached = cache.get(node);
|
|
314
|
+
if (cached != null) {
|
|
315
|
+
return cached as ListItemNodeWithMath<RuntimeExtensionNode>;
|
|
316
|
+
}
|
|
317
|
+
const projected = {
|
|
318
|
+
checked: node.checked,
|
|
319
|
+
children: node.children.map(child =>
|
|
320
|
+
projectBlockNode(child, cache, withRanges),
|
|
321
|
+
),
|
|
322
|
+
};
|
|
323
|
+
cache.set(node, projected);
|
|
324
|
+
return projected;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function projectBlockNode(
|
|
328
|
+
node: MarkdownAstBlockContent<RuntimeExtensionNode>,
|
|
329
|
+
cache: LegacyProjectionCache,
|
|
330
|
+
withRanges: boolean,
|
|
331
|
+
): RuntimeBlockNode {
|
|
332
|
+
const cached = cache.get(node);
|
|
333
|
+
if (cached != null) {
|
|
334
|
+
return cached as RuntimeBlockNode;
|
|
335
|
+
}
|
|
336
|
+
const metadata = withRanges ? projectRange(node.position) : {};
|
|
337
|
+
let projected: RuntimeBlockNode;
|
|
338
|
+
switch (node.type) {
|
|
339
|
+
case 'heading':
|
|
340
|
+
projected = {
|
|
341
|
+
type: 'heading',
|
|
342
|
+
level: node.depth,
|
|
343
|
+
children: node.children.map(child =>
|
|
344
|
+
projectInlineNode(child, cache, withRanges),
|
|
345
|
+
),
|
|
346
|
+
...metadata,
|
|
347
|
+
};
|
|
348
|
+
break;
|
|
349
|
+
case 'paragraph':
|
|
350
|
+
projected = {
|
|
351
|
+
type: 'paragraph',
|
|
352
|
+
children: node.children.map(child =>
|
|
353
|
+
projectInlineNode(child, cache, withRanges),
|
|
354
|
+
),
|
|
355
|
+
...metadata,
|
|
356
|
+
};
|
|
357
|
+
break;
|
|
358
|
+
case 'code':
|
|
359
|
+
projected = {
|
|
360
|
+
type: 'codeblock',
|
|
361
|
+
language: getMarkdownAstLegacyCodeLanguage(node) ?? 'plaintext',
|
|
362
|
+
content: node.value,
|
|
363
|
+
...metadata,
|
|
364
|
+
};
|
|
365
|
+
break;
|
|
366
|
+
case 'math':
|
|
367
|
+
projected = {type: 'math', value: node.value, ...metadata};
|
|
368
|
+
break;
|
|
369
|
+
case 'blockquote':
|
|
370
|
+
projected = {
|
|
371
|
+
type: 'blockquote',
|
|
372
|
+
children: node.children.map(child =>
|
|
373
|
+
projectBlockNode(child, cache, withRanges),
|
|
374
|
+
),
|
|
375
|
+
...metadata,
|
|
376
|
+
};
|
|
377
|
+
break;
|
|
378
|
+
case 'list':
|
|
379
|
+
projected = {
|
|
380
|
+
type: 'list',
|
|
381
|
+
ordered: node.ordered,
|
|
382
|
+
start: node.start,
|
|
383
|
+
delimiter: node.delimiter,
|
|
384
|
+
loose: node.spread,
|
|
385
|
+
items: node.children.map(item =>
|
|
386
|
+
projectListItem(item, cache, withRanges),
|
|
387
|
+
),
|
|
388
|
+
...metadata,
|
|
389
|
+
};
|
|
390
|
+
break;
|
|
391
|
+
case 'table': {
|
|
392
|
+
const [header = {type: 'tableRow' as const, children: []}, ...rows] =
|
|
393
|
+
node.children;
|
|
394
|
+
projected = {
|
|
395
|
+
type: 'table',
|
|
396
|
+
headers: header.children.map(cell =>
|
|
397
|
+
projectTableCell(cell, cache, withRanges),
|
|
398
|
+
),
|
|
399
|
+
alignments: [...node.align],
|
|
400
|
+
rows: rows.map(row =>
|
|
401
|
+
row.children.map(cell => projectTableCell(cell, cache, withRanges)),
|
|
402
|
+
),
|
|
403
|
+
...metadata,
|
|
404
|
+
};
|
|
405
|
+
break;
|
|
406
|
+
}
|
|
407
|
+
case 'thematicBreak':
|
|
408
|
+
projected = {type: 'hr', ...metadata};
|
|
409
|
+
break;
|
|
410
|
+
case 'image':
|
|
411
|
+
projected = {type: 'image', alt: node.alt, src: node.url, ...metadata};
|
|
412
|
+
break;
|
|
413
|
+
case 'extension':
|
|
414
|
+
projected = projectExtensionNode(node, withRanges);
|
|
415
|
+
break;
|
|
416
|
+
}
|
|
417
|
+
cache.set(node, projected);
|
|
418
|
+
return projected;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
function projectInlineNodes(
|
|
422
|
+
nodes: ReadonlyArray<MarkdownAstPhrasingContent<RuntimeExtensionNode>>,
|
|
423
|
+
withRanges: boolean,
|
|
424
|
+
cache: LegacyProjectionCache = new WeakMap(),
|
|
425
|
+
): RuntimeInlineNode[] {
|
|
426
|
+
return nodes.map(node => projectInlineNode(node, cache, withRanges));
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
function projectMarkdownRoot(
|
|
430
|
+
root: MarkdownAstRoot<RuntimeExtensionNode>,
|
|
431
|
+
withRanges: boolean,
|
|
432
|
+
cache: LegacyProjectionCache = new WeakMap(),
|
|
433
|
+
): RuntimeBlockNode[] {
|
|
434
|
+
return root.children.map(node => projectBlockNode(node, cache, withRanges));
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/** Whether a caller asked for the released `range` field on its blocks. */
|
|
438
|
+
function wantsLegacyRanges(
|
|
439
|
+
arg: ReadonlySet<string> | RuntimeParseOptions | undefined,
|
|
440
|
+
): boolean {
|
|
441
|
+
return (
|
|
442
|
+
arg != null &&
|
|
443
|
+
typeof (arg as {has?: unknown}).has !== 'function' &&
|
|
444
|
+
(arg as RuntimeParseOptions).sourceRanges === true
|
|
445
|
+
);
|
|
446
|
+
}
|
|
447
|
+
|
|
133
448
|
// ---------------------------------------------------------------------------
|
|
134
449
|
// Parse options
|
|
135
450
|
// ---------------------------------------------------------------------------
|
|
@@ -141,10 +456,14 @@ export type TableAlignment = 'left' | 'center' | 'right' | null;
|
|
|
141
456
|
* `parseMarkdownIncremental` functions also accept the legacy
|
|
142
457
|
* `ReadonlySet<string>` shape as the second argument.
|
|
143
458
|
*/
|
|
144
|
-
type CommonParseOptions
|
|
459
|
+
type CommonParseOptions<
|
|
460
|
+
Plugins extends ReadonlyArray<MarkdownPluginEntry> = readonly [],
|
|
461
|
+
> = {
|
|
145
462
|
/** Set of citation source ids — `[id]` / `【id】` markers in this set
|
|
146
463
|
* become citation nodes instead of plain text / links. */
|
|
147
464
|
sourceIds?: ReadonlySet<string>;
|
|
465
|
+
/** Ordered opt-in Markdown extensions. */
|
|
466
|
+
plugins?: Plugins;
|
|
148
467
|
/**
|
|
149
468
|
* Autolink mode. When set to `'gfm'`, the parser turns bare
|
|
150
469
|
* `https?://…` / `www.…` URLs, `<URL>` / `<email>` angle-bracket
|
|
@@ -170,22 +489,56 @@ type CommonParseOptions = {
|
|
|
170
489
|
};
|
|
171
490
|
|
|
172
491
|
/** Options for default and legacy parser results. */
|
|
173
|
-
export type ParseOptions
|
|
492
|
+
export type ParseOptions<
|
|
493
|
+
Plugins extends ReadonlyArray<MarkdownPluginEntry> = readonly [],
|
|
494
|
+
> = CommonParseOptions<Plugins> & {math?: false | undefined};
|
|
174
495
|
|
|
175
496
|
/**
|
|
176
497
|
* Options that explicitly parse `$…$` and `$$…$$` into math-enabled result
|
|
177
498
|
* unions. Keeping this separate prevents a legacy `ParseOptions` annotation
|
|
178
499
|
* from silently widening an exhaustive node switch.
|
|
179
500
|
*/
|
|
180
|
-
export type MathParseOptions
|
|
501
|
+
export type MathParseOptions<
|
|
502
|
+
Plugins extends ReadonlyArray<MarkdownPluginEntry> = readonly [],
|
|
503
|
+
> = CommonParseOptions<Plugins> & {math: true};
|
|
504
|
+
|
|
505
|
+
export type IncrementalParseOptions<
|
|
506
|
+
Plugins extends ReadonlyArray<MarkdownPluginEntry> = readonly [],
|
|
507
|
+
> = ParseOptions<Plugins> & {
|
|
508
|
+
/** False while more source may arrive; true for the terminal snapshot. */
|
|
509
|
+
readonly isFinal?: boolean;
|
|
510
|
+
};
|
|
181
511
|
|
|
182
|
-
type
|
|
512
|
+
export type IncrementalMathParseOptions<
|
|
513
|
+
Plugins extends ReadonlyArray<MarkdownPluginEntry> = readonly [],
|
|
514
|
+
> = MathParseOptions<Plugins> & {
|
|
515
|
+
/** False while more source may arrive; true for the terminal snapshot. */
|
|
516
|
+
readonly isFinal?: boolean;
|
|
517
|
+
};
|
|
518
|
+
|
|
519
|
+
type RuntimeParseOptions = CommonParseOptions<
|
|
520
|
+
ReadonlyArray<MarkdownPluginEntry>
|
|
521
|
+
> & {
|
|
522
|
+
math?: boolean;
|
|
523
|
+
isFinal?: boolean;
|
|
524
|
+
};
|
|
183
525
|
|
|
184
526
|
type ResolvedOptions = {
|
|
185
527
|
readonly sourceIds: ReadonlySet<string> | undefined;
|
|
186
528
|
readonly autolink: 'gfm' | undefined;
|
|
187
529
|
readonly math?: boolean;
|
|
530
|
+
/** Whether the caller asked for the released `range` projection. */
|
|
188
531
|
readonly sourceRanges?: boolean;
|
|
532
|
+
/**
|
|
533
|
+
* Whether this parse authors canonical `position` offsets on its blocks.
|
|
534
|
+
* Internal only, and independent of `sourceRanges`: the canonical tree
|
|
535
|
+
* transforms observe always carries provenance, while `sourceRanges` alone
|
|
536
|
+
* decides whether the released projection exposes it as `range`.
|
|
537
|
+
*/
|
|
538
|
+
readonly astPositions?: boolean;
|
|
539
|
+
readonly plugins?: PreparedMarkdownPlugins;
|
|
540
|
+
readonly isFinal: boolean;
|
|
541
|
+
readonly allowBlockSyntax?: boolean;
|
|
189
542
|
/**
|
|
190
543
|
* Offset of this parse's input within the document the ranges are reported
|
|
191
544
|
* against. Internal only — the incremental parser parses slices and needs
|
|
@@ -201,13 +554,42 @@ type ResolvedOptions = {
|
|
|
201
554
|
readonly linkDefs?: ReadonlyMap<string, string>;
|
|
202
555
|
};
|
|
203
556
|
|
|
204
|
-
|
|
557
|
+
/**
|
|
558
|
+
* Every resolved options object is built here, so all of them share one key
|
|
559
|
+
* set in one order — and therefore one hidden class. The parser reads these
|
|
560
|
+
* fields in its hottest loops; letting an omitted-options parse and an
|
|
561
|
+
* options-bag parse produce differently-shaped objects makes those reads
|
|
562
|
+
* polymorphic in any process that does both, which is exactly what a caller
|
|
563
|
+
* comparing an empty pipeline against a configured one does.
|
|
564
|
+
*/
|
|
565
|
+
function makeResolvedOptions(
|
|
566
|
+
fields: Partial<ResolvedOptions> & {readonly isFinal: boolean},
|
|
567
|
+
): ResolvedOptions {
|
|
568
|
+
return {
|
|
569
|
+
sourceIds: fields.sourceIds,
|
|
570
|
+
autolink: fields.autolink,
|
|
571
|
+
math: fields.math,
|
|
572
|
+
sourceRanges: fields.sourceRanges,
|
|
573
|
+
astPositions: fields.astPositions ?? true,
|
|
574
|
+
plugins: fields.plugins,
|
|
575
|
+
isFinal: fields.isFinal,
|
|
576
|
+
allowBlockSyntax: fields.allowBlockSyntax ?? true,
|
|
577
|
+
baseOffset: fields.baseOffset,
|
|
578
|
+
linkDefs: fields.linkDefs,
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
const EMPTY_OPTS: ResolvedOptions = makeResolvedOptions({isFinal: true});
|
|
583
|
+
const EMPTY_INCREMENTAL_OPTS: ResolvedOptions = makeResolvedOptions({
|
|
584
|
+
isFinal: false,
|
|
585
|
+
});
|
|
205
586
|
|
|
206
587
|
function resolveOptions(
|
|
207
588
|
arg: ReadonlySet<string> | RuntimeParseOptions | undefined,
|
|
589
|
+
incremental = false,
|
|
208
590
|
): ResolvedOptions {
|
|
209
591
|
if (arg == null) {
|
|
210
|
-
return EMPTY_OPTS;
|
|
592
|
+
return incremental ? EMPTY_INCREMENTAL_OPTS : EMPTY_OPTS;
|
|
211
593
|
}
|
|
212
594
|
// Duck-type the legacy `ReadonlySet<string>` form: any object whose
|
|
213
595
|
// `.has` is callable is treated as the legacy sourceIds set. This is
|
|
@@ -215,15 +597,23 @@ function resolveOptions(
|
|
|
215
597
|
// or polyfilled `ReadonlySet` implementations as a `ParseOptions` bag
|
|
216
598
|
// and silently lose citation resolution.
|
|
217
599
|
if (typeof (arg as {has?: unknown}).has === 'function') {
|
|
218
|
-
return {
|
|
600
|
+
return makeResolvedOptions({
|
|
601
|
+
sourceIds: arg as ReadonlySet<string>,
|
|
602
|
+
isFinal: !incremental,
|
|
603
|
+
});
|
|
219
604
|
}
|
|
220
605
|
const opts = arg as RuntimeParseOptions;
|
|
221
|
-
return {
|
|
606
|
+
return makeResolvedOptions({
|
|
222
607
|
sourceIds: opts.sourceIds,
|
|
223
608
|
autolink: opts.autolink,
|
|
224
609
|
math: opts.math === true ? true : undefined,
|
|
225
610
|
sourceRanges: opts.sourceRanges,
|
|
226
|
-
|
|
611
|
+
plugins:
|
|
612
|
+
opts.plugins != null && opts.plugins.length > 0
|
|
613
|
+
? prepareMarkdownPlugins(opts.plugins)
|
|
614
|
+
: undefined,
|
|
615
|
+
isFinal: incremental ? opts.isFinal === true : true,
|
|
616
|
+
});
|
|
227
617
|
}
|
|
228
618
|
|
|
229
619
|
// ---------------------------------------------------------------------------
|
|
@@ -520,6 +910,15 @@ function extractLinkDefinitions(
|
|
|
520
910
|
return {defs, cleaned, lineMap};
|
|
521
911
|
}
|
|
522
912
|
|
|
913
|
+
/** Order-independent signature of a citation-source set, for cache checks. */
|
|
914
|
+
function sourceIdsSignature(
|
|
915
|
+
sourceIds: ReadonlySet<string> | undefined,
|
|
916
|
+
): string {
|
|
917
|
+
return sourceIds == null || sourceIds.size === 0
|
|
918
|
+
? ''
|
|
919
|
+
: [...sourceIds].sort().join('\u0000');
|
|
920
|
+
}
|
|
921
|
+
|
|
523
922
|
/** Order-independent signature of a link-definition set, for cache checks. */
|
|
524
923
|
function linkDefsSignature(defs: ReadonlyMap<string, string>): string {
|
|
525
924
|
if (defs.size === 0) {
|
|
@@ -537,12 +936,20 @@ function linkDefsSignature(defs: ReadonlyMap<string, string>): string {
|
|
|
537
936
|
* Returns the node plus the index just past the reference, or null when it is
|
|
538
937
|
* not a resolvable reference (caller falls through to literal handling).
|
|
539
938
|
*/
|
|
939
|
+
function protectedInlineOptions(opts: ResolvedOptions): ResolvedOptions {
|
|
940
|
+
return opts.plugins == null ? opts : {...opts, plugins: undefined};
|
|
941
|
+
}
|
|
942
|
+
|
|
540
943
|
function matchReferenceLink(
|
|
541
944
|
text: string,
|
|
542
945
|
start: number,
|
|
543
946
|
linkDefs: ReadonlyMap<string, string>,
|
|
544
947
|
opts: ResolvedOptions,
|
|
545
|
-
|
|
948
|
+
context: 'default' | 'tableCell',
|
|
949
|
+
): {
|
|
950
|
+
node: MarkdownAstPhrasingContent<RuntimeExtensionNode>;
|
|
951
|
+
end: number;
|
|
952
|
+
} | null {
|
|
546
953
|
const textClose = text.indexOf(']', start + 1);
|
|
547
954
|
if (textClose === -1) {
|
|
548
955
|
return null;
|
|
@@ -558,9 +965,17 @@ function matchReferenceLink(
|
|
|
558
965
|
// matches nothing.
|
|
559
966
|
const label = rawLabel === '' ? linkText : rawLabel;
|
|
560
967
|
const href = linkDefs.get(normalizeLinkLabel(label));
|
|
561
|
-
if (href != null &&
|
|
968
|
+
if (href != null && isSafeMarkdownParserUrl(href)) {
|
|
562
969
|
return {
|
|
563
|
-
node: {
|
|
970
|
+
node: {
|
|
971
|
+
type: 'link',
|
|
972
|
+
url: href,
|
|
973
|
+
children: parseInlineImpl(
|
|
974
|
+
linkText,
|
|
975
|
+
protectedInlineOptions(opts),
|
|
976
|
+
context,
|
|
977
|
+
),
|
|
978
|
+
},
|
|
564
979
|
end: labelClose + 1,
|
|
565
980
|
};
|
|
566
981
|
}
|
|
@@ -573,11 +988,19 @@ function matchReferenceLink(
|
|
|
573
988
|
return null;
|
|
574
989
|
}
|
|
575
990
|
const href = linkDefs.get(normalizeLinkLabel(linkText));
|
|
576
|
-
if (href == null || !
|
|
991
|
+
if (href == null || !isSafeMarkdownParserUrl(href)) {
|
|
577
992
|
return null;
|
|
578
993
|
}
|
|
579
994
|
return {
|
|
580
|
-
node: {
|
|
995
|
+
node: {
|
|
996
|
+
type: 'link',
|
|
997
|
+
url: href,
|
|
998
|
+
children: parseInlineImpl(
|
|
999
|
+
linkText,
|
|
1000
|
+
protectedInlineOptions(opts),
|
|
1001
|
+
context,
|
|
1002
|
+
),
|
|
1003
|
+
},
|
|
581
1004
|
end: textClose + 1,
|
|
582
1005
|
};
|
|
583
1006
|
}
|
|
@@ -587,7 +1010,10 @@ function matchReferenceImage(
|
|
|
587
1010
|
text: string,
|
|
588
1011
|
start: number,
|
|
589
1012
|
linkDefs: ReadonlyMap<string, string>,
|
|
590
|
-
): {
|
|
1013
|
+
): {
|
|
1014
|
+
node: MarkdownAstPhrasingContent<RuntimeExtensionNode>;
|
|
1015
|
+
end: number;
|
|
1016
|
+
} | null {
|
|
591
1017
|
const altClose = text.indexOf(']', start + 2);
|
|
592
1018
|
if (altClose === -1) {
|
|
593
1019
|
return null;
|
|
@@ -599,8 +1025,8 @@ function matchReferenceImage(
|
|
|
599
1025
|
const rawLabel = text.slice(altClose + 2, labelClose);
|
|
600
1026
|
const label = rawLabel === '' ? alt : rawLabel;
|
|
601
1027
|
const src = linkDefs.get(normalizeLinkLabel(label));
|
|
602
|
-
if (src != null &&
|
|
603
|
-
return {node: {type: 'image', src, alt}, end: labelClose + 1};
|
|
1028
|
+
if (src != null && isSafeMarkdownParserUrl(src)) {
|
|
1029
|
+
return {node: {type: 'image', url: src, alt}, end: labelClose + 1};
|
|
604
1030
|
}
|
|
605
1031
|
// No match — fall back to a shortcut `![alt]`.
|
|
606
1032
|
}
|
|
@@ -609,10 +1035,10 @@ function matchReferenceImage(
|
|
|
609
1035
|
return null;
|
|
610
1036
|
}
|
|
611
1037
|
const src = linkDefs.get(normalizeLinkLabel(alt));
|
|
612
|
-
if (src == null || !
|
|
1038
|
+
if (src == null || !isSafeMarkdownParserUrl(src)) {
|
|
613
1039
|
return null;
|
|
614
1040
|
}
|
|
615
|
-
return {node: {type: 'image', src, alt}, end: altClose + 1};
|
|
1041
|
+
return {node: {type: 'image', url: src, alt}, end: altClose + 1};
|
|
616
1042
|
}
|
|
617
1043
|
|
|
618
1044
|
// ---------------------------------------------------------------------------
|
|
@@ -701,30 +1127,6 @@ function findInlineMathEnd(text: string, start: number): number {
|
|
|
701
1127
|
}
|
|
702
1128
|
|
|
703
1129
|
// ---------------------------------------------------------------------------
|
|
704
|
-
// URL scheme sanitization
|
|
705
|
-
// ---------------------------------------------------------------------------
|
|
706
|
-
|
|
707
|
-
/**
|
|
708
|
-
* Reject URLs with dangerous schemes (javascript:, vbscript:, data:) that
|
|
709
|
-
* could execute arbitrary code when rendered as link hrefs or image srcs.
|
|
710
|
-
* Returns true if the URL is safe to use, false otherwise.
|
|
711
|
-
*/
|
|
712
|
-
function isSafeUrl(url: string): boolean {
|
|
713
|
-
// Trim and collapse whitespace/control chars that browsers tolerate but
|
|
714
|
-
// could bypass a naive prefix check (e.g. "java\nscript:alert(1)").
|
|
715
|
-
// eslint-disable-next-line no-control-regex -- control chars are the bypass
|
|
716
|
-
const normalized = url.replace(/[\x00-\x1f\x7f]/g, '').trim();
|
|
717
|
-
const lower = normalized.toLowerCase();
|
|
718
|
-
if (
|
|
719
|
-
lower.startsWith('javascript:') ||
|
|
720
|
-
lower.startsWith('vbscript:') ||
|
|
721
|
-
lower.startsWith('data:text/html')
|
|
722
|
-
) {
|
|
723
|
-
return false;
|
|
724
|
-
}
|
|
725
|
-
return true;
|
|
726
|
-
}
|
|
727
|
-
|
|
728
1130
|
// ---------------------------------------------------------------------------
|
|
729
1131
|
// Inline parser
|
|
730
1132
|
// ---------------------------------------------------------------------------
|
|
@@ -778,20 +1180,246 @@ function matchBracketCitation(
|
|
|
778
1180
|
return {sourceId: id, end: closeIndex + 1};
|
|
779
1181
|
}
|
|
780
1182
|
|
|
1183
|
+
type ExtensionMatch =
|
|
1184
|
+
| {readonly status: 'none'}
|
|
1185
|
+
| {readonly status: 'defer'}
|
|
1186
|
+
| {
|
|
1187
|
+
readonly status: 'match';
|
|
1188
|
+
readonly node: RuntimeExtensionNode;
|
|
1189
|
+
readonly end: number;
|
|
1190
|
+
};
|
|
1191
|
+
|
|
1192
|
+
function currentLineStart(source: string, offset: number): number {
|
|
1193
|
+
return source.lastIndexOf('\n', offset - 1) + 1;
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
function matchExtensionSyntax(
|
|
1197
|
+
source: string,
|
|
1198
|
+
offset: number,
|
|
1199
|
+
context: 'inline' | 'block',
|
|
1200
|
+
opts: ResolvedOptions,
|
|
1201
|
+
): ExtensionMatch {
|
|
1202
|
+
const byFirstCharacter =
|
|
1203
|
+
context === 'inline'
|
|
1204
|
+
? opts.plugins?.inlineByFirstCharacter
|
|
1205
|
+
: opts.allowBlockSyntax === false
|
|
1206
|
+
? undefined
|
|
1207
|
+
: opts.plugins?.blockByFirstCharacter;
|
|
1208
|
+
const candidates = byFirstCharacter?.get(source[offset]);
|
|
1209
|
+
if (candidates == null) {
|
|
1210
|
+
return {status: 'none'};
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
const seen = new Set<PreparedSyntaxContribution>();
|
|
1214
|
+
for (const candidate of candidates) {
|
|
1215
|
+
if (seen.has(candidate)) {
|
|
1216
|
+
continue;
|
|
1217
|
+
}
|
|
1218
|
+
seen.add(candidate);
|
|
1219
|
+
let fullPrefix = false;
|
|
1220
|
+
for (const prefix of candidate.contribution.startsWith) {
|
|
1221
|
+
if (source.startsWith(prefix, offset)) {
|
|
1222
|
+
fullPrefix = true;
|
|
1223
|
+
break;
|
|
1224
|
+
}
|
|
1225
|
+
if (!opts.isFinal) {
|
|
1226
|
+
const remaining = source.slice(offset);
|
|
1227
|
+
if (remaining.length < prefix.length && prefix.startsWith(remaining)) {
|
|
1228
|
+
return {status: 'defer'};
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
if (!fullPrefix) {
|
|
1233
|
+
continue;
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
const end = Math.min(
|
|
1237
|
+
source.length,
|
|
1238
|
+
offset + candidate.contribution.maxSpan,
|
|
1239
|
+
);
|
|
1240
|
+
let result: ReturnType<typeof candidate.contribution.tokenize>;
|
|
1241
|
+
try {
|
|
1242
|
+
result = candidate.contribution.tokenize({
|
|
1243
|
+
source: end === source.length ? source : source.slice(0, end),
|
|
1244
|
+
offset,
|
|
1245
|
+
end,
|
|
1246
|
+
isFinal: opts.isFinal,
|
|
1247
|
+
context,
|
|
1248
|
+
lineStart: currentLineStart(source, offset),
|
|
1249
|
+
column: offset - currentLineStart(source, offset),
|
|
1250
|
+
});
|
|
1251
|
+
} catch (error) {
|
|
1252
|
+
reportMarkdownPluginFailure(candidate.pluginName, 'syntax', error);
|
|
1253
|
+
continue;
|
|
1254
|
+
}
|
|
1255
|
+
if (
|
|
1256
|
+
result == null ||
|
|
1257
|
+
typeof result !== 'object' ||
|
|
1258
|
+
typeof (result as {then?: unknown}).then === 'function'
|
|
1259
|
+
) {
|
|
1260
|
+
reportMarkdownPluginFailure(
|
|
1261
|
+
candidate.pluginName,
|
|
1262
|
+
'syntax',
|
|
1263
|
+
new TypeError('Tokenizer returned an invalid result'),
|
|
1264
|
+
);
|
|
1265
|
+
continue;
|
|
1266
|
+
}
|
|
1267
|
+
if (result.status === 'no-match') {
|
|
1268
|
+
continue;
|
|
1269
|
+
}
|
|
1270
|
+
if (result.status === 'defer') {
|
|
1271
|
+
return !opts.isFinal &&
|
|
1272
|
+
end === source.length &&
|
|
1273
|
+
source.length - offset < candidate.contribution.maxSpan
|
|
1274
|
+
? {status: 'defer'}
|
|
1275
|
+
: {status: 'none'};
|
|
1276
|
+
}
|
|
1277
|
+
if (
|
|
1278
|
+
result.status !== 'match' ||
|
|
1279
|
+
!Number.isInteger(result.end) ||
|
|
1280
|
+
result.end <= offset ||
|
|
1281
|
+
result.end > end ||
|
|
1282
|
+
result.node.type !== 'extension' ||
|
|
1283
|
+
result.node.plugin !== candidate.pluginName ||
|
|
1284
|
+
result.node.display !== context ||
|
|
1285
|
+
result.node.name.trim() === '' ||
|
|
1286
|
+
!isMarkdownPluginData(result.node.data) ||
|
|
1287
|
+
!opts.plugins?.renderers.has(
|
|
1288
|
+
`${candidate.pluginName}\0${result.node.name}`,
|
|
1289
|
+
)
|
|
1290
|
+
) {
|
|
1291
|
+
reportMarkdownPluginFailure(
|
|
1292
|
+
candidate.pluginName,
|
|
1293
|
+
'syntax',
|
|
1294
|
+
new TypeError('Tokenizer returned an invalid extension node'),
|
|
1295
|
+
);
|
|
1296
|
+
continue;
|
|
1297
|
+
}
|
|
1298
|
+
const rawNode = result.node as typeof result.node & {
|
|
1299
|
+
readonly source?: unknown;
|
|
1300
|
+
readonly position?: unknown;
|
|
1301
|
+
};
|
|
1302
|
+
const {
|
|
1303
|
+
source: _ignoredSource,
|
|
1304
|
+
position: _ignoredPosition,
|
|
1305
|
+
...safeNode
|
|
1306
|
+
} = rawNode;
|
|
1307
|
+
const absoluteStart = (opts.baseOffset ?? 0) + offset;
|
|
1308
|
+
const absoluteEnd = (opts.baseOffset ?? 0) + result.end;
|
|
1309
|
+
return {
|
|
1310
|
+
status: 'match',
|
|
1311
|
+
end: result.end,
|
|
1312
|
+
node: Object.freeze({
|
|
1313
|
+
...safeNode,
|
|
1314
|
+
data: freezeMarkdownPluginData(result.node.data),
|
|
1315
|
+
source: source.slice(offset, result.end),
|
|
1316
|
+
...(opts.sourceRanges && context === 'block'
|
|
1317
|
+
? {
|
|
1318
|
+
position: Object.freeze({
|
|
1319
|
+
start: Object.freeze({offset: absoluteStart}),
|
|
1320
|
+
end: Object.freeze({offset: absoluteEnd}),
|
|
1321
|
+
}),
|
|
1322
|
+
}
|
|
1323
|
+
: null),
|
|
1324
|
+
}) as RuntimeExtensionNode,
|
|
1325
|
+
};
|
|
1326
|
+
}
|
|
1327
|
+
return {status: 'none'};
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
type ParseOptionsWithoutPlugins = Omit<ParseOptions, 'plugins'>;
|
|
1331
|
+
type MathParseOptionsWithoutPlugins = Omit<MathParseOptions, 'plugins'>;
|
|
1332
|
+
type IncrementalParseOptionsWithoutPlugins = Omit<
|
|
1333
|
+
IncrementalParseOptions,
|
|
1334
|
+
'plugins'
|
|
1335
|
+
>;
|
|
1336
|
+
type IncrementalMathParseOptionsWithoutPlugins = Omit<
|
|
1337
|
+
IncrementalMathParseOptions,
|
|
1338
|
+
'plugins'
|
|
1339
|
+
>;
|
|
1340
|
+
|
|
781
1341
|
export function parseInline(
|
|
782
1342
|
text: string,
|
|
783
1343
|
sourceIds?: ReadonlySet<string>,
|
|
784
1344
|
): InlineNode[];
|
|
785
1345
|
export function parseInline(
|
|
786
1346
|
text: string,
|
|
787
|
-
options:
|
|
1347
|
+
options: MathParseOptionsWithoutPlugins,
|
|
788
1348
|
): InlineNodeWithMath[];
|
|
789
|
-
export function parseInline(
|
|
1349
|
+
export function parseInline(
|
|
1350
|
+
text: string,
|
|
1351
|
+
options: ParseOptionsWithoutPlugins,
|
|
1352
|
+
): InlineNode[];
|
|
1353
|
+
export function parseInline<
|
|
1354
|
+
const Plugins extends ReadonlyArray<MarkdownPluginEntry>,
|
|
1355
|
+
>(
|
|
1356
|
+
text: string,
|
|
1357
|
+
options: ParseOptionsWithoutPlugins & {plugins: Plugins},
|
|
1358
|
+
): InlineNode<MarkdownExtensionsOf<Plugins>>[];
|
|
1359
|
+
export function parseInline<
|
|
1360
|
+
const Plugins extends ReadonlyArray<MarkdownPluginEntry>,
|
|
1361
|
+
>(
|
|
1362
|
+
text: string,
|
|
1363
|
+
options: MathParseOptionsWithoutPlugins & {plugins: Plugins},
|
|
1364
|
+
): InlineNodeWithMath<MarkdownExtensionsOf<Plugins>>[];
|
|
790
1365
|
export function parseInline(
|
|
791
1366
|
text: string,
|
|
792
1367
|
arg?: ReadonlySet<string> | RuntimeParseOptions,
|
|
793
|
-
):
|
|
794
|
-
return
|
|
1368
|
+
): RuntimeInlineNode[] {
|
|
1369
|
+
return projectInlineNodes(
|
|
1370
|
+
parseInlineAstRuntime(text, arg),
|
|
1371
|
+
wantsLegacyRanges(arg),
|
|
1372
|
+
);
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
/**
|
|
1376
|
+
* Parse inline Markdown into the canonical immutable Astryx AST.
|
|
1377
|
+
*
|
|
1378
|
+
* Unlike `parseInline`, this returns MDAST-aligned node names and fields. Math
|
|
1379
|
+
* nodes are present only when `math: true`, and plugin extension nodes are
|
|
1380
|
+
* inferred from the ordered `plugins` tuple.
|
|
1381
|
+
*/
|
|
1382
|
+
export function parseInlineAst(
|
|
1383
|
+
text: string,
|
|
1384
|
+
sourceIds?: ReadonlySet<string>,
|
|
1385
|
+
): MarkdownAstPhrasingContent[];
|
|
1386
|
+
export function parseInlineAst(
|
|
1387
|
+
text: string,
|
|
1388
|
+
options: ParseOptionsWithoutPlugins | MathParseOptionsWithoutPlugins,
|
|
1389
|
+
): MarkdownAstPhrasingContent[];
|
|
1390
|
+
export function parseInlineAst<
|
|
1391
|
+
const Plugins extends ReadonlyArray<MarkdownPluginEntry>,
|
|
1392
|
+
>(
|
|
1393
|
+
text: string,
|
|
1394
|
+
options: (ParseOptionsWithoutPlugins | MathParseOptionsWithoutPlugins) & {
|
|
1395
|
+
plugins: Plugins;
|
|
1396
|
+
},
|
|
1397
|
+
): MarkdownAstPhrasingContent<MarkdownExtensionsOf<Plugins>>[];
|
|
1398
|
+
export function parseInlineAst(
|
|
1399
|
+
text: string,
|
|
1400
|
+
arg?: ReadonlySet<string> | RuntimeParseOptions,
|
|
1401
|
+
): MarkdownAstPhrasingContent<RuntimeExtensionNode>[] {
|
|
1402
|
+
return parseInlineAstRuntime(text, arg);
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
function parseInlineAstRuntime(
|
|
1406
|
+
text: string,
|
|
1407
|
+
arg?: ReadonlySet<string> | RuntimeParseOptions,
|
|
1408
|
+
): MarkdownAstPhrasingContent<RuntimeExtensionNode>[] {
|
|
1409
|
+
const opts = resolveOptions(arg);
|
|
1410
|
+
const nodes = parseInlineEntry(text, opts);
|
|
1411
|
+
if ((opts.plugins?.transforms.length ?? 0) === 0) {
|
|
1412
|
+
return nodes;
|
|
1413
|
+
}
|
|
1414
|
+
const transformed = applyMarkdownTransforms(
|
|
1415
|
+
{type: 'root', children: [{type: 'paragraph', children: nodes}]},
|
|
1416
|
+
opts.plugins,
|
|
1417
|
+
text,
|
|
1418
|
+
opts.isFinal,
|
|
1419
|
+
'inline',
|
|
1420
|
+
);
|
|
1421
|
+
const paragraph = transformed.children[0];
|
|
1422
|
+
return paragraph?.type === 'paragraph' ? [...paragraph.children] : nodes;
|
|
795
1423
|
}
|
|
796
1424
|
|
|
797
1425
|
/**
|
|
@@ -806,22 +1434,32 @@ export function parseInline(
|
|
|
806
1434
|
function parseInlineEntry(
|
|
807
1435
|
text: string,
|
|
808
1436
|
opts: ResolvedOptions,
|
|
809
|
-
|
|
810
|
-
|
|
1437
|
+
context: 'default' | 'tableCell' = 'default',
|
|
1438
|
+
): MarkdownAstPhrasingContent<RuntimeExtensionNode>[] {
|
|
1439
|
+
const nodes = parseInlineImpl(text, opts, context);
|
|
811
1440
|
return opts.autolink === 'gfm' ? transformAutolinks(nodes) : nodes;
|
|
812
1441
|
}
|
|
813
1442
|
|
|
814
1443
|
function parseInlineImpl(
|
|
815
1444
|
text: string,
|
|
816
1445
|
opts: ResolvedOptions,
|
|
817
|
-
|
|
818
|
-
|
|
1446
|
+
context: 'default' | 'tableCell' = 'default',
|
|
1447
|
+
): MarkdownAstPhrasingContent<RuntimeExtensionNode>[] {
|
|
1448
|
+
const nodes: MarkdownAstPhrasingContent<RuntimeExtensionNode>[] = [];
|
|
1449
|
+
// Only a plugin that actually contributes INLINE syntax may cost anything
|
|
1450
|
+
// per source position. A transform-only list contributes none, so it takes
|
|
1451
|
+
// the same path as an omitted or empty one: no candidate probe per
|
|
1452
|
+
// position, and no map lookup per character in the plain-text scan below.
|
|
1453
|
+
const inlineExtensionStarts =
|
|
1454
|
+
opts.plugins != null && opts.plugins.inlineByFirstCharacter.size > 0
|
|
1455
|
+
? opts.plugins.inlineByFirstCharacter
|
|
1456
|
+
: undefined;
|
|
819
1457
|
let i = 0;
|
|
820
1458
|
|
|
821
1459
|
while (i < text.length) {
|
|
822
1460
|
// --- Escape ---
|
|
823
1461
|
if (text[i] === '\\' && i + 1 < text.length) {
|
|
824
|
-
nodes.push({type: 'text',
|
|
1462
|
+
nodes.push({type: 'text', value: text[i + 1]});
|
|
825
1463
|
i += 2;
|
|
826
1464
|
continue;
|
|
827
1465
|
}
|
|
@@ -832,7 +1470,13 @@ function parseInlineImpl(
|
|
|
832
1470
|
const openIndex = i + tickCount;
|
|
833
1471
|
const closeIndex = text.indexOf('`'.repeat(tickCount), openIndex);
|
|
834
1472
|
if (closeIndex !== -1) {
|
|
835
|
-
nodes.push({
|
|
1473
|
+
nodes.push({
|
|
1474
|
+
type: 'inlineCode',
|
|
1475
|
+
value:
|
|
1476
|
+
context === 'tableCell'
|
|
1477
|
+
? text.slice(openIndex, closeIndex).replace(/\\\|/g, '|')
|
|
1478
|
+
: text.slice(openIndex, closeIndex),
|
|
1479
|
+
});
|
|
836
1480
|
i = closeIndex + tickCount;
|
|
837
1481
|
continue;
|
|
838
1482
|
}
|
|
@@ -842,7 +1486,10 @@ function parseInlineImpl(
|
|
|
842
1486
|
if (opts.math && text[i] === '$') {
|
|
843
1487
|
const closeIndex = findInlineMathEnd(text, i);
|
|
844
1488
|
if (closeIndex !== -1) {
|
|
845
|
-
nodes.push({
|
|
1489
|
+
nodes.push({
|
|
1490
|
+
type: 'inlineMath',
|
|
1491
|
+
value: text.slice(i + 1, closeIndex),
|
|
1492
|
+
});
|
|
846
1493
|
i = closeIndex + 1;
|
|
847
1494
|
continue;
|
|
848
1495
|
}
|
|
@@ -865,13 +1512,13 @@ function parseInlineImpl(
|
|
|
865
1512
|
const srcClose = findClosingParen(text, altClose + 2);
|
|
866
1513
|
if (srcClose !== -1) {
|
|
867
1514
|
const src = text.slice(altClose + 2, srcClose);
|
|
868
|
-
if (!
|
|
1515
|
+
if (!isSafeMarkdownParserUrl(src)) {
|
|
869
1516
|
// Dangerous scheme — emit as plain text.
|
|
870
|
-
nodes.push({type: 'text',
|
|
1517
|
+
nodes.push({type: 'text', value: text.slice(i, srcClose + 1)});
|
|
871
1518
|
} else {
|
|
872
1519
|
nodes.push({
|
|
873
1520
|
type: 'image',
|
|
874
|
-
src,
|
|
1521
|
+
url: src,
|
|
875
1522
|
alt: text.slice(i + 2, altClose),
|
|
876
1523
|
});
|
|
877
1524
|
}
|
|
@@ -908,14 +1555,18 @@ function parseInlineImpl(
|
|
|
908
1555
|
const urlClose = findClosingParen(text, textClose + 2);
|
|
909
1556
|
if (urlClose !== -1) {
|
|
910
1557
|
const href = text.slice(textClose + 2, urlClose);
|
|
911
|
-
if (!
|
|
1558
|
+
if (!isSafeMarkdownParserUrl(href)) {
|
|
912
1559
|
// Dangerous scheme — emit as plain text instead of a link.
|
|
913
|
-
nodes.push({type: 'text',
|
|
1560
|
+
nodes.push({type: 'text', value: text.slice(i, urlClose + 1)});
|
|
914
1561
|
} else {
|
|
915
1562
|
nodes.push({
|
|
916
1563
|
type: 'link',
|
|
917
|
-
href,
|
|
918
|
-
children: parseInlineImpl(
|
|
1564
|
+
url: href,
|
|
1565
|
+
children: parseInlineImpl(
|
|
1566
|
+
text.slice(i + 1, textClose),
|
|
1567
|
+
protectedInlineOptions(opts),
|
|
1568
|
+
context,
|
|
1569
|
+
),
|
|
919
1570
|
});
|
|
920
1571
|
}
|
|
921
1572
|
i = urlClose + 1;
|
|
@@ -926,7 +1577,7 @@ function parseInlineImpl(
|
|
|
926
1577
|
|
|
927
1578
|
// --- Reference link [text][label] / [text][] / [text] ---
|
|
928
1579
|
if (opts.linkDefs != null && text[i] === '[') {
|
|
929
|
-
const ref = matchReferenceLink(text, i, opts.linkDefs, opts);
|
|
1580
|
+
const ref = matchReferenceLink(text, i, opts.linkDefs, opts, context);
|
|
930
1581
|
if (ref) {
|
|
931
1582
|
nodes.push(ref.node);
|
|
932
1583
|
i = ref.end;
|
|
@@ -950,11 +1601,15 @@ function parseInlineImpl(
|
|
|
950
1601
|
(!isUnderscore || !isWordChar(text[closeIndex + 3]))
|
|
951
1602
|
) {
|
|
952
1603
|
nodes.push({
|
|
953
|
-
type: '
|
|
1604
|
+
type: 'strong',
|
|
954
1605
|
children: [
|
|
955
1606
|
{
|
|
956
|
-
type: '
|
|
957
|
-
children: parseInlineImpl(
|
|
1607
|
+
type: 'emphasis',
|
|
1608
|
+
children: parseInlineImpl(
|
|
1609
|
+
text.slice(i + 3, closeIndex),
|
|
1610
|
+
opts,
|
|
1611
|
+
context,
|
|
1612
|
+
),
|
|
958
1613
|
},
|
|
959
1614
|
],
|
|
960
1615
|
});
|
|
@@ -980,8 +1635,12 @@ function parseInlineImpl(
|
|
|
980
1635
|
(!isUnderscore || !isWordChar(text[closeIndex + 2]))
|
|
981
1636
|
) {
|
|
982
1637
|
nodes.push({
|
|
983
|
-
type: '
|
|
984
|
-
children: parseInlineImpl(
|
|
1638
|
+
type: 'strong',
|
|
1639
|
+
children: parseInlineImpl(
|
|
1640
|
+
text.slice(i + 2, closeIndex),
|
|
1641
|
+
opts,
|
|
1642
|
+
context,
|
|
1643
|
+
),
|
|
985
1644
|
});
|
|
986
1645
|
i = closeIndex + 2;
|
|
987
1646
|
continue;
|
|
@@ -994,8 +1653,12 @@ function parseInlineImpl(
|
|
|
994
1653
|
const closeIndex = text.indexOf('~~', i + 2);
|
|
995
1654
|
if (closeIndex !== -1) {
|
|
996
1655
|
nodes.push({
|
|
997
|
-
type: '
|
|
998
|
-
children: parseInlineImpl(
|
|
1656
|
+
type: 'delete',
|
|
1657
|
+
children: parseInlineImpl(
|
|
1658
|
+
text.slice(i + 2, closeIndex),
|
|
1659
|
+
opts,
|
|
1660
|
+
context,
|
|
1661
|
+
),
|
|
999
1662
|
});
|
|
1000
1663
|
i = closeIndex + 2;
|
|
1001
1664
|
continue;
|
|
@@ -1015,8 +1678,12 @@ function parseInlineImpl(
|
|
|
1015
1678
|
(!isUnderscore || !isWordChar(text[closeIndex + 1]))
|
|
1016
1679
|
) {
|
|
1017
1680
|
nodes.push({
|
|
1018
|
-
type: '
|
|
1019
|
-
children: parseInlineImpl(
|
|
1681
|
+
type: 'emphasis',
|
|
1682
|
+
children: parseInlineImpl(
|
|
1683
|
+
text.slice(i + 1, closeIndex),
|
|
1684
|
+
opts,
|
|
1685
|
+
context,
|
|
1686
|
+
),
|
|
1020
1687
|
});
|
|
1021
1688
|
i = closeIndex + 1;
|
|
1022
1689
|
continue;
|
|
@@ -1024,12 +1691,29 @@ function parseInlineImpl(
|
|
|
1024
1691
|
}
|
|
1025
1692
|
}
|
|
1026
1693
|
|
|
1694
|
+
// --- Extension syntax (built-ins and protected contexts win) ---
|
|
1695
|
+
if (inlineExtensionStarts !== undefined) {
|
|
1696
|
+
const extension = matchExtensionSyntax(text, i, 'inline', opts);
|
|
1697
|
+
if (extension.status === 'match') {
|
|
1698
|
+
nodes.push(
|
|
1699
|
+
extension.node as Extract<RuntimeExtensionNode, {display: 'inline'}>,
|
|
1700
|
+
);
|
|
1701
|
+
i = extension.end;
|
|
1702
|
+
continue;
|
|
1703
|
+
}
|
|
1704
|
+
if (extension.status === 'defer') {
|
|
1705
|
+
break;
|
|
1706
|
+
}
|
|
1707
|
+
}
|
|
1708
|
+
|
|
1027
1709
|
// --- Plain text (with line-break detection) ---
|
|
1028
1710
|
let end = i + 1;
|
|
1029
1711
|
while (
|
|
1030
1712
|
end < text.length &&
|
|
1031
1713
|
!'*_~`[!\\\n\u3010'.includes(text[end]) &&
|
|
1032
|
-
!(opts.math && text[end] === '$')
|
|
1714
|
+
!(opts.math && text[end] === '$') &&
|
|
1715
|
+
(inlineExtensionStarts === undefined ||
|
|
1716
|
+
!inlineExtensionStarts.has(text[end]))
|
|
1033
1717
|
) {
|
|
1034
1718
|
end++;
|
|
1035
1719
|
}
|
|
@@ -1043,9 +1727,12 @@ function parseInlineImpl(
|
|
|
1043
1727
|
if (trimmed.length > 0) {
|
|
1044
1728
|
const last = nodes[nodes.length - 1];
|
|
1045
1729
|
if (last?.type === 'text') {
|
|
1046
|
-
|
|
1730
|
+
nodes[nodes.length - 1] = {
|
|
1731
|
+
...last,
|
|
1732
|
+
value: last.value + trimmed,
|
|
1733
|
+
};
|
|
1047
1734
|
} else {
|
|
1048
|
-
nodes.push({type: 'text',
|
|
1735
|
+
nodes.push({type: 'text', value: trimmed});
|
|
1049
1736
|
}
|
|
1050
1737
|
}
|
|
1051
1738
|
nodes.push({type: 'break'});
|
|
@@ -1056,9 +1743,9 @@ function parseInlineImpl(
|
|
|
1056
1743
|
|
|
1057
1744
|
const last = nodes[nodes.length - 1];
|
|
1058
1745
|
if (last?.type === 'text') {
|
|
1059
|
-
last.
|
|
1746
|
+
nodes[nodes.length - 1] = {...last, value: last.value + content};
|
|
1060
1747
|
} else {
|
|
1061
|
-
nodes.push({type: 'text', content});
|
|
1748
|
+
nodes.push({type: 'text', value: content});
|
|
1062
1749
|
}
|
|
1063
1750
|
i = end;
|
|
1064
1751
|
}
|
|
@@ -1183,7 +1870,7 @@ function scanAutolinksInText(text: string): AutolinkMatch[] {
|
|
|
1183
1870
|
while ((m = re.exec(text)) !== null) {
|
|
1184
1871
|
const url = m[1];
|
|
1185
1872
|
// Skip dangerous URL schemes (javascript:, vbscript:, data:text/html)
|
|
1186
|
-
if (!
|
|
1873
|
+
if (!isSafeMarkdownParserUrl(url)) {
|
|
1187
1874
|
continue;
|
|
1188
1875
|
}
|
|
1189
1876
|
matches.push({
|
|
@@ -1303,26 +1990,28 @@ function scanAutolinksInText(text: string): AutolinkMatch[] {
|
|
|
1303
1990
|
* Split a text-node `content` string into a sequence of text + link nodes
|
|
1304
1991
|
* based on autolink matches.
|
|
1305
1992
|
*/
|
|
1306
|
-
function splitTextOnAutolinks(
|
|
1993
|
+
function splitTextOnAutolinks(
|
|
1994
|
+
content: string,
|
|
1995
|
+
): MarkdownAstPhrasingContent<RuntimeExtensionNode>[] {
|
|
1307
1996
|
const matches = scanAutolinksInText(content);
|
|
1308
1997
|
if (matches.length === 0) {
|
|
1309
|
-
return [{type: 'text', content}];
|
|
1998
|
+
return [{type: 'text', value: content}];
|
|
1310
1999
|
}
|
|
1311
|
-
const out:
|
|
2000
|
+
const out: MarkdownAstPhrasingContent<RuntimeExtensionNode>[] = [];
|
|
1312
2001
|
let cursor = 0;
|
|
1313
2002
|
for (const m of matches) {
|
|
1314
2003
|
if (m.start > cursor) {
|
|
1315
|
-
out.push({type: 'text',
|
|
2004
|
+
out.push({type: 'text', value: content.slice(cursor, m.start)});
|
|
1316
2005
|
}
|
|
1317
2006
|
out.push({
|
|
1318
2007
|
type: 'link',
|
|
1319
|
-
|
|
1320
|
-
children: [{type: 'text',
|
|
2008
|
+
url: m.href,
|
|
2009
|
+
children: [{type: 'text', value: m.display}],
|
|
1321
2010
|
});
|
|
1322
2011
|
cursor = m.end;
|
|
1323
2012
|
}
|
|
1324
2013
|
if (cursor < content.length) {
|
|
1325
|
-
out.push({type: 'text',
|
|
2014
|
+
out.push({type: 'text', value: content.slice(cursor)});
|
|
1326
2015
|
}
|
|
1327
2016
|
return out;
|
|
1328
2017
|
}
|
|
@@ -1335,23 +2024,25 @@ function splitTextOnAutolinks(content: string): InlineNodeWithMath[] {
|
|
|
1335
2024
|
* `image` alt text, `citation`, or `break`. Runs only on the outermost
|
|
1336
2025
|
* block's inline tree (see `parseInlineEntry`).
|
|
1337
2026
|
*/
|
|
1338
|
-
function transformAutolinks(
|
|
1339
|
-
|
|
2027
|
+
function transformAutolinks(
|
|
2028
|
+
nodes: ReadonlyArray<MarkdownAstPhrasingContent<RuntimeExtensionNode>>,
|
|
2029
|
+
): MarkdownAstPhrasingContent<RuntimeExtensionNode>[] {
|
|
2030
|
+
const out: MarkdownAstPhrasingContent<RuntimeExtensionNode>[] = [];
|
|
1340
2031
|
for (const node of nodes) {
|
|
1341
2032
|
if (node.type === 'text') {
|
|
1342
|
-
const split = splitTextOnAutolinks(node.
|
|
2033
|
+
const split = splitTextOnAutolinks(node.value);
|
|
1343
2034
|
for (const seg of split) {
|
|
1344
2035
|
const last = out[out.length - 1];
|
|
1345
2036
|
if (seg.type === 'text' && last?.type === 'text') {
|
|
1346
|
-
last.
|
|
2037
|
+
out[out.length - 1] = {...last, value: last.value + seg.value};
|
|
1347
2038
|
} else {
|
|
1348
2039
|
out.push(seg);
|
|
1349
2040
|
}
|
|
1350
2041
|
}
|
|
1351
2042
|
} else if (
|
|
1352
|
-
node.type === '
|
|
1353
|
-
node.type === '
|
|
1354
|
-
node.type === '
|
|
2043
|
+
node.type === 'strong' ||
|
|
2044
|
+
node.type === 'emphasis' ||
|
|
2045
|
+
node.type === 'delete'
|
|
1355
2046
|
) {
|
|
1356
2047
|
out.push({...node, children: transformAutolinks(node.children)});
|
|
1357
2048
|
} else {
|
|
@@ -1434,7 +2125,16 @@ function isTableSeparator(line: string): boolean {
|
|
|
1434
2125
|
* offset into it would not address the document.
|
|
1435
2126
|
*/
|
|
1436
2127
|
function nested(opts: ResolvedOptions): ResolvedOptions {
|
|
1437
|
-
|
|
2128
|
+
const nestedOptions =
|
|
2129
|
+
opts.allowBlockSyntax === false ? opts : {...opts, allowBlockSyntax: false};
|
|
2130
|
+
return nestedOptions.sourceRanges || nestedOptions.astPositions
|
|
2131
|
+
? {...nestedOptions, sourceRanges: false, astPositions: false}
|
|
2132
|
+
: nestedOptions;
|
|
2133
|
+
}
|
|
2134
|
+
|
|
2135
|
+
function blockExtensionColumn(line: string): number | null {
|
|
2136
|
+
const indentation = line.length - line.trimStart().length;
|
|
2137
|
+
return indentation <= 3 ? indentation : null;
|
|
1438
2138
|
}
|
|
1439
2139
|
|
|
1440
2140
|
/**
|
|
@@ -1510,10 +2210,13 @@ function parseTable(
|
|
|
1510
2210
|
lines: string[],
|
|
1511
2211
|
lineIndex: number,
|
|
1512
2212
|
opts: ResolvedOptions,
|
|
1513
|
-
): {node:
|
|
1514
|
-
const headers:
|
|
1515
|
-
|
|
1516
|
-
)
|
|
2213
|
+
): {node: MarkdownAstBlockContent<RuntimeExtensionNode>; nextIndex: number} {
|
|
2214
|
+
const headers: MarkdownAstTableCell<RuntimeExtensionNode>[] = splitTableRow(
|
|
2215
|
+
lines[lineIndex],
|
|
2216
|
+
).map(cell => ({
|
|
2217
|
+
type: 'tableCell',
|
|
2218
|
+
children: parseInlineEntry(cell, opts, 'tableCell'),
|
|
2219
|
+
}));
|
|
1517
2220
|
const alignments: TableAlignment[] = splitTableRow(lines[lineIndex + 1]).map(
|
|
1518
2221
|
cell => {
|
|
1519
2222
|
const trimmed = cell.trim();
|
|
@@ -1528,22 +2231,32 @@ function parseTable(
|
|
|
1528
2231
|
: null;
|
|
1529
2232
|
},
|
|
1530
2233
|
);
|
|
1531
|
-
const rows:
|
|
2234
|
+
const rows: MarkdownAstTableRow<RuntimeExtensionNode>[] = [];
|
|
1532
2235
|
let rowIndex = lineIndex + 2;
|
|
1533
2236
|
while (
|
|
1534
2237
|
rowIndex < lines.length &&
|
|
1535
2238
|
lines[rowIndex].includes('|') &&
|
|
1536
2239
|
lines[rowIndex].trim() !== ''
|
|
1537
2240
|
) {
|
|
1538
|
-
rows.push(
|
|
1539
|
-
|
|
1540
|
-
|
|
2241
|
+
rows.push({
|
|
2242
|
+
type: 'tableRow',
|
|
2243
|
+
children: splitTableRow(lines[rowIndex]).map(cell => ({
|
|
2244
|
+
type: 'tableCell',
|
|
2245
|
+
children: parseInlineEntry(cell, opts, 'tableCell'),
|
|
1541
2246
|
})),
|
|
1542
|
-
);
|
|
2247
|
+
});
|
|
1543
2248
|
rowIndex++;
|
|
1544
2249
|
}
|
|
2250
|
+
const header: MarkdownAstTableRow<RuntimeExtensionNode> = {
|
|
2251
|
+
type: 'tableRow',
|
|
2252
|
+
children: headers,
|
|
2253
|
+
};
|
|
1545
2254
|
return {
|
|
1546
|
-
node: {
|
|
2255
|
+
node: {
|
|
2256
|
+
type: 'table',
|
|
2257
|
+
align: alignments,
|
|
2258
|
+
children: [header, ...rows],
|
|
2259
|
+
},
|
|
1547
2260
|
nextIndex: rowIndex,
|
|
1548
2261
|
};
|
|
1549
2262
|
}
|
|
@@ -1553,8 +2266,8 @@ function parseList(
|
|
|
1553
2266
|
startIndex: number,
|
|
1554
2267
|
ordered: boolean,
|
|
1555
2268
|
opts: ResolvedOptions,
|
|
1556
|
-
): {node:
|
|
1557
|
-
const items:
|
|
2269
|
+
): {node: MarkdownAstBlockContent<RuntimeExtensionNode>; nextIndex: number} {
|
|
2270
|
+
const items: MarkdownAstListItem<RuntimeExtensionNode>[] = [];
|
|
1558
2271
|
const baseIndent = getIndent(lines[startIndex]);
|
|
1559
2272
|
// Ordered lists may use either '.' or ')' as the marker delimiter
|
|
1560
2273
|
// (CommonMark 5.2). Capture which one this list starts with so its items
|
|
@@ -1608,7 +2321,11 @@ function parseList(
|
|
|
1608
2321
|
itemText += '\n' + deindented.join('\n');
|
|
1609
2322
|
}
|
|
1610
2323
|
|
|
1611
|
-
items.push({
|
|
2324
|
+
items.push({
|
|
2325
|
+
type: 'listItem',
|
|
2326
|
+
checked,
|
|
2327
|
+
children: parseMarkdownImpl(itemText, nested(opts)),
|
|
2328
|
+
});
|
|
1612
2329
|
|
|
1613
2330
|
// CommonMark loose list: blank line(s) between items of the same style
|
|
1614
2331
|
// and indent still form one list. Skip the blanks and continue if the
|
|
@@ -1626,17 +2343,15 @@ function parseList(
|
|
|
1626
2343
|
index = lookahead;
|
|
1627
2344
|
}
|
|
1628
2345
|
}
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
items,
|
|
1637
|
-
},
|
|
1638
|
-
nextIndex: index,
|
|
2346
|
+
const node: MarkdownAstList<RuntimeExtensionNode> = {
|
|
2347
|
+
type: 'list',
|
|
2348
|
+
ordered,
|
|
2349
|
+
start,
|
|
2350
|
+
delimiter: ordered ? (delim as '.' | ')') : undefined,
|
|
2351
|
+
spread: loose || undefined,
|
|
2352
|
+
children: items,
|
|
1639
2353
|
};
|
|
2354
|
+
return {node, nextIndex: index};
|
|
1640
2355
|
}
|
|
1641
2356
|
|
|
1642
2357
|
// ---------------------------------------------------------------------------
|
|
@@ -1649,23 +2364,89 @@ export function parseMarkdown(
|
|
|
1649
2364
|
): BlockNode[];
|
|
1650
2365
|
export function parseMarkdown(
|
|
1651
2366
|
input: string,
|
|
1652
|
-
options:
|
|
2367
|
+
options: MathParseOptionsWithoutPlugins,
|
|
1653
2368
|
): BlockNodeWithMath[];
|
|
1654
2369
|
export function parseMarkdown(
|
|
1655
2370
|
input: string,
|
|
1656
|
-
options:
|
|
2371
|
+
options: ParseOptionsWithoutPlugins,
|
|
1657
2372
|
): BlockNode[];
|
|
2373
|
+
export function parseMarkdown<
|
|
2374
|
+
const Plugins extends ReadonlyArray<MarkdownPluginEntry>,
|
|
2375
|
+
>(
|
|
2376
|
+
input: string,
|
|
2377
|
+
options: ParseOptionsWithoutPlugins & {plugins: Plugins},
|
|
2378
|
+
): BlockNode<MarkdownExtensionsOf<Plugins>>[];
|
|
2379
|
+
export function parseMarkdown<
|
|
2380
|
+
const Plugins extends ReadonlyArray<MarkdownPluginEntry>,
|
|
2381
|
+
>(
|
|
2382
|
+
input: string,
|
|
2383
|
+
options: MathParseOptionsWithoutPlugins & {plugins: Plugins},
|
|
2384
|
+
): BlockNodeWithMath<MarkdownExtensionsOf<Plugins>>[];
|
|
1658
2385
|
export function parseMarkdown(
|
|
1659
2386
|
input: string,
|
|
1660
2387
|
arg?: ReadonlySet<string> | RuntimeParseOptions,
|
|
1661
|
-
):
|
|
1662
|
-
return
|
|
2388
|
+
): RuntimeBlockNode[] {
|
|
2389
|
+
return projectMarkdownRoot(
|
|
2390
|
+
parseMarkdownAstInternal(input, arg, true),
|
|
2391
|
+
wantsLegacyRanges(arg),
|
|
2392
|
+
);
|
|
2393
|
+
}
|
|
2394
|
+
|
|
2395
|
+
/**
|
|
2396
|
+
* Parse block Markdown into the canonical immutable Astryx AST.
|
|
2397
|
+
*
|
|
2398
|
+
* Unlike `parseMarkdown`, this returns the MDAST-aligned root used by Markdown,
|
|
2399
|
+
* transforms, and Outline. Math nodes are present only when `math: true`, and
|
|
2400
|
+
* plugin extension nodes are inferred from the ordered `plugins` tuple.
|
|
2401
|
+
*/
|
|
2402
|
+
export function parseMarkdownAst(
|
|
2403
|
+
input: string,
|
|
2404
|
+
sourceIds?: ReadonlySet<string>,
|
|
2405
|
+
): MarkdownAstRoot;
|
|
2406
|
+
export function parseMarkdownAst(
|
|
2407
|
+
input: string,
|
|
2408
|
+
options: ParseOptionsWithoutPlugins | MathParseOptionsWithoutPlugins,
|
|
2409
|
+
): MarkdownAstRoot;
|
|
2410
|
+
export function parseMarkdownAst<
|
|
2411
|
+
const Plugins extends ReadonlyArray<MarkdownPluginEntry>,
|
|
2412
|
+
>(
|
|
2413
|
+
input: string,
|
|
2414
|
+
options: (ParseOptionsWithoutPlugins | MathParseOptionsWithoutPlugins) & {
|
|
2415
|
+
plugins: Plugins;
|
|
2416
|
+
},
|
|
2417
|
+
): MarkdownAstRoot<MarkdownExtensionsOf<Plugins>>;
|
|
2418
|
+
export function parseMarkdownAst(
|
|
2419
|
+
input: string,
|
|
2420
|
+
arg?: ReadonlySet<string> | RuntimeParseOptions,
|
|
2421
|
+
): MarkdownAstRoot<RuntimeExtensionNode> {
|
|
2422
|
+
return parseMarkdownAstInternal(input, arg, true);
|
|
2423
|
+
}
|
|
2424
|
+
|
|
2425
|
+
/** @internal Full-parse entry point with streaming finality for Outline. */
|
|
2426
|
+
export function parseMarkdownAstInternal(
|
|
2427
|
+
input: string,
|
|
2428
|
+
arg: ReadonlySet<string> | RuntimeParseOptions | undefined,
|
|
2429
|
+
isFinal: boolean,
|
|
2430
|
+
): MarkdownAstRoot<RuntimeExtensionNode> {
|
|
2431
|
+
const resolved = resolveOptions(arg);
|
|
2432
|
+
const opts = isFinal ? resolved : {...resolved, isFinal: false};
|
|
2433
|
+
const root: MarkdownAstRoot<RuntimeExtensionNode> = {
|
|
2434
|
+
type: 'root',
|
|
2435
|
+
children: parseMarkdownImpl(input, opts),
|
|
2436
|
+
};
|
|
2437
|
+
return applyMarkdownTransforms(
|
|
2438
|
+
root,
|
|
2439
|
+
opts.plugins,
|
|
2440
|
+
input,
|
|
2441
|
+
opts.isFinal,
|
|
2442
|
+
'block',
|
|
2443
|
+
);
|
|
1663
2444
|
}
|
|
1664
2445
|
|
|
1665
2446
|
function parseMarkdownImpl(
|
|
1666
2447
|
input: string,
|
|
1667
2448
|
baseOpts: ResolvedOptions,
|
|
1668
|
-
):
|
|
2449
|
+
): MarkdownAstBlockContent<RuntimeExtensionNode>[] {
|
|
1669
2450
|
// Collect this input's link reference definitions and strip their lines,
|
|
1670
2451
|
// then merge them with any definitions inherited from an enclosing parse
|
|
1671
2452
|
// (the incremental parser passes the whole document's definitions in; a
|
|
@@ -1686,18 +2467,35 @@ function parseMarkdownImpl(
|
|
|
1686
2467
|
const opts: ResolvedOptions =
|
|
1687
2468
|
linkDefs != null ? {...baseOpts, linkDefs} : baseOpts;
|
|
1688
2469
|
const lines = cleaned.split('\n');
|
|
1689
|
-
const
|
|
2470
|
+
const hasBlockExtensionSyntax =
|
|
2471
|
+
opts.allowBlockSyntax !== false &&
|
|
2472
|
+
(opts.plugins?.blockByFirstCharacter.size ?? 0) > 0;
|
|
2473
|
+
// Derived from the already-split lines rather than a second character
|
|
2474
|
+
// scan: every line contributes its own length plus the newline it ended on.
|
|
2475
|
+
const lineOffsets = [0];
|
|
2476
|
+
if (opts.astPositions === true || hasBlockExtensionSyntax) {
|
|
2477
|
+
let offset = 0;
|
|
2478
|
+
for (let index = 0; index < lines.length; index++) {
|
|
2479
|
+
offset += lines[index].length + 1;
|
|
2480
|
+
lineOffsets.push(offset);
|
|
2481
|
+
}
|
|
2482
|
+
}
|
|
2483
|
+
const blockExtensionMatches = new Map<number, ExtensionMatch>();
|
|
2484
|
+
const blocks: MarkdownAstBlockContent<RuntimeExtensionNode>[] = [];
|
|
1690
2485
|
// The line each block started on, parallel to `blocks`. Only collected when
|
|
1691
2486
|
// ranges were asked for; a block's end is resolved after the loop, since the
|
|
1692
2487
|
// branch that produced it has already moved `index` past whatever it read.
|
|
1693
|
-
const blockStartLines: number[] | null =
|
|
2488
|
+
const blockStartLines: number[] | null =
|
|
2489
|
+
opts.astPositions === true ? [] : null;
|
|
1694
2490
|
// Set only by a block that consumes blank lines as content, where the
|
|
1695
2491
|
// positional end derivation would trim them away.
|
|
1696
|
-
const blockEndLines: (number | undefined)[] | null =
|
|
1697
|
-
? []
|
|
1698
|
-
: null;
|
|
2492
|
+
const blockEndLines: (number | undefined)[] | null =
|
|
2493
|
+
opts.astPositions === true ? [] : null;
|
|
1699
2494
|
let blockStartLine = 0;
|
|
1700
|
-
const pushBlock = (
|
|
2495
|
+
const pushBlock = (
|
|
2496
|
+
node: MarkdownAstBlockContent<RuntimeExtensionNode>,
|
|
2497
|
+
endLine?: number,
|
|
2498
|
+
) => {
|
|
1701
2499
|
blocks.push(node);
|
|
1702
2500
|
blockStartLines?.push(blockStartLine);
|
|
1703
2501
|
blockEndLines?.push(endLine);
|
|
@@ -1713,10 +2511,16 @@ function parseMarkdownImpl(
|
|
|
1713
2511
|
}
|
|
1714
2512
|
|
|
1715
2513
|
// --- Fenced code block ---
|
|
1716
|
-
const fenceMatch = line.match(/^(`{3,}|~{3,})
|
|
2514
|
+
const fenceMatch = line.match(/^(`{3,}|~{3,})/);
|
|
1717
2515
|
if (fenceMatch) {
|
|
1718
2516
|
const fence = fenceMatch[1];
|
|
1719
|
-
const
|
|
2517
|
+
const info = line.slice(fence.length);
|
|
2518
|
+
const language = info.match(/^(\S+)/)?.[1] ?? null;
|
|
2519
|
+
const legacyLanguage = info.match(/^(\w*)/)?.[1] || null;
|
|
2520
|
+
const meta =
|
|
2521
|
+
language == null
|
|
2522
|
+
? undefined
|
|
2523
|
+
: info.slice(language.length).trim() || undefined;
|
|
1720
2524
|
const codeLines: string[] = [];
|
|
1721
2525
|
index++;
|
|
1722
2526
|
while (index < lines.length && !lines[index].startsWith(fence)) {
|
|
@@ -1728,7 +2532,15 @@ function parseMarkdownImpl(
|
|
|
1728
2532
|
// can end on them, so it states its own end rather than letting the
|
|
1729
2533
|
// positional derivation trim them off.
|
|
1730
2534
|
pushBlock(
|
|
1731
|
-
|
|
2535
|
+
markMarkdownAstLegacyCodeLanguage(
|
|
2536
|
+
{
|
|
2537
|
+
type: 'code',
|
|
2538
|
+
lang: language,
|
|
2539
|
+
...(meta == null ? {} : {meta}),
|
|
2540
|
+
value: codeLines.join('\n'),
|
|
2541
|
+
},
|
|
2542
|
+
legacyLanguage,
|
|
2543
|
+
),
|
|
1732
2544
|
Math.min(index, lines.length) - 1,
|
|
1733
2545
|
);
|
|
1734
2546
|
continue;
|
|
@@ -1752,7 +2564,7 @@ function parseMarkdownImpl(
|
|
|
1752
2564
|
if (headingMatch) {
|
|
1753
2565
|
pushBlock({
|
|
1754
2566
|
type: 'heading',
|
|
1755
|
-
|
|
2567
|
+
depth: headingMatch[1].length as 1 | 2 | 3 | 4 | 5 | 6,
|
|
1756
2568
|
children: parseInlineEntry(headingMatch[2], opts),
|
|
1757
2569
|
});
|
|
1758
2570
|
index++;
|
|
@@ -1761,7 +2573,7 @@ function parseMarkdownImpl(
|
|
|
1761
2573
|
|
|
1762
2574
|
// --- HR (must precede list check to handle `- - -`, `* * *`, `_ _ _`) ---
|
|
1763
2575
|
if (isHorizontalRule(line)) {
|
|
1764
|
-
pushBlock({type: '
|
|
2576
|
+
pushBlock({type: 'thematicBreak'});
|
|
1765
2577
|
index++;
|
|
1766
2578
|
continue;
|
|
1767
2579
|
}
|
|
@@ -1773,9 +2585,9 @@ function parseMarkdownImpl(
|
|
|
1773
2585
|
if (
|
|
1774
2586
|
imageMatch &&
|
|
1775
2587
|
line.trim() === imageMatch[0] &&
|
|
1776
|
-
|
|
2588
|
+
isSafeMarkdownParserUrl(imageMatch[2])
|
|
1777
2589
|
) {
|
|
1778
|
-
pushBlock({type: 'image', alt: imageMatch[1],
|
|
2590
|
+
pushBlock({type: 'image', alt: imageMatch[1], url: imageMatch[2]});
|
|
1779
2591
|
index++;
|
|
1780
2592
|
continue;
|
|
1781
2593
|
}
|
|
@@ -1825,16 +2637,80 @@ function parseMarkdownImpl(
|
|
|
1825
2637
|
continue;
|
|
1826
2638
|
}
|
|
1827
2639
|
|
|
2640
|
+
// --- Extension block syntax (built-in blocks take precedence) ---
|
|
2641
|
+
if (hasBlockExtensionSyntax) {
|
|
2642
|
+
const extensionColumn = blockExtensionColumn(line);
|
|
2643
|
+
const extensionOffset =
|
|
2644
|
+
extensionColumn == null
|
|
2645
|
+
? lineOffsets[index]
|
|
2646
|
+
: lineOffsets[index] + extensionColumn;
|
|
2647
|
+
const extension =
|
|
2648
|
+
blockExtensionMatches.get(extensionOffset) ??
|
|
2649
|
+
(extensionColumn == null
|
|
2650
|
+
? {status: 'none' as const}
|
|
2651
|
+
: matchExtensionSyntax(cleaned, extensionOffset, 'block', opts));
|
|
2652
|
+
blockExtensionMatches.delete(extensionOffset);
|
|
2653
|
+
if (extension.status === 'match') {
|
|
2654
|
+
const consumedEnd = extension.end;
|
|
2655
|
+
const consumed = cleaned.slice(lineOffsets[index], consumedEnd);
|
|
2656
|
+
if (
|
|
2657
|
+
consumedEnd < cleaned.length &&
|
|
2658
|
+
cleaned[consumedEnd] !== '\n' &&
|
|
2659
|
+
cleaned[consumedEnd - 1] !== '\n'
|
|
2660
|
+
) {
|
|
2661
|
+
reportMarkdownPluginFailure(
|
|
2662
|
+
extension.node.plugin,
|
|
2663
|
+
'syntax',
|
|
2664
|
+
new TypeError('Block syntax must consume complete lines'),
|
|
2665
|
+
);
|
|
2666
|
+
} else {
|
|
2667
|
+
const newlineCount = consumed.split('\n').length - 1;
|
|
2668
|
+
const consumedLines = Math.max(
|
|
2669
|
+
1,
|
|
2670
|
+
newlineCount + (consumed.endsWith('\n') ? 0 : 1),
|
|
2671
|
+
);
|
|
2672
|
+
pushBlock(
|
|
2673
|
+
extension.node as Extract<RuntimeExtensionNode, {display: 'block'}>,
|
|
2674
|
+
index + consumedLines - 1,
|
|
2675
|
+
);
|
|
2676
|
+
index += consumedLines;
|
|
2677
|
+
continue;
|
|
2678
|
+
}
|
|
2679
|
+
}
|
|
2680
|
+
if (extension.status === 'defer') {
|
|
2681
|
+
break;
|
|
2682
|
+
}
|
|
2683
|
+
}
|
|
2684
|
+
|
|
1828
2685
|
// --- Paragraph ---
|
|
1829
2686
|
const paraLines: string[] = [line];
|
|
1830
2687
|
index++;
|
|
1831
|
-
while (
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
2688
|
+
while (index < lines.length) {
|
|
2689
|
+
const nextLine = lines[index];
|
|
2690
|
+
if (
|
|
2691
|
+
isBlockStart(nextLine) ||
|
|
2692
|
+
(opts.math && matchDisplayMathBlock(lines, index) != null) ||
|
|
2693
|
+
nextLine.trim() === ''
|
|
2694
|
+
) {
|
|
2695
|
+
break;
|
|
2696
|
+
}
|
|
2697
|
+
if (hasBlockExtensionSyntax) {
|
|
2698
|
+
const extensionColumn = blockExtensionColumn(nextLine);
|
|
2699
|
+
if (extensionColumn != null) {
|
|
2700
|
+
const extensionOffset = lineOffsets[index] + extensionColumn;
|
|
2701
|
+
const extension = matchExtensionSyntax(
|
|
2702
|
+
cleaned,
|
|
2703
|
+
extensionOffset,
|
|
2704
|
+
'block',
|
|
2705
|
+
opts,
|
|
2706
|
+
);
|
|
2707
|
+
if (extension.status !== 'none') {
|
|
2708
|
+
blockExtensionMatches.set(extensionOffset, extension);
|
|
2709
|
+
break;
|
|
2710
|
+
}
|
|
2711
|
+
}
|
|
2712
|
+
}
|
|
2713
|
+
paraLines.push(nextLine);
|
|
1838
2714
|
index++;
|
|
1839
2715
|
}
|
|
1840
2716
|
pushBlock({
|
|
@@ -1866,7 +2742,7 @@ function parseMarkdownImpl(
|
|
|
1866
2742
|
* `lineMap` says which input line each surviving line came from.
|
|
1867
2743
|
*/
|
|
1868
2744
|
function stampSourceRanges(
|
|
1869
|
-
blocks:
|
|
2745
|
+
blocks: MarkdownAstBlockContent<RuntimeExtensionNode>[],
|
|
1870
2746
|
blockStartLines: number[],
|
|
1871
2747
|
blockEndLines: (number | undefined)[],
|
|
1872
2748
|
lines: string[],
|
|
@@ -1903,7 +2779,13 @@ function stampSourceRanges(
|
|
|
1903
2779
|
// range that dropped it would slice to something that re-parses
|
|
1904
2780
|
// differently.
|
|
1905
2781
|
const end = lineStart(endLine) + lines[endLine].length;
|
|
1906
|
-
blocks[i] = {
|
|
2782
|
+
blocks[i] = {
|
|
2783
|
+
...blocks[i],
|
|
2784
|
+
position: {
|
|
2785
|
+
start: {offset: lineStart(startLine)},
|
|
2786
|
+
end: {offset: end},
|
|
2787
|
+
},
|
|
2788
|
+
};
|
|
1907
2789
|
}
|
|
1908
2790
|
}
|
|
1909
2791
|
|
|
@@ -1938,6 +2820,10 @@ export interface IncrementalState<MathEnabled extends boolean = false> {
|
|
|
1938
2820
|
* lack the ranges the caller now asks for, or carry ones it did not.
|
|
1939
2821
|
*/
|
|
1940
2822
|
sourceRanges?: boolean;
|
|
2823
|
+
/** Identity of citation sources used by settled nodes. */
|
|
2824
|
+
sourceIdsKey?: string;
|
|
2825
|
+
/** Ordered syntax-only identity used by settled nodes. */
|
|
2826
|
+
pluginSyntaxIdentity?: string;
|
|
1941
2827
|
/**
|
|
1942
2828
|
* Signature of the link reference definitions the cached `settledBlocks`
|
|
1943
2829
|
* were parsed with. Definitions are document-global and typically arrive
|
|
@@ -1968,6 +2854,13 @@ type IncrementalCache = {
|
|
|
1968
2854
|
/** The effective document-global definitions used by slice parses. */
|
|
1969
2855
|
linkDefs: ReadonlyMap<string, string>;
|
|
1970
2856
|
linkDefsKey: string;
|
|
2857
|
+
/** Canonical settled blocks shared by rendering and compatibility projection. */
|
|
2858
|
+
settledAstBlocks: MarkdownAstBlockContent<RuntimeExtensionNode>[];
|
|
2859
|
+
settledRevision: number;
|
|
2860
|
+
projectedRevision: number;
|
|
2861
|
+
projectedSettledBlocks: RuntimeBlockNode[];
|
|
2862
|
+
/** Preserves released settled-node identity across projected snapshots. */
|
|
2863
|
+
projectionCache: LegacyProjectionCache;
|
|
1971
2864
|
work: IncrementalWork;
|
|
1972
2865
|
};
|
|
1973
2866
|
|
|
@@ -1986,6 +2879,11 @@ function makeIncrementalCache(
|
|
|
1986
2879
|
tailLinkDefs: new Map(),
|
|
1987
2880
|
linkDefs: defs,
|
|
1988
2881
|
linkDefsKey: linkDefsSignature(defs),
|
|
2882
|
+
settledAstBlocks: [],
|
|
2883
|
+
settledRevision: 0,
|
|
2884
|
+
projectedRevision: -1,
|
|
2885
|
+
projectedSettledBlocks: [],
|
|
2886
|
+
projectionCache: new WeakMap(),
|
|
1989
2887
|
work: {
|
|
1990
2888
|
splitCharacters: 0,
|
|
1991
2889
|
boundaryLines: 0,
|
|
@@ -2470,7 +3368,7 @@ function trimUnsettledStructural(text: string): string {
|
|
|
2470
3368
|
* rather than into the slice.
|
|
2471
3369
|
*/
|
|
2472
3370
|
function atOffset(opts: ResolvedOptions, offset: number): ResolvedOptions {
|
|
2473
|
-
return opts.
|
|
3371
|
+
return opts.astPositions === true ? {...opts, baseOffset: offset} : opts;
|
|
2474
3372
|
}
|
|
2475
3373
|
|
|
2476
3374
|
/**
|
|
@@ -2481,9 +3379,9 @@ function atOffset(opts: ResolvedOptions, offset: number): ResolvedOptions {
|
|
|
2481
3379
|
* lists even though the full-text parser joins them per CommonMark §5.3.
|
|
2482
3380
|
*/
|
|
2483
3381
|
function mergeSettledBlocks(
|
|
2484
|
-
prev:
|
|
2485
|
-
delta:
|
|
2486
|
-
):
|
|
3382
|
+
prev: MarkdownAstBlockContent<RuntimeExtensionNode>[],
|
|
3383
|
+
delta: MarkdownAstBlockContent<RuntimeExtensionNode>[],
|
|
3384
|
+
): MarkdownAstBlockContent<RuntimeExtensionNode>[] {
|
|
2487
3385
|
if (prev.length === 0 || delta.length === 0) {
|
|
2488
3386
|
return [...prev, ...delta];
|
|
2489
3387
|
}
|
|
@@ -2495,17 +3393,21 @@ function mergeSettledBlocks(
|
|
|
2495
3393
|
prevLast.ordered === deltaFirst.ordered &&
|
|
2496
3394
|
prevLast.delimiter === deltaFirst.delimiter
|
|
2497
3395
|
) {
|
|
2498
|
-
const merged:
|
|
3396
|
+
const merged: MarkdownAstBlockContent<RuntimeExtensionNode> = {
|
|
2499
3397
|
type: 'list',
|
|
2500
3398
|
ordered: prevLast.ordered,
|
|
2501
3399
|
start: prevLast.start,
|
|
2502
3400
|
delimiter: prevLast.delimiter,
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
// One list now, so one
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
3401
|
+
spread: true,
|
|
3402
|
+
children: [...prevLast.children, ...deltaFirst.children],
|
|
3403
|
+
// One list now, so one position spans both halves.
|
|
3404
|
+
...(prevLast.position != null && deltaFirst.position != null
|
|
3405
|
+
? {
|
|
3406
|
+
position: {
|
|
3407
|
+
start: prevLast.position.start,
|
|
3408
|
+
end: deltaFirst.position.end,
|
|
3409
|
+
},
|
|
3410
|
+
}
|
|
2509
3411
|
: null),
|
|
2510
3412
|
};
|
|
2511
3413
|
return [...prev.slice(0, -1), merged, ...delta.slice(1)];
|
|
@@ -2515,15 +3417,15 @@ function mergeSettledBlocks(
|
|
|
2515
3417
|
|
|
2516
3418
|
/** Append a newly-settled slice without copying the already-settled prefix. */
|
|
2517
3419
|
function appendSettledBlocks(
|
|
2518
|
-
prev:
|
|
2519
|
-
delta:
|
|
2520
|
-
):
|
|
3420
|
+
prev: MarkdownAstBlockContent<RuntimeExtensionNode>[],
|
|
3421
|
+
delta: MarkdownAstBlockContent<RuntimeExtensionNode>[],
|
|
3422
|
+
): boolean {
|
|
2521
3423
|
if (delta.length === 0) {
|
|
2522
|
-
return;
|
|
3424
|
+
return false;
|
|
2523
3425
|
}
|
|
2524
3426
|
if (prev.length === 0) {
|
|
2525
3427
|
prev.push(...delta);
|
|
2526
|
-
return;
|
|
3428
|
+
return false;
|
|
2527
3429
|
}
|
|
2528
3430
|
const prevLast = prev[prev.length - 1];
|
|
2529
3431
|
const deltaFirst = delta[0];
|
|
@@ -2538,16 +3440,22 @@ function appendSettledBlocks(
|
|
|
2538
3440
|
ordered: prevLast.ordered,
|
|
2539
3441
|
start: prevLast.start,
|
|
2540
3442
|
delimiter: prevLast.delimiter,
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
...(prevLast.
|
|
2544
|
-
? {
|
|
3443
|
+
spread: true,
|
|
3444
|
+
children: [...prevLast.children, ...deltaFirst.children],
|
|
3445
|
+
...(prevLast.position != null && deltaFirst.position != null
|
|
3446
|
+
? {
|
|
3447
|
+
position: {
|
|
3448
|
+
start: prevLast.position.start,
|
|
3449
|
+
end: deltaFirst.position.end,
|
|
3450
|
+
},
|
|
3451
|
+
}
|
|
2545
3452
|
: null),
|
|
2546
3453
|
};
|
|
2547
3454
|
prev.push(...delta.slice(1));
|
|
2548
|
-
return;
|
|
3455
|
+
return true;
|
|
2549
3456
|
}
|
|
2550
3457
|
prev.push(...delta);
|
|
3458
|
+
return false;
|
|
2551
3459
|
}
|
|
2552
3460
|
|
|
2553
3461
|
function sameUnsettledDefinitions(
|
|
@@ -2577,12 +3485,19 @@ function resetIncrementalCache(
|
|
|
2577
3485
|
state.settledBlocks = [];
|
|
2578
3486
|
state.settledUpTo = 0;
|
|
2579
3487
|
state.linkDefsKey = undefined;
|
|
3488
|
+
state.sourceIdsKey = undefined;
|
|
3489
|
+
state.pluginSyntaxIdentity = undefined;
|
|
2580
3490
|
state.math = undefined;
|
|
2581
3491
|
cache.settledEnd = 0;
|
|
2582
3492
|
cache.settledLinkDefs.clear();
|
|
2583
3493
|
cache.tailLinkDefs = new Map();
|
|
2584
3494
|
cache.linkDefs = new Map();
|
|
2585
3495
|
cache.linkDefsKey = '';
|
|
3496
|
+
cache.settledAstBlocks = [];
|
|
3497
|
+
cache.settledRevision++;
|
|
3498
|
+
cache.projectedRevision = -1;
|
|
3499
|
+
cache.projectedSettledBlocks = [];
|
|
3500
|
+
cache.projectionCache = new WeakMap();
|
|
2586
3501
|
cache.work = {
|
|
2587
3502
|
splitCharacters: 0,
|
|
2588
3503
|
boundaryLines: 0,
|
|
@@ -2610,40 +3525,102 @@ export function parseMarkdownIncremental(
|
|
|
2610
3525
|
export function parseMarkdownIncremental(
|
|
2611
3526
|
input: string,
|
|
2612
3527
|
state: IncrementalState<true>,
|
|
2613
|
-
options:
|
|
3528
|
+
options: IncrementalMathParseOptionsWithoutPlugins,
|
|
2614
3529
|
): BlockNodeWithMath[];
|
|
2615
3530
|
export function parseMarkdownIncremental(
|
|
2616
3531
|
input: string,
|
|
2617
3532
|
state: IncrementalState<false>,
|
|
2618
|
-
options:
|
|
3533
|
+
options: IncrementalParseOptionsWithoutPlugins,
|
|
2619
3534
|
): BlockNode[];
|
|
3535
|
+
export function parseMarkdownIncremental<
|
|
3536
|
+
const Plugins extends ReadonlyArray<MarkdownPluginEntry>,
|
|
3537
|
+
>(
|
|
3538
|
+
input: string,
|
|
3539
|
+
state: IncrementalState<false>,
|
|
3540
|
+
options: IncrementalParseOptionsWithoutPlugins & {plugins: Plugins},
|
|
3541
|
+
): BlockNode<MarkdownExtensionsOf<Plugins>>[];
|
|
3542
|
+
export function parseMarkdownIncremental<
|
|
3543
|
+
const Plugins extends ReadonlyArray<MarkdownPluginEntry>,
|
|
3544
|
+
>(
|
|
3545
|
+
input: string,
|
|
3546
|
+
state: IncrementalState<true>,
|
|
3547
|
+
options: IncrementalMathParseOptionsWithoutPlugins & {plugins: Plugins},
|
|
3548
|
+
): BlockNodeWithMath<MarkdownExtensionsOf<Plugins>>[];
|
|
2620
3549
|
export function parseMarkdownIncremental(
|
|
2621
3550
|
input: string,
|
|
2622
3551
|
state: IncrementalState<boolean>,
|
|
2623
3552
|
arg?: ReadonlySet<string> | RuntimeParseOptions,
|
|
2624
|
-
):
|
|
2625
|
-
const
|
|
3553
|
+
): RuntimeBlockNode[] {
|
|
3554
|
+
const withRanges = wantsLegacyRanges(arg);
|
|
3555
|
+
const root = parseMarkdownAstIncremental(input, state, arg);
|
|
3556
|
+
const cache = incrementalCaches.get(state) ?? makeIncrementalCache(state);
|
|
3557
|
+
const projected = projectMarkdownRoot(
|
|
3558
|
+
root,
|
|
3559
|
+
withRanges,
|
|
3560
|
+
cache.projectionCache,
|
|
3561
|
+
);
|
|
3562
|
+
if (cache.projectedRevision !== cache.settledRevision) {
|
|
3563
|
+
cache.projectedSettledBlocks = cache.settledAstBlocks.map(block =>
|
|
3564
|
+
projectBlockNode(block, cache.projectionCache, withRanges),
|
|
3565
|
+
);
|
|
3566
|
+
cache.projectedRevision = cache.settledRevision;
|
|
3567
|
+
}
|
|
3568
|
+
state.settledBlocks =
|
|
3569
|
+
cache.projectedSettledBlocks as typeof state.settledBlocks;
|
|
3570
|
+
return projected;
|
|
3571
|
+
}
|
|
3572
|
+
|
|
3573
|
+
/** @internal Canonical incremental parse used by Markdown rendering. */
|
|
3574
|
+
export function parseMarkdownAstIncremental(
|
|
3575
|
+
input: string,
|
|
3576
|
+
state: IncrementalState<boolean>,
|
|
3577
|
+
arg?: ReadonlySet<string> | RuntimeParseOptions,
|
|
3578
|
+
): MarkdownAstRoot<RuntimeExtensionNode> {
|
|
3579
|
+
const opts = resolveOptions(arg, true);
|
|
3580
|
+
const root: MarkdownAstRoot<RuntimeExtensionNode> = {
|
|
3581
|
+
type: 'root',
|
|
3582
|
+
children: parseMarkdownIncrementalAstBlocks(input, state, opts),
|
|
3583
|
+
};
|
|
3584
|
+
return applyMarkdownTransforms(
|
|
3585
|
+
root,
|
|
3586
|
+
opts.plugins,
|
|
3587
|
+
input,
|
|
3588
|
+
opts.isFinal,
|
|
3589
|
+
'block',
|
|
3590
|
+
);
|
|
3591
|
+
}
|
|
3592
|
+
|
|
3593
|
+
function parseMarkdownIncrementalAstBlocks(
|
|
3594
|
+
input: string,
|
|
3595
|
+
state: IncrementalState<boolean>,
|
|
3596
|
+
opts: ResolvedOptions,
|
|
3597
|
+
): MarkdownAstBlockContent<RuntimeExtensionNode>[] {
|
|
2626
3598
|
const cache = incrementalCaches.get(state) ?? makeIncrementalCache(state);
|
|
2627
3599
|
let reparseSettled = false;
|
|
2628
3600
|
|
|
3601
|
+
const nextSourceIdsKey = sourceIdsSignature(opts.sourceIds);
|
|
3602
|
+
const nextPluginSyntaxIdentity = opts.plugins?.syntaxIdentity ?? '';
|
|
2629
3603
|
// Invalidate cache when an option that changes parsed nodes flips — cached
|
|
2630
3604
|
// settled blocks were parsed with the previous setting and would otherwise
|
|
2631
3605
|
// be reused unchanged.
|
|
2632
3606
|
if (
|
|
2633
3607
|
state.autolink !== opts.autolink ||
|
|
2634
3608
|
state.math !== opts.math ||
|
|
2635
|
-
Boolean(state.sourceRanges) !== Boolean(opts.sourceRanges)
|
|
3609
|
+
Boolean(state.sourceRanges) !== Boolean(opts.sourceRanges) ||
|
|
3610
|
+
state.sourceIdsKey !== nextSourceIdsKey ||
|
|
3611
|
+
state.pluginSyntaxIdentity !== nextPluginSyntaxIdentity
|
|
2636
3612
|
) {
|
|
2637
3613
|
reparseSettled = true;
|
|
2638
3614
|
state.autolink = opts.autolink;
|
|
2639
3615
|
state.math = opts.math;
|
|
2640
3616
|
state.sourceRanges = opts.sourceRanges;
|
|
3617
|
+
state.sourceIdsKey = nextSourceIdsKey;
|
|
3618
|
+
state.pluginSyntaxIdentity = nextPluginSyntaxIdentity;
|
|
2641
3619
|
}
|
|
2642
3620
|
if (input === '') {
|
|
2643
3621
|
resetIncrementalCache(state, cache);
|
|
2644
3622
|
return [];
|
|
2645
3623
|
}
|
|
2646
|
-
|
|
2647
3624
|
// The settled prefix is only reusable while the input still contains it
|
|
2648
3625
|
// verbatim. Lengths alone cannot tell: a same-length or longer replacement
|
|
2649
3626
|
// (new args after reusing a state) disagrees with the prefix without ever
|
|
@@ -2660,6 +3637,8 @@ export function parseMarkdownIncremental(
|
|
|
2660
3637
|
state.autolink = opts.autolink;
|
|
2661
3638
|
state.math = opts.math;
|
|
2662
3639
|
state.sourceRanges = opts.sourceRanges;
|
|
3640
|
+
state.sourceIdsKey = nextSourceIdsKey;
|
|
3641
|
+
state.pluginSyntaxIdentity = nextPluginSyntaxIdentity;
|
|
2663
3642
|
}
|
|
2664
3643
|
|
|
2665
3644
|
// The recurring parse costs are confined to the mutable suffix: splitting,
|
|
@@ -2744,28 +3723,52 @@ export function parseMarkdownIncremental(
|
|
|
2744
3723
|
? trimOpenDisplayMath(unsettledRaw)
|
|
2745
3724
|
: trimUnsettledStructural(unsettledRaw);
|
|
2746
3725
|
|
|
3726
|
+
const legacyRanges = opts.sourceRanges === true;
|
|
2747
3727
|
let parsedSettledBlocks = 0;
|
|
2748
3728
|
if (reparseSettled) {
|
|
2749
|
-
|
|
3729
|
+
cache.settledAstBlocks = state.settledText
|
|
2750
3730
|
? parseMarkdownImpl(state.settledText, parseOpts)
|
|
2751
3731
|
: [];
|
|
2752
|
-
parsedSettledBlocks =
|
|
3732
|
+
parsedSettledBlocks = cache.settledAstBlocks.length;
|
|
3733
|
+
cache.settledRevision++;
|
|
3734
|
+
cache.projectedSettledBlocks = cache.settledAstBlocks.map(block =>
|
|
3735
|
+
projectBlockNode(block, cache.projectionCache, legacyRanges),
|
|
3736
|
+
);
|
|
3737
|
+
cache.projectedRevision = cache.settledRevision;
|
|
2753
3738
|
} else if (settledDelta !== '') {
|
|
2754
3739
|
const deltaBlocks = parseMarkdownImpl(
|
|
2755
3740
|
settledDelta,
|
|
2756
3741
|
atOffset(parseOpts, oldSettledEnd),
|
|
2757
3742
|
);
|
|
2758
|
-
|
|
3743
|
+
const mergeIndex = cache.settledAstBlocks.length - 1;
|
|
3744
|
+
const mergedList = appendSettledBlocks(cache.settledAstBlocks, deltaBlocks);
|
|
2759
3745
|
parsedSettledBlocks = deltaBlocks.length;
|
|
3746
|
+
cache.settledRevision++;
|
|
3747
|
+
const projectedDelta = deltaBlocks.map(block =>
|
|
3748
|
+
projectBlockNode(block, cache.projectionCache, legacyRanges),
|
|
3749
|
+
);
|
|
3750
|
+
if (mergedList) {
|
|
3751
|
+
const canonicalMerged = cache.settledAstBlocks[mergeIndex];
|
|
3752
|
+
const projectedMerged = projectBlockNode(
|
|
3753
|
+
canonicalMerged,
|
|
3754
|
+
cache.projectionCache,
|
|
3755
|
+
legacyRanges,
|
|
3756
|
+
);
|
|
3757
|
+
cache.projectedSettledBlocks[mergeIndex] = projectedMerged;
|
|
3758
|
+
cache.projectedSettledBlocks.push(...projectedDelta.slice(1));
|
|
3759
|
+
} else {
|
|
3760
|
+
cache.projectedSettledBlocks.push(...projectedDelta);
|
|
3761
|
+
}
|
|
3762
|
+
cache.projectedRevision = cache.settledRevision;
|
|
2760
3763
|
}
|
|
2761
3764
|
|
|
2762
3765
|
// The unsettled tail is trimmed before parsing, so its offset in the
|
|
2763
3766
|
// document is where that trimmed text actually starts — not the boundary,
|
|
2764
|
-
// which is a line index. If it somehow
|
|
2765
|
-
//
|
|
2766
|
-
//
|
|
3767
|
+
// which is a line index. If it somehow cannot be located, parse it without
|
|
3768
|
+
// positions rather than report wrong ones. The search starts at the
|
|
3769
|
+
// settled end, so it scans the tail and never the prefix.
|
|
2767
3770
|
const unsettledStart =
|
|
2768
|
-
unsettledText && opts.
|
|
3771
|
+
unsettledText && opts.astPositions === true
|
|
2769
3772
|
? input.indexOf(unsettledText, cache.settledEnd)
|
|
2770
3773
|
: -1;
|
|
2771
3774
|
const unsettledBlocks = unsettledText
|
|
@@ -2790,7 +3793,7 @@ export function parseMarkdownIncremental(
|
|
|
2790
3793
|
definitionCharacters: settledDelta.length + unsettledInput.length,
|
|
2791
3794
|
renderedBlocks: parsedSettledBlocks + unsettledBlocks.length,
|
|
2792
3795
|
};
|
|
2793
|
-
return mergeSettledBlocks(
|
|
3796
|
+
return mergeSettledBlocks(cache.settledAstBlocks, unsettledBlocks);
|
|
2794
3797
|
}
|
|
2795
3798
|
|
|
2796
3799
|
// ---------------------------------------------------------------------------
|
|
@@ -2801,30 +3804,6 @@ export function parseMarkdownIncremental(
|
|
|
2801
3804
|
// derives its item ids from the same functions, so outline hash links always
|
|
2802
3805
|
// resolve to a rendered heading by construction.
|
|
2803
3806
|
|
|
2804
|
-
/** Flatten inline nodes into their plain text content. */
|
|
2805
|
-
export function inlineText(nodes: ReadonlyArray<InlineNodeWithMath>): string {
|
|
2806
|
-
return nodes
|
|
2807
|
-
.map(node => {
|
|
2808
|
-
switch (node.type) {
|
|
2809
|
-
case 'text':
|
|
2810
|
-
case 'code':
|
|
2811
|
-
case 'math':
|
|
2812
|
-
return node.type === 'math' ? node.value : node.content;
|
|
2813
|
-
case 'bold':
|
|
2814
|
-
case 'italic':
|
|
2815
|
-
case 'strikethrough':
|
|
2816
|
-
case 'link':
|
|
2817
|
-
return inlineText(node.children);
|
|
2818
|
-
case 'image':
|
|
2819
|
-
return node.alt;
|
|
2820
|
-
case 'citation':
|
|
2821
|
-
case 'break':
|
|
2822
|
-
return '';
|
|
2823
|
-
}
|
|
2824
|
-
})
|
|
2825
|
-
.join('');
|
|
2826
|
-
}
|
|
2827
|
-
|
|
2828
3807
|
/** Turn heading text into a URL-safe slug (lowercase, hyphen-separated). */
|
|
2829
3808
|
export function slugify(value: string): string {
|
|
2830
3809
|
return value
|