@@ -140,6 +140,146 @@ describe('Tree', () => {
140140 } )
141141 } )
142142
143+ describe ( 'special character encoding [x+hh]' , ( ) => {
144+ it ( 'parses single hex character code' , ( ) => {
145+ const tree = new PrefixTree ( RESOLVED_OPTIONS )
146+ const child = tree . insert ( '[x+2E]well-known' , '[x+2E]well-known.vue' )
147+ expect ( child ) . toBeDefined ( )
148+
149+ expect ( child . value ) . toMatchObject ( {
150+ rawSegment : '[x+2E]well-known' ,
151+ pathSegment : '.well-known' ,
152+ } )
153+ expect ( child . fullPath ) . toBe ( '/.well-known' )
154+ expect ( child . value . params ) . toEqual ( [ ] )
155+ expect ( child . value . isStatic ( ) ) . toBe ( true )
156+ } )
157+
158+ it ( 'parses multiple hex character codes in separate brackets' , ( ) => {
159+ const tree = new PrefixTree ( RESOLVED_OPTIONS )
160+ const child = tree . insert ( '[x+2E][x+2F]test' , '[x+2E][x+2F]test.vue' )
161+
162+ expect ( child . value ) . toMatchObject ( {
163+ rawSegment : '[x+2E][x+2F]test' ,
164+ pathSegment : './test' ,
165+ } )
166+ expect ( child . fullPath ) . toBe ( '/./test' )
167+ expect ( child . value . isStatic ( ) ) . toBe ( true )
168+ } )
169+
170+ it ( 'parses hex codes mixed with static prefix' , ( ) => {
171+ const tree = new PrefixTree ( RESOLVED_OPTIONS )
172+ const child = tree . insert ( 'prefix-[x+2E]-suffix' , 'file.vue' )
173+
174+ expect ( child . value ) . toMatchObject ( {
175+ rawSegment : 'prefix-[x+2E]-suffix' ,
176+ pathSegment : 'prefix-.-suffix' ,
177+ } )
178+ expect ( child . fullPath ) . toBe ( '/prefix-.-suffix' )
179+ expect ( child . value . isStatic ( ) ) . toBe ( true )
180+ } )
181+
182+ it ( 'creates smiley route path' , ( ) => {
183+ const tree = new PrefixTree ( RESOLVED_OPTIONS )
184+ const smileyNode = tree . insert (
185+ 'smileys/[x+3A]-[x+29]' ,
186+ 'smileys/[x+3A]-[x+29].vue'
187+ )
188+
189+ expect ( smileyNode . value ) . toMatchObject ( {
190+ pathSegment : ':-)' ,
191+ } )
192+ expect ( smileyNode . fullPath ) . toBe ( '/smileys/:-)' )
193+ expect ( smileyNode . value . isStatic ( ) ) . toBe ( true )
194+ } )
195+
196+ it ( 'allows lowercase hex codes' , ( ) => {
197+ const tree = new PrefixTree ( RESOLVED_OPTIONS )
198+ tree . insert ( '[x+2e]test' , '[x+2e]test.vue' )
199+ const child = tree . children . get ( '[x+2e]test' ) !
200+
201+ expect ( child . value . pathSegment ) . toBe ( '.test' )
202+ } )
203+
204+ it ( 'allows mixed case hex codes' , ( ) => {
205+ const tree = new PrefixTree ( RESOLVED_OPTIONS )
206+ const child = tree . insert ( '[x+2F][x+2e]' , 'file.vue' )
207+ expect ( child . value . pathSegment ) . toBe ( '/.' )
208+ } )
209+
210+ it ( 'throws on invalid hex code (non-hex characters)' , ( ) => {
211+ const tree = new PrefixTree ( RESOLVED_OPTIONS )
212+
213+ expect ( ( ) => tree . insert ( '[x+ZZ]' , '[x+ZZ].vue' ) ) . toThrow (
214+ / I n v a l i d h e x c o d e " Z Z " /
215+ )
216+ } )
217+
218+ it ( 'throws on incomplete hex code (single digit)' , ( ) => {
219+ const tree = new PrefixTree ( RESOLVED_OPTIONS )
220+
221+ expect ( ( ) => tree . insert ( '[x+2]' , '[x+2].vue' ) ) . toThrow (
222+ / m u s t b e e x a c t l y 2 d i g i t s /
223+ )
224+ } )
225+
226+ it ( 'throws on too many digits in hex code' , ( ) => {
227+ const tree = new PrefixTree ( RESOLVED_OPTIONS )
228+
229+ expect ( ( ) => tree . insert ( '[x+2EE]' , '[x+2EE].vue' ) ) . toThrow (
230+ / c o d e m u s t b e e x a c t l y 2 d i g i t s /
231+ )
232+ } )
233+
234+ it ( 'throws on empty hex code' , ( ) => {
235+ const tree = new PrefixTree ( RESOLVED_OPTIONS )
236+
237+ expect ( ( ) => tree . insert ( '[x+]' , '[x+].vue' ) ) . toThrow (
238+ / m u s t b e e x a c t l y 2 d i g i t s /
239+ )
240+ } )
241+
242+ it ( 'throws on unclosed hex code bracket' , ( ) => {
243+ const tree = new PrefixTree ( RESOLVED_OPTIONS )
244+
245+ expect ( ( ) => tree . insert ( '[x+2E' , '[x+2E.vue' ) ) . toThrow ( / I n v a l i d s e g m e n t / )
246+ } )
247+
248+ it ( 'does not interfere with regular params' , ( ) => {
249+ const tree = new PrefixTree ( RESOLVED_OPTIONS )
250+ tree . insert ( '[id]-[x+2E]-[name]' , '[id]-[x+2E]-[name].vue' )
251+ const child = tree . children . get ( '[id]-[x+2E]-[name]' ) !
252+
253+ expect ( child . value . isParam ( ) ) . toBe ( true )
254+ expect ( child . value . params ) . toHaveLength ( 2 )
255+ expect ( child . value . params [ 0 ] ) . toMatchObject ( { paramName : 'id' } )
256+ expect ( child . value . params [ 1 ] ) . toMatchObject ( { paramName : 'name' } )
257+ expect ( child . value . pathSegment ) . toContain ( '-.-' )
258+ expect ( child . value . pathSegment ) . toContain ( ':id' )
259+ expect ( child . value . pathSegment ) . toContain ( ':name' )
260+ } )
261+
262+ it ( 'does not treat param starting with x as hex code' , ( ) => {
263+ const tree = new PrefixTree ( RESOLVED_OPTIONS )
264+ tree . insert ( '[xid]' , '[xid].vue' )
265+ const child = tree . children . get ( '[xid]' ) !
266+
267+ expect ( child . value . isParam ( ) ) . toBe ( true )
268+ expect ( child . value . params [ 0 ] ) . toMatchObject ( { paramName : 'xid' } )
269+ expect ( child . value . pathSegment ) . toBe ( ':xid' )
270+ } )
271+
272+ it ( 'treats param named exactly x as normal param' , ( ) => {
273+ const tree = new PrefixTree ( RESOLVED_OPTIONS )
274+ tree . insert ( '[x]' , '[x].vue' )
275+ const child = tree . children . get ( '[x]' ) !
276+
277+ expect ( child . value . isParam ( ) ) . toBe ( true )
278+ expect ( child . value . params [ 0 ] ) . toMatchObject ( { paramName : 'x' } )
279+ expect ( child . value . pathSegment ) . toBe ( ':x' )
280+ } )
281+ } )
282+
143283 it ( 'separate param names from static segments' , ( ) => {
144284 const tree = new PrefixTree ( RESOLVED_OPTIONS )
145285 tree . insert ( '[id]_a' , '[id]_a.vue' )
0 commit comments