33 * @typedef {import('hast').Root } Root
44 */
55import { toString } from 'hast-util-to-string' ;
6+ import rangeParser from 'parse-numeric-range' ;
67import { visit } from 'unist-util-visit' ;
78
89/**
@@ -34,6 +35,18 @@ function visitor(node) {
3435 return ;
3536 }
3637
38+ const meta = /** @type {string } */ (
39+ node ?. data ?. meta || node ?. properties ?. metastring || ''
40+ ) ;
41+
42+ if ( meta . includes ( 'onboardingOptions' ) ) {
43+ handle_metadata_options ( node , meta ) ;
44+ return ;
45+ }
46+ handle_inline_options ( node ) ;
47+ }
48+
49+ function handle_inline_options ( node ) {
3750 /* @type {string | undefined } */
3851 let currentOption ;
3952
@@ -57,3 +70,74 @@ function visitor(node) {
5770 }
5871 } ) ;
5972}
73+
74+ /**
75+ * Parse the line numbers from the metastring
76+ * @param {string } meta
77+ * @return {number[] }
78+ * @example
79+ * parseLines('1, 3-4') // [1, 3, 4]
80+ * parseLines('') // []
81+ */
82+ const parseLines = meta => {
83+ const RE = / ( [ \d , - ] + ) / ;
84+ // Remove space between {} e.g. {1, 3}
85+ const parsedMeta = meta
86+ . split ( ',' )
87+ . map ( str => str . trim ( ) )
88+ . join ( ',' ) ;
89+ if ( RE . test ( parsedMeta ) ) {
90+ const strlineNumbers = RE . exec ( parsedMeta ) ?. [ 1 ] ;
91+ if ( ! strlineNumbers ) {
92+ return [ ] ;
93+ }
94+ const lineNumbers = rangeParser ( strlineNumbers ) ;
95+ return lineNumbers ;
96+ }
97+ return [ ] ;
98+ } ;
99+
100+ /**
101+ * Create a closure that returns an onboarding option `id` for a given line if it exists
102+ *
103+ * @param {string } meta
104+ * @return { (index:number) => string | undefined }
105+ */
106+ const getOptionForLine = meta => {
107+ // match the onboardingOptions object, but avoid `other stuff`
108+ const optionsRE = / { " o n b o a r d i n g O p t i o n s " : \s * ( { [ ^ } ] * } ) \s * } / ;
109+ let linesForOptions = { } ;
110+ const options = optionsRE . exec ( meta ) ?. [ 1 ] ;
111+ if ( ! options ) {
112+ return ( ) => undefined ;
113+ }
114+
115+ // eval provides the convenience of not having to wrap the object properties in quotes
116+ const parsedOptions = JSON . parse ( options ) ;
117+ linesForOptions = Object . keys ( parsedOptions ) . reduce ( ( acc , key ) => {
118+ acc [ key ] = parseLines ( parsedOptions [ key ] ) ;
119+ return acc ;
120+ } , { } ) ;
121+ return index => {
122+ for ( const key in linesForOptions ) {
123+ if ( linesForOptions [ key ] . includes ( index + 1 ) ) {
124+ return key ;
125+ }
126+ }
127+ return undefined ;
128+ } ;
129+ } ;
130+
131+ /**
132+ * @param {Element } node
133+ */
134+ function handle_metadata_options ( node , meta ) {
135+ const optionForLine = getOptionForLine ( meta ) ;
136+
137+ node . children . forEach ( ( line , index ) => {
138+ const option = optionForLine ( index ) ;
139+ if ( option ) {
140+ line . properties [ 'data-onboarding-option' ] = option ;
141+ }
142+ } ) ;
143+ }
0 commit comments