djledda.de main
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

81 lines
2.8 KiB

  1. import { contentType } from "jsr:@std/media-types";
  2. import { transpile } from "jsr:@deno/emit";
  3. import config from "./deno.json" with { type: "json" };
  4. const jsContentType = contentType(".js");
  5. export enum MediaType {
  6. TypeScript,
  7. JSX,
  8. TSX,
  9. };
  10. // https://github.com/denoland/deno_ast/blob/ea1ccec37e1aa8e5e1e70f983a7ed1472d0e132a/src/media_type.rs#L117
  11. const customContentType = {
  12. [MediaType.TypeScript]: "text/typescript; charset=utf-8",
  13. [MediaType.JSX]: "text/jsx; charset=utf-8",
  14. [MediaType.TSX]: "text/tsx; charset=utf-8",
  15. };
  16. async function rewriteTsResponse(response: Response, url: URL, mediaType: MediaType) {
  17. const tsCode = await response.text();
  18. const targetUrlStr = url.toString();
  19. try {
  20. const result = await transpile(url, {
  21. importMap: config,
  22. compilerOptions: config.compilerOptions,
  23. load(specifier) {
  24. if (specifier !== targetUrlStr) {
  25. return Promise.resolve({
  26. kind: "module",
  27. specifier,
  28. content: "",
  29. headers: { "content-type": "application/javascript; charset=utf-8" },
  30. });
  31. } else {
  32. return Promise.resolve({
  33. kind: "module",
  34. specifier,
  35. content: tsCode,
  36. headers: {
  37. "content-type": customContentType[mediaType],
  38. },
  39. });
  40. }
  41. },
  42. });
  43. const jsCode = result.get(targetUrlStr);
  44. const { headers } = response;
  45. headers.set("content-type", jsContentType);
  46. headers.delete("content-length");
  47. return new Response(jsCode, {
  48. status: response.status,
  49. statusText: response.statusText,
  50. headers,
  51. });
  52. } catch (e) {
  53. console.error(e);
  54. return new Response(`${e}`, {
  55. status: 500,
  56. statusText: `${e}`,
  57. headers: response.headers,
  58. });
  59. }
  60. }
  61. export default async function transpileResponse(response: Response, requestUrl: string, filepath?: string): Promise<Response> {
  62. const url = new URL(`ts-serve:///${ requestUrl }`);
  63. if (response.status !== 200) {
  64. return response;
  65. }
  66. const pathname = filepath !== undefined ? filepath : url.pathname;
  67. const extension = pathname.split('.').at(-1) ?? null;
  68. switch (extension) {
  69. case 'ts': return await rewriteTsResponse(response, url, MediaType.TypeScript);
  70. case 'tsx': return await rewriteTsResponse(response, url, MediaType.TSX);
  71. case 'jsx': return await rewriteTsResponse(response, url, MediaType.JSX);
  72. default: return response;
  73. }
  74. }