@@ -30,31 +30,75 @@ export interface Shortcode {
3030 params : string [ ] ;
3131}
3232
33- export function parseShortcode ( shortCodeCapture : string ) : Shortcode {
34- const [ name , ...args ] = shortCodeCapture . trim ( ) . split ( " " ) ;
35- const namedParams : Record < string , string > = { } ;
36- const params : string [ ] = [ ] ;
37- const rawParams = args . map ( ( v ) => {
38- const p = v . indexOf ( "=" ) ;
39- let name : string | undefined = undefined ;
40- let value : string ;
41- if ( p === - 1 ) {
42- value = v ;
43- params . push ( value ) ;
33+ // shortcode capture BNF:
34+ // shortcode = shortcode-name shortcode-params?
35+ // shortcode-name = [a-zA-Z0-9_]+
36+ // shortcode-params = shortcode-param*
37+ // shortcode-param = shortcode-param-value | shortcode-param-name "=" shortcode-param-value
38+ // shortcode-param-name = [a-zA-Z0-9_-]+
39+ // shortcode-param-value = [^"'\s]+ | '"' [^"]* '"' | "'" [^']* "'"
40+ function parseShortcodeCapture ( capture : string ) : Shortcode | undefined {
41+ // match shortcode name
42+ const nameMatch = capture . match ( / ^ [ a - z A - Z 0 - 9 _ ] + / ) ;
43+ if ( ! nameMatch ) {
44+ return ;
45+ }
46+ const params : Shortcode [ "params" ] = [ ] ;
47+ const namedParams : Shortcode [ "namedParams" ] = { } ;
48+ const rawParams : Shortcode [ "rawParams" ] = [ ] ;
49+
50+ const name = nameMatch [ 0 ] ;
51+ let paramStr = capture . slice ( name . length ) . trim ( ) ;
52+ // match params
53+ const paramName = "([a-zA-Z0-9_-]+)" ;
54+ const paramValue1 = "([^\"'\\s]+)" ;
55+ const paramValue2 = `"([^"]*)"` ;
56+ const paramValue3 = `'([^\']*)'` ;
57+
58+ const paramValue = `(?:${ paramValue1 } )|(?:${ paramValue2 } )|(?:${ paramValue3 } )` ;
59+ const paramNameAndValue =
60+ `(?:${ paramName } \\s*=\\s*${ paramValue1 } )|(?:${ paramName } \\s*=\\s*${ paramValue2 } )|(?:${ paramName } \\s*=\\s*${ paramValue3 } )` ;
61+
62+ const paramRe = new RegExp ( `(?:${ paramValue } |${ paramNameAndValue } )` ) ;
63+
64+ while ( paramStr . length ) {
65+ const paramMatch = paramStr . match ( paramRe ) ;
66+ if ( ! paramMatch ) {
67+ throw new Error ( "invalid shortcode: " + capture ) ;
68+ }
69+
70+ const captures = paramMatch . slice ( 1 ) . filter ( ( x ) => x !== undefined ) ;
71+ if ( captures . length === 1 ) {
72+ params . push ( captures [ 0 ] ) ;
73+ rawParams . push ( {
74+ value : captures [ 0 ] ,
75+ } ) ;
76+ // value only
77+ } else if ( captures . length === 2 ) {
78+ namedParams [ captures [ 0 ] ] = captures [ 1 ] ;
79+ rawParams . push ( {
80+ name : captures [ 0 ] ,
81+ value : captures [ 1 ] ,
82+ } ) ;
4483 } else {
45- name = v . slice ( 0 , p ) ;
46- value = v . slice ( p + 1 ) ;
47- namedParams [ name ] = value ;
84+ throw new Error (
85+ "Internal Error, could not determine correct shortcode capture for " +
86+ capture ,
87+ ) ;
4888 }
49- return { name, value } ;
50- } ) ;
5189
52- return {
53- name,
54- rawParams,
55- namedParams,
56- params,
57- } ;
90+ paramStr = paramStr . slice ( paramMatch [ 0 ] . length ) . trim ( ) ;
91+ }
92+ return { name, params, namedParams, rawParams } ;
93+ }
94+
95+ // TODO this should be handled by a pandoc parser.
96+ export function parseShortcode ( shortCodeCapture : string ) : Shortcode {
97+ const result = parseShortcodeCapture ( shortCodeCapture ) ;
98+ if ( ! result ) {
99+ throw new Error ( "invalid shortcode: " + shortCodeCapture ) ;
100+ }
101+ return result ;
58102}
59103
60104export function getShortcodeUnnamedParams ( shortcode : Shortcode ) : string [ ] {
0 commit comments