@@ -36,22 +36,32 @@ function removeSlashes(path) {
3636 return removeTrailingSlash ( removeLeadingSlash ( path ) ) ;
3737}
3838
39+ /**
40+ * A `maidenPathname` is a `URL.pathname` without any path prefix, relative or absolute.
41+ * e.g. 👇
42+ * ```
43+ * https://www.example.com/js/main.js -> js/main.js
44+ * /js/main.js -> js/main.js
45+ * ./js/main.js -> js/main.js
46+ * js/main.js -> js/main.js
47+ * ```
48+ * @param {string } maidenPathname
49+ * @returns {string }
50+ */
3951function getFileExtensionFromPath ( maidenPathname ) {
4052 const [ fileExtension ] = maidenPathname . split ( '.' ) . reverse ( ) ;
4153 return `.${ fileExtension } ` ;
4254}
4355
56+ /**
57+ * @param {Request } request
58+ * @returns {Promise<Response> }
59+ */
4460async function requestHandler ( request ) {
45- const mode = request . headers . get ( 'sec-fetch-mode' ) ;
46- const dest = request . headers . get ( 'sec-fetch-dest' ) ;
47- const site = request . headers . get ( 'sec-fetch-site' ) ;
48-
4961 const { pathname } = new URL ( request . url ) ;
50- const assetPath = assetMap [ pathname ] ;
51- const maidenPathname = removeSlashes ( assetPath ) ;
5262
5363 const { sessionStorage, localStorage } = globalThis ;
54-
64+
5565 const storage = sessionStorage || localStorage ;
5666
5767 if ( storage ) {
@@ -70,34 +80,117 @@ async function requestHandler(request) {
7080 }
7181 }
7282
73- const textFileContent = await Deno . readTextFile ( assetPath ) ;
74- // Absolute paths.
75- // The content of the repository is available under at Deno.cwd().
76- // const readmeAbsolute = await Deno.readFile(`${Deno.cwd()}/README.md`);
77- // File URLs are also supported.
78- // const readmeFileUrl = await Deno.readFile(
79- // new URL(`file://${Deno.cwd()}/README.md`),
80- // );
81-
82- // Decode the Uint8Array as string.
83- // const readme = new TextDecoder().decode(readmeRelative);
84- // return new Response(textFileContent);
85- return new Response ( textFileContent , {
86- headers : {
87- "content-type" : MEDIA_TYPES [ getFileExtensionFromPath ( maidenPathname ) ] ,
83+ const assetPath = assetMap [ pathname ] ;
84+
85+ if ( assetPath ) {
86+ try {
87+
88+ const mode = request . headers . get ( 'sec-fetch-mode' ) ;
89+ const dest = request . headers . get ( 'sec-fetch-dest' ) ;
90+ const contentTypeHTML = mode === 'navigate' || dest === 'document' ;
91+
92+ if ( contentTypeHTML ) {
93+ const content = await Deno . readTextFile ( assetPath ) ;
94+ const { main } = await import ( './js/importmap-generator.js' ) ;
95+ const importMap = await main ( ) ;
96+
97+ const [ beforeImportmap , afterImportmap ] = content . split ( "//__importmap" ) ;
98+ const html = `${ beforeImportmap } ${ importMap } ${ afterImportmap } ` ;
99+
100+ return new Response ( html , {
101+ headers : {
102+ "content-type" : MEDIA_TYPES [ '.html' ] ,
103+ }
104+ } ) ;
105+ }
106+
107+ const content = Deno . readFile ( assetPath ) ;
108+ const textDecodedContent = new TextDecoder ( ) . decode ( fileContent ) ;
109+ const maidenPathname = removeSlashes ( assetPath ) ;
110+
111+ return new Response ( textDecodedContent , {
112+ headers : {
113+ "content-type" : MEDIA_TYPES [ getFileExtensionFromPath ( maidenPathname ) ] ,
114+ }
115+ } ) ;
116+ } catch ( error ) {
117+ return new Response ( error . message || error . toString ( ) , { status : 500 } ) ;
118+ }
119+ }
120+
121+
122+ const site = request . headers . get ( 'sec-fetch-site' ) ;
123+ const contentTypeCompiledJSX = dest === 'script' && mode === 'cors' && site === 'same-origin' && pathname . endsWith ( ".jsx.js" ) ;
124+
125+ if ( contentTypeCompiledJSX ) {
126+ try {
127+ const { files, diagnostics } = await Deno . emit ( `.${ pathname } ` . slice ( 0 , - 3 ) ) ;
128+
129+ if ( diagnostics . length ) {
130+ // there is something that impacted the emit
131+ console . warn ( Deno . formatDiagnostics ( diagnostics ) ) ;
132+ }
133+ // @ts -ignore
134+ const [ , content ] = Object . entries ( files ) . find ( ( [ fileName ] ) => {
135+ const cwd = toFileUrl ( Deno . cwd ( ) ) . href ;
136+ const commonPath = common ( [
137+ cwd ,
138+ fileName ,
139+ ] ) ;
140+ const shortFileName = fileName . replace ( commonPath , `/` ) ;
141+
142+ return shortFileName === pathname ;
143+ } ) ;
144+ return new Response ( content ) ;
145+ } catch ( error ) {
146+ return new Response ( error . message || error . toString ( ) , { status : 500 } )
147+ }
148+ }
149+
150+ if ( pathname . endsWith ( ".jsx" ) ) {
151+ try {
152+ if ( storage ) {
153+ const { files, diagnostics } = await Deno . emit ( `.${ pathname } ` ) ;
154+
155+ if ( diagnostics . length ) {
156+ // there is something that impacted the emit
157+ console . warn ( Deno . formatDiagnostics ( diagnostics ) ) ;
158+ }
159+
160+ for ( const [ fileName , text ] of Object . entries ( files ) ) {
161+
162+ const cwd = toFileUrl ( Deno . cwd ( ) ) . href ;
163+ const commonPath = common ( [
164+ cwd ,
165+ fileName ,
166+ ] ) ;
167+ const maidenPathname = fileName . replace ( commonPath , `` ) ;
168+
169+ storage . setItem ( maidenPathname , text ) ;
170+ }
171+ }
172+
173+ return new Response ( pathname , {
174+ status : 303 ,
175+ headers : {
176+ "location" : `${ request . url } .js` ,
177+ } ,
178+ } ) ;
179+ } catch ( error ) {
180+ return new Response ( error . message || error . toString ( ) , { status : 500 } )
88181 }
182+ }
183+
184+ return new Response ( null , {
185+ status : 404 ,
89186 } ) ;
90187}
91188
92- // console.log("Listening on http://localhost:8080");
93- // await listenAndServe(":8080", handler);
94-
95189if ( import . meta. main ) {
96190 const PORT = Deno . env . get ( "PORT" ) || 1729 ;
97191 const timestamp = Date . now ( ) ;
98192 const humanReadableDateTime = new Date ( timestamp ) . toLocaleString ( ) ;
99193 console . log ( 'Current Date: ' , humanReadableDateTime )
100194 console . info ( `Server Listening on http://localhost:${ PORT } ` ) ;
101195 listenAndServe ( `:${ PORT } ` , requestHandler ) ;
102- }
103-
196+ }
0 commit comments