@@ -5,6 +5,10 @@ import {basename, join, resolve} from "node:path";
55import deepEqual from "fast-deep-equal" ;
66import { isEnoent } from "../src/error.js" ;
77import { type ParseResult , parseMarkdown } from "../src/markdown.js" ;
8+ import { normalizePieceHtml } from "../src/markdown.js" ;
9+
10+ const html = ( strings , ...values ) => String . raw ( { raw : strings } , ...values ) ;
11+ const mockContext = ( ) => ( { files : [ ] , imports : [ ] , pieces : [ ] , startLine : 0 , currentLine : 0 } ) ;
812
913describe ( "parseMarkdown(input)" , ( ) => {
1014 const inputRoot = "test/input" ;
@@ -58,6 +62,203 @@ describe("parseMarkdown(input)", () => {
5862 }
5963} ) ;
6064
65+ describe ( "normalizePieceHtml adds local file attachments" , ( ) => {
66+ const sourcePath = "/attachments.md" ;
67+
68+ it ( "img[src]" , ( ) => {
69+ const htmlStr = html `< img src ="./test.png "> ` ;
70+ const expected = html `< img src ="./_file/test.png "> ` ;
71+ const context = mockContext ( ) ;
72+ const actual = normalizePieceHtml ( htmlStr , sourcePath , context ) ;
73+
74+ assert . equal ( actual , expected ) ;
75+ assert . deepEqual ( context . files , [
76+ {
77+ mimeType : "image/png" ,
78+ name : "./test.png" ,
79+ path : "./_file/test.png"
80+ }
81+ ] ) ;
82+ } ) ;
83+
84+ it ( "img[srcset]" , ( ) => {
85+ const htmlStr = html `
86+ < img
87+ srcset ="small.jpg 480w, large.jpg 800w "
88+ sizes ="(max-width: 600px) 480px,
89+ 800px "
90+ src ="large.jpg "
91+ alt ="Image for testing "
92+ />
93+ ` ;
94+ const expected = html `
95+ < img srcset ="./_file/small.jpg 480w, ./_file/large.jpg 800w " sizes ="(max-width: 600px) 480px,
96+ 800px " src ="./_file/large.jpg " alt ="Image for testing ">
97+ ` ;
98+ const context = mockContext ( ) ;
99+ const actual = normalizePieceHtml ( htmlStr , sourcePath , context ) ;
100+
101+ assert . equal ( actual , expected ) ;
102+ assert . deepEqual ( context . files , [
103+ {
104+ mimeType : "image/jpeg" ,
105+ name : "large.jpg" ,
106+ path : "./_file/large.jpg"
107+ } ,
108+ {
109+ mimeType : "image/jpeg" ,
110+ name : "small.jpg" ,
111+ path : "./_file/small.jpg"
112+ }
113+ ] ) ;
114+ } ) ;
115+
116+ it ( "video[src]" , ( ) => {
117+ const htmlStr = html `< video src ="observable.mov " controls >
118+ Your browser doesn't support HTML video.
119+ </ video > ` ;
120+ const expected = html `< video src ="./_file/observable.mov " controls >
121+ Your browser doesn't support HTML video.
122+ </ video > ` ;
123+ const context = mockContext ( ) ;
124+ const actual = normalizePieceHtml ( htmlStr , sourcePath , context ) ;
125+
126+ assert . equal ( actual , expected ) ;
127+ assert . deepEqual ( context . files , [
128+ {
129+ mimeType : "video/quicktime" ,
130+ name : "observable.mov" ,
131+ path : "./_file/observable.mov"
132+ }
133+ ] ) ;
134+ } ) ;
135+
136+ it ( "video source[src]" , ( ) => {
137+ const htmlStr = html `< video width ="320 " height ="240 " controls >
138+ < source src ="observable.mp4 " type ="video/mp4 ">
139+ < source src ="observable.mov " type ="video/mov ">
140+ Your browser doesn't support HTML video.
141+ </ video > ` ;
142+
143+ const expected = html `< video width ="320 " height ="240 " controls >
144+ < source src ="./_file/observable.mp4 " type ="video/mp4 ">
145+ < source src ="./_file/observable.mov " type ="video/mov ">
146+ Your browser doesn't support HTML video.
147+ </ video > ` ;
148+
149+ const context = mockContext ( ) ;
150+ const actual = normalizePieceHtml ( htmlStr , sourcePath , context ) ;
151+
152+ assert . equal ( actual , expected ) ;
153+ assert . deepEqual ( context . files , [
154+ {
155+ mimeType : "video/mp4" ,
156+ name : "observable.mp4" ,
157+ path : "./_file/observable.mp4"
158+ } ,
159+ {
160+ mimeType : "video/quicktime" ,
161+ name : "observable.mov" ,
162+ path : "./_file/observable.mov"
163+ }
164+ ] ) ;
165+ } ) ;
166+
167+ it ( "picture source[srcset]" , ( ) => {
168+ const htmlStr = html `< picture >
169+ < source srcset ="observable-logo-wide.png " media ="(min-width: 600px) "/>
170+ < img src ="observable-logo-narrow.png " />
171+ </ picture > ` ;
172+
173+ const expected = html `< picture >
174+ < source srcset ="./_file/observable-logo-wide.png " media ="(min-width: 600px) ">
175+ < img src ="./_file/observable-logo-narrow.png ">
176+ </ picture > ` ;
177+
178+ const context = mockContext ( ) ;
179+ const actual = normalizePieceHtml ( htmlStr , sourcePath , context ) ;
180+
181+ assert . equal ( actual , expected ) ;
182+ assert . deepEqual ( context . files , [
183+ {
184+ mimeType : "image/png" ,
185+ name : "observable-logo-narrow.png" ,
186+ path : "./_file/observable-logo-narrow.png"
187+ } ,
188+ {
189+ mimeType : "image/png" ,
190+ name : "observable-logo-wide.png" ,
191+ path : "./_file/observable-logo-wide.png"
192+ }
193+ ] ) ;
194+ } ) ;
195+ } ) ;
196+
197+ describe ( "normalizePieceHtml only adds local files" , ( ) => {
198+ const sourcePath = "/attachments.md" ;
199+
200+ it ( "img[src] only adds local files" , ( ) => {
201+ const htmlStr = html `< img src ="https://upload.wikimedia.org/wikipedia/commons/thumb/0/0c/American_Shorthair.jpg/900px-American_Shorthair.jpg "> ` ;
202+ const expected = html `< img src ="https://upload.wikimedia.org/wikipedia/commons/thumb/0/0c/American_Shorthair.jpg/900px-American_Shorthair.jpg "> ` ;
203+ const context = mockContext ( ) ;
204+ const actual = normalizePieceHtml ( htmlStr , sourcePath , context ) ;
205+
206+ assert . equal ( actual , expected ) ;
207+ assert . deepEqual ( context . files , [ ] ) ;
208+ } ) ;
209+
210+ it ( "img[srcset] only adds local files" , ( ) => {
211+ const htmlStr = html `
212+ < img
213+ srcset ="small.jpg 480w, https://upload.wikimedia.org/900px-American_Shorthair.jpg 900w "
214+ sizes ="(max-width: 600px) 480px, 900px "
215+ src ="https://upload.wikimedia.org/900px-American_Shorthair.jpg "
216+ alt ="Cat image for testing "
217+ />
218+ ` ;
219+ const expected = html `
220+ < img srcset ="./_file/small.jpg 480w, https://upload.wikimedia.org/900px-American_Shorthair.jpg 900w " sizes ="(max-width: 600px) 480px, 900px " src ="https://upload.wikimedia.org/900px-American_Shorthair.jpg " alt ="Cat image for testing ">
221+ ` ;
222+ const context = mockContext ( ) ;
223+ const actual = normalizePieceHtml ( htmlStr , sourcePath , context ) ;
224+
225+ assert . equal ( actual , expected ) ;
226+ assert . deepEqual ( context . files , [
227+ {
228+ mimeType : "image/jpeg" ,
229+ name : "small.jpg" ,
230+ path : "./_file/small.jpg"
231+ }
232+ ] ) ;
233+ } ) ;
234+
235+ it ( "video source[src] only adds local files" , ( ) => {
236+ const htmlStr = html `< video width ="320 " height ="240 " controls >
237+ < source src ="https://www.youtube.com/watch?v=SsFyayu5csc " type ="video/youtube "/>
238+ < source src ="observable.mov " type ="video/mov ">
239+ Your browser doesn't support HTML video.
240+ </ video > ` ;
241+
242+ const expected = html `< video width ="320 " height ="240 " controls >
243+ < source src ="https://www.youtube.com/watch?v=SsFyayu5csc " type ="video/youtube ">
244+ < source src ="./_file/observable.mov " type ="video/mov ">
245+ Your browser doesn't support HTML video.
246+ </ video > ` ;
247+
248+ const context = mockContext ( ) ;
249+ const actual = normalizePieceHtml ( htmlStr , sourcePath , context ) ;
250+
251+ assert . equal ( actual , expected ) ;
252+ assert . deepEqual ( context . files , [
253+ {
254+ mimeType : "video/quicktime" ,
255+ name : "observable.mov" ,
256+ path : "./_file/observable.mov"
257+ }
258+ ] ) ;
259+ } ) ;
260+ } ) ;
261+
61262function jsonMeta ( { html, ...rest } : ParseResult ) : string {
62263 return JSON . stringify ( rest , null , 2 ) ;
63264}
0 commit comments