diff --git a/platform/src/components/aws/ssr-site.ts b/platform/src/components/aws/ssr-site.ts index fe6b53eec..9409d1e1b 100644 --- a/platform/src/components/aws/ssr-site.ts +++ b/platform/src/components/aws/ssr-site.ts @@ -20,13 +20,12 @@ import { Input } from "../input.js"; import { transform, type Prettify, type Transform } from "../component.js"; import { VisibleError } from "../error.js"; import { Cron } from "./cron.js"; -import { BaseSiteFileOptions } from "../base/base-site.js"; +import { BaseSiteFileOptions, getContentType } from "../base/base-site.js"; import { BaseSsrSiteArgs } from "../base/base-ssr-site.js"; import { cloudfront, getPartitionOutput, getRegionOutput, - iam, lambda, types, } from "@pulumi/aws"; @@ -564,51 +563,6 @@ export function createServersAndDistribution( }); } - function getContentType(filename: string, textEncoding: string) { - const ext = filename.endsWith(".well-known/site-association-json") - ? ".json" - : path.extname(filename); - const extensions = { - [".txt"]: { mime: "text/plain", isText: true }, - [".htm"]: { mime: "text/html", isText: true }, - [".html"]: { mime: "text/html", isText: true }, - [".xhtml"]: { mime: "application/xhtml+xml", isText: true }, - [".css"]: { mime: "text/css", isText: true }, - [".js"]: { mime: "text/javascript", isText: true }, - [".mjs"]: { mime: "text/javascript", isText: true }, - [".apng"]: { mime: "image/apng", isText: false }, - [".avif"]: { mime: "image/avif", isText: false }, - [".gif"]: { mime: "image/gif", isText: false }, - [".jpeg"]: { mime: "image/jpeg", isText: false }, - [".jpg"]: { mime: "image/jpeg", isText: false }, - [".png"]: { mime: "image/png", isText: false }, - [".svg"]: { mime: "image/svg+xml", isText: true }, - [".bmp"]: { mime: "image/bmp", isText: false }, - [".tiff"]: { mime: "image/tiff", isText: false }, - [".webp"]: { mime: "image/webp", isText: false }, - [".ico"]: { mime: "image/vnd.microsoft.icon", isText: false }, - [".eot"]: { mime: "application/vnd.ms-fontobject", isText: false }, - [".ttf"]: { mime: "font/ttf", isText: false }, - [".otf"]: { mime: "font/otf", isText: false }, - [".woff"]: { mime: "font/woff", isText: false }, - [".woff2"]: { mime: "font/woff2", isText: false }, - [".json"]: { mime: "application/json", isText: true }, - [".jsonld"]: { mime: "application/ld+json", isText: true }, - [".xml"]: { mime: "application/xml", isText: true }, - [".pdf"]: { mime: "application/pdf", isText: false }, - [".zip"]: { mime: "application/zip", isText: false }, - [".wasm"]: { mime: "application/wasm", isText: false }, - [".webmanifest"]: { mime: "application/manifest+json", isText: true }, - }; - const extensionData = extensions[ext as keyof typeof extensions]; - const mime = extensionData?.mime ?? "application/octet-stream"; - const charset = - extensionData?.isText && textEncoding !== "none" - ? `;charset=${textEncoding}` - : ""; - return `${mime}${charset}`; - } - function createEdgeFunctions() { const functions: Record = {}; diff --git a/platform/src/components/aws/static-site.ts b/platform/src/components/aws/static-site.ts index 556ce5fd4..9651cf386 100644 --- a/platform/src/components/aws/static-site.ts +++ b/platform/src/components/aws/static-site.ts @@ -15,13 +15,14 @@ import { Link } from "../link.js"; import { Input } from "../input.js"; import { globSync } from "glob"; import { BucketFile, BucketFiles } from "./providers/bucket-files.js"; +import { getContentType } from "../base/base-site.js"; import { BaseStaticSiteArgs, BaseStaticSiteAssets, buildApp, prepare, } from "../base/base-static-site.js"; -import { cloudfront, iam, s3 } from "@pulumi/aws"; +import { cloudfront, s3 } from "@pulumi/aws"; import { URL_UNAVAILABLE } from "./linkable.js"; import { DevArgs } from "../dev.js"; import { OriginAccessControl } from "./providers/origin-access-control.js"; @@ -775,7 +776,8 @@ export class StaticSite extends Component implements Link.Linkable { key: path.posix.join(assets.path ?? "", file), hash, cacheControl: fileOption.cacheControl, - contentType: getContentType(file, "UTF-8"), + contentType: + fileOption.contentType ?? getContentType(file, "UTF-8"), }; }), )), @@ -795,50 +797,6 @@ export class StaticSite extends Component implements Link.Linkable { }); } - function getContentType(filename: string, textEncoding: string) { - const ext = filename.endsWith(".well-known/site-association-json") - ? ".json" - : path.extname(filename); - const extensions = { - [".txt"]: { mime: "text/plain", isText: true }, - [".htm"]: { mime: "text/html", isText: true }, - [".html"]: { mime: "text/html", isText: true }, - [".xhtml"]: { mime: "application/xhtml+xml", isText: true }, - [".css"]: { mime: "text/css", isText: true }, - [".js"]: { mime: "text/javascript", isText: true }, - [".mjs"]: { mime: "text/javascript", isText: true }, - [".apng"]: { mime: "image/apng", isText: false }, - [".avif"]: { mime: "image/avif", isText: false }, - [".gif"]: { mime: "image/gif", isText: false }, - [".jpeg"]: { mime: "image/jpeg", isText: false }, - [".jpg"]: { mime: "image/jpeg", isText: false }, - [".png"]: { mime: "image/png", isText: false }, - [".svg"]: { mime: "image/svg+xml", isText: true }, - [".bmp"]: { mime: "image/bmp", isText: false }, - [".tiff"]: { mime: "image/tiff", isText: false }, - [".webp"]: { mime: "image/webp", isText: false }, - [".ico"]: { mime: "image/vnd.microsoft.icon", isText: false }, - [".eot"]: { mime: "application/vnd.ms-fontobject", isText: false }, - [".ttf"]: { mime: "font/ttf", isText: false }, - [".otf"]: { mime: "font/otf", isText: false }, - [".woff"]: { mime: "font/woff", isText: false }, - [".woff2"]: { mime: "font/woff2", isText: false }, - [".json"]: { mime: "application/json", isText: true }, - [".jsonld"]: { mime: "application/ld+json", isText: true }, - [".xml"]: { mime: "application/xml", isText: true }, - [".pdf"]: { mime: "application/pdf", isText: false }, - [".zip"]: { mime: "application/zip", isText: false }, - [".wasm"]: { mime: "application/wasm", isText: false }, - }; - const extensionData = extensions[ext as keyof typeof extensions]; - const mime = extensionData?.mime ?? "application/octet-stream"; - const charset = - extensionData?.isText && textEncoding !== "none" - ? `;charset=${textEncoding}` - : ""; - return `${mime}${charset}`; - } - function createDistribution() { return new Cdn( ...transform( diff --git a/platform/src/components/base/base-site.ts b/platform/src/components/base/base-site.ts index 076ad6df9..abb77a63c 100644 --- a/platform/src/components/base/base-site.ts +++ b/platform/src/components/base/base-site.ts @@ -1,3 +1,4 @@ +import path from "path"; import { Semaphore } from "../../util/semaphore"; export interface BaseSiteFileOptions { @@ -23,3 +24,48 @@ export interface BaseSiteFileOptions { export const limiter = new Semaphore( parseInt(process.env.SST_SITE_BUILD_CONCURRENCY || "4"), ); + +export function getContentType(filename: string, textEncoding: string) { + const ext = filename.endsWith(".well-known/site-association-json") + ? ".json" + : path.extname(filename); + const extensions = { + [".txt"]: { mime: "text/plain", isText: true }, + [".htm"]: { mime: "text/html", isText: true }, + [".html"]: { mime: "text/html", isText: true }, + [".xhtml"]: { mime: "application/xhtml+xml", isText: true }, + [".css"]: { mime: "text/css", isText: true }, + [".js"]: { mime: "text/javascript", isText: true }, + [".mjs"]: { mime: "text/javascript", isText: true }, + [".apng"]: { mime: "image/apng", isText: false }, + [".avif"]: { mime: "image/avif", isText: false }, + [".gif"]: { mime: "image/gif", isText: false }, + [".jpeg"]: { mime: "image/jpeg", isText: false }, + [".jpg"]: { mime: "image/jpeg", isText: false }, + [".png"]: { mime: "image/png", isText: false }, + [".svg"]: { mime: "image/svg+xml", isText: true }, + [".bmp"]: { mime: "image/bmp", isText: false }, + [".tiff"]: { mime: "image/tiff", isText: false }, + [".webp"]: { mime: "image/webp", isText: false }, + [".ico"]: { mime: "image/vnd.microsoft.icon", isText: false }, + [".eot"]: { mime: "application/vnd.ms-fontobject", isText: false }, + [".ttf"]: { mime: "font/ttf", isText: false }, + [".otf"]: { mime: "font/otf", isText: false }, + [".woff"]: { mime: "font/woff", isText: false }, + [".woff2"]: { mime: "font/woff2", isText: false }, + [".json"]: { mime: "application/json", isText: true }, + [".jsonld"]: { mime: "application/ld+json", isText: true }, + [".xml"]: { mime: "application/xml", isText: true }, + [".pdf"]: { mime: "application/pdf", isText: false }, + [".zip"]: { mime: "application/zip", isText: false }, + [".wasm"]: { mime: "application/wasm", isText: false }, + [".webmanifest"]: { mime: "application/manifest+json", isText: true }, + }; + const extensionData = extensions[ext as keyof typeof extensions]; + const mime = extensionData?.mime ?? "application/octet-stream"; + const charset = + extensionData?.isText && textEncoding !== "none" + ? `;charset=${textEncoding}` + : ""; + return `${mime}${charset}`; +} diff --git a/platform/src/components/cloudflare/ssr-site.ts b/platform/src/components/cloudflare/ssr-site.ts index b0c2ed147..9b29bdf02 100644 --- a/platform/src/components/cloudflare/ssr-site.ts +++ b/platform/src/components/cloudflare/ssr-site.ts @@ -6,7 +6,7 @@ import { Output, Unwrap, output, all, ComponentResource } from "@pulumi/pulumi"; import { Input } from "../input.js"; import { transform, type Transform } from "../component.js"; import { VisibleError } from "../error.js"; -import { BaseSiteFileOptions } from "../base/base-site.js"; +import { BaseSiteFileOptions, getContentType } from "../base/base-site.js"; import { BaseSsrSiteArgs } from "../base/base-ssr-site.js"; import { Kv, KvArgs } from "./kv.js"; import { Worker, WorkerArgs } from "./worker.js"; @@ -142,7 +142,8 @@ export function createRouter( key: path.posix.join(copy.to, file), hash, cacheControl: fileOption.cacheControl, - contentType: getContentType(file, "UTF-8"), + contentType: + fileOption.contentType ?? getContentType(file, "UTF-8"), }; }), )), @@ -173,50 +174,6 @@ export function createRouter( ); } - function getContentType(filename: string, textEncoding: string) { - const ext = filename.endsWith(".well-known/site-association-json") - ? ".json" - : path.extname(filename); - const extensions = { - [".txt"]: { mime: "text/plain", isText: true }, - [".htm"]: { mime: "text/html", isText: true }, - [".html"]: { mime: "text/html", isText: true }, - [".xhtml"]: { mime: "application/xhtml+xml", isText: true }, - [".css"]: { mime: "text/css", isText: true }, - [".js"]: { mime: "text/javascript", isText: true }, - [".mjs"]: { mime: "text/javascript", isText: true }, - [".apng"]: { mime: "image/apng", isText: false }, - [".avif"]: { mime: "image/avif", isText: false }, - [".gif"]: { mime: "image/gif", isText: false }, - [".jpeg"]: { mime: "image/jpeg", isText: false }, - [".jpg"]: { mime: "image/jpeg", isText: false }, - [".png"]: { mime: "image/png", isText: false }, - [".svg"]: { mime: "image/svg+xml", isText: true }, - [".bmp"]: { mime: "image/bmp", isText: false }, - [".tiff"]: { mime: "image/tiff", isText: false }, - [".webp"]: { mime: "image/webp", isText: false }, - [".ico"]: { mime: "image/vnd.microsoft.icon", isText: false }, - [".eot"]: { mime: "application/vnd.ms-fontobject", isText: false }, - [".ttf"]: { mime: "font/ttf", isText: false }, - [".otf"]: { mime: "font/otf", isText: false }, - [".woff"]: { mime: "font/woff", isText: false }, - [".woff2"]: { mime: "font/woff2", isText: false }, - [".json"]: { mime: "application/json", isText: true }, - [".jsonld"]: { mime: "application/ld+json", isText: true }, - [".xml"]: { mime: "application/xml", isText: true }, - [".pdf"]: { mime: "application/pdf", isText: false }, - [".zip"]: { mime: "application/zip", isText: false }, - [".wasm"]: { mime: "application/wasm", isText: false }, - }; - const extensionData = extensions[ext as keyof typeof extensions]; - const mime = extensionData?.mime ?? "application/octet-stream"; - const charset = - extensionData?.isText && textEncoding !== "none" - ? `;charset=${textEncoding}` - : ""; - return `${mime}${charset}`; - } - function createServerWorker() { return new Worker( `${name}Server`, diff --git a/platform/src/components/cloudflare/static-site.ts b/platform/src/components/cloudflare/static-site.ts index f60617141..2e16d055b 100644 --- a/platform/src/components/cloudflare/static-site.ts +++ b/platform/src/components/cloudflare/static-site.ts @@ -1,7 +1,7 @@ import fs from "fs"; import path from "path"; import crypto from "crypto"; -import { ComponentResourceOptions, Output, all } from "@pulumi/pulumi"; +import { ComponentResourceOptions, all } from "@pulumi/pulumi"; import { Kv, KvArgs } from "./kv.js"; import { Component, Transform, transform } from "../component.js"; import { Link } from "../link.js"; @@ -9,6 +9,7 @@ import { Input } from "../input.js"; import { globSync } from "glob"; import { KvData } from "./providers/kv-data.js"; import { Worker } from "./worker.js"; +import { getContentType } from "../base/base-site.js"; import { BaseStaticSiteArgs, BaseStaticSiteAssets, @@ -339,7 +340,8 @@ export class StaticSite extends Component implements Link.Linkable { key: file, hash, cacheControl: fileOption.cacheControl, - contentType: getContentType(file, "UTF-8"), + contentType: + fileOption.contentType ?? getContentType(file, "UTF-8"), }; }), )), @@ -371,50 +373,6 @@ export class StaticSite extends Component implements Link.Linkable { ); } - function getContentType(filename: string, textEncoding: string) { - const ext = filename.endsWith(".well-known/site-association-json") - ? ".json" - : path.extname(filename); - const extensions = { - [".txt"]: { mime: "text/plain", isText: true }, - [".htm"]: { mime: "text/html", isText: true }, - [".html"]: { mime: "text/html", isText: true }, - [".xhtml"]: { mime: "application/xhtml+xml", isText: true }, - [".css"]: { mime: "text/css", isText: true }, - [".js"]: { mime: "text/javascript", isText: true }, - [".mjs"]: { mime: "text/javascript", isText: true }, - [".apng"]: { mime: "image/apng", isText: false }, - [".avif"]: { mime: "image/avif", isText: false }, - [".gif"]: { mime: "image/gif", isText: false }, - [".jpeg"]: { mime: "image/jpeg", isText: false }, - [".jpg"]: { mime: "image/jpeg", isText: false }, - [".png"]: { mime: "image/png", isText: false }, - [".svg"]: { mime: "image/svg+xml", isText: true }, - [".bmp"]: { mime: "image/bmp", isText: false }, - [".tiff"]: { mime: "image/tiff", isText: false }, - [".webp"]: { mime: "image/webp", isText: false }, - [".ico"]: { mime: "image/vnd.microsoft.icon", isText: false }, - [".eot"]: { mime: "application/vnd.ms-fontobject", isText: false }, - [".ttf"]: { mime: "font/ttf", isText: false }, - [".otf"]: { mime: "font/otf", isText: false }, - [".woff"]: { mime: "font/woff", isText: false }, - [".woff2"]: { mime: "font/woff2", isText: false }, - [".json"]: { mime: "application/json", isText: true }, - [".jsonld"]: { mime: "application/ld+json", isText: true }, - [".xml"]: { mime: "application/xml", isText: true }, - [".pdf"]: { mime: "application/pdf", isText: false }, - [".zip"]: { mime: "application/zip", isText: false }, - [".wasm"]: { mime: "application/wasm", isText: false }, - }; - const extensionData = extensions[ext as keyof typeof extensions]; - const mime = extensionData?.mime ?? "application/octet-stream"; - const charset = - extensionData?.isText && textEncoding !== "none" - ? `;charset=${textEncoding}` - : ""; - return `${mime}${charset}`; - } - function createRouter() { return new Worker( `${name}Router`,