fix: Make js assets cachable (#3337)

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: pandeymangg <anshuman.pandey9999@gmail.com>
Co-authored-by: Anshuman Pandey <54475686+pandeymangg@users.noreply.github.com>
This commit is contained in:
Jonas Höbenreich
2024-10-11 11:45:57 +02:00
committed by GitHub
parent f1b9a82192
commit 3be72007fa
17 changed files with 107 additions and 89 deletions

2
.gitignore vendored
View File

@@ -58,3 +58,5 @@ packages/lib/uploads
# Vite Timestamps
*vite.config.*.timestamp-*
# js compiled assets
apps/web/public/js

View File

@@ -43,7 +43,7 @@ All you need to do is copy a `<script>` tag to your HTML head, and thats abou
var apiHost = "https://app.formbricks.com";
var environmentId = "<your-environment-id>";
var userId = "<your-user-id>"; //optional
var t=document.createElement("script");t.type="text/javascript",t.async=!0,t.src=apiHost+"/api/packages/js";var e=document.getElementsByTagName("script")[0];e.parentNode.insertBefore(t,e),setTimeout(function(){window.formbricks.init({environmentId: environmentId, apiHost: apiHost, userId: userId})},500)}();
var t=document.createElement("script");t.type="text/javascript",t.async=!0,t.src=apiHost+"/js/formbricks.umd.cjs";var e=document.getElementsByTagName("script")[0];e.parentNode.insertBefore(t,e),setTimeout(function(){window.formbricks.init({environmentId: environmentId, apiHost: apiHost, userId: userId})},500)}();
</script>
<!-- END Formbricks Surveys -->
```

View File

@@ -34,7 +34,7 @@ export const OnboardingSetupInstructions = ({
var apiHost = "${webAppUrl}";
var environmentId = "${environmentId}";
var userId = "testUser";
var t=document.createElement("script");t.type="text/javascript",t.async=!0,t.src=apiHost+"/api/packages/js";var e=document.getElementsByTagName("script")[0];e.parentNode.insertBefore(t,e),setTimeout(function(){window.formbricks.init({environmentId: environmentId, apiHost: apiHost, userId: userId})},500)}();
var t=document.createElement("script");t.type="text/javascript",t.async=!0,t.src=apiHost+"/js/formbricks.umd.cjs";var e=document.getElementsByTagName("script")[0];e.parentNode.insertBefore(t,e),setTimeout(function(){window.formbricks.init({environmentId: environmentId, apiHost: apiHost, userId: userId})},500)}();
</script>
<!-- END Formbricks Surveys -->
`;
@@ -44,7 +44,7 @@ export const OnboardingSetupInstructions = ({
!function(){
var apiHost = "${webAppUrl}";
var environmentId = "${environmentId}";
var t=document.createElement("script");t.type="text/javascript",t.async=!0,t.src=apiHost+"/api/packages/js";var e=document.getElementsByTagName("script")[0];e.parentNode.insertBefore(t,e),setTimeout(function(){window.formbricks.init({environmentId: environmentId, apiHost: apiHost})},500)}();
var t=document.createElement("script");t.type="text/javascript",t.async=!0,t.src=apiHost+"/js/formbricks.umd.cjs";var e=document.getElementsByTagName("script")[0];e.parentNode.insertBefore(t,e),setTimeout(function(){window.formbricks.init({environmentId: environmentId, apiHost: apiHost})},500)}();
</script>
<!-- END Formbricks Surveys -->
`;

View File

@@ -122,7 +122,7 @@ if (typeof window !== "undefined") {
</p>
<CodeBlock language="js">{`<!-- START Formbricks Surveys -->
<script type="text/javascript">
!function(){var t=document.createElement("script");t.type="text/javascript",t.async=!0,t.src="${webAppUrl}/api/packages/js";var e=document.getElementsByTagName("script")[0];e.parentNode.insertBefore(t,e),setTimeout(function(){window.formbricks.init({environmentId: "${environmentId}", apiHost: "${window.location.protocol}//${window.location.host}"})},500)}();
!function(){var t=document.createElement("script");t.type="text/javascript",t.async=!0,t.src="${webAppUrl}/js/formbricks.umd.cjs";var e=document.getElementsByTagName("script")[0];e.parentNode.insertBefore(t,e),setTimeout(function(){window.formbricks.init({environmentId: "${environmentId}", apiHost: "${window.location.protocol}//${window.location.host}"})},500)}();
</script>
<!-- END Formbricks Surveys -->`}</CodeBlock>
<h4>Step 2: Debug mode</h4>

View File

@@ -1,21 +0,0 @@
// DEPRECATED - This file is deprecated and will be removed in the future
// Deprecated since 22-03-2024
// This endpoint has been deprecated. Please use the new endpoint /api/packages/js instead.
import { responses } from "@/app/lib/api/response";
import { WEBAPP_URL } from "@formbricks/lib/constants";
export const GET = async () => {
try {
return responses.goneResponse(
"This endpoint has been deprecated. Please use the new endpoint /api/packages/<package-name>",
{
"x-deprecated": "true",
"x-deprecated-date": "22-03-2024",
"x-deprecated-redirect": `${WEBAPP_URL}/api/packages/js`,
},
true
);
} catch (error) {
return responses.internalServerErrorResponse("this endpoint is not available");
}
};

View File

@@ -1,52 +0,0 @@
import { responses } from "@/app/lib/api/response";
import fs from "fs/promises";
import { NextRequest } from "next/server";
export const GET = async (_: NextRequest, { params }: { params: { package: string } }) => {
let path: string;
const packageRequested = params.package;
switch (packageRequested) {
case "js":
path = `../../packages/js-core/dist/index.umd.cjs`;
break;
case "surveys":
path = `../../packages/surveys/dist/index.umd.cjs`;
break;
default:
return responses.notFoundResponse(
"package",
packageRequested,
true,
"public, max-age=600, s-maxage=600, stale-while-revalidate=600, stale-if-error=600" // 10 minutes cache for not found
);
}
try {
const packageSrcCode = await fs.readFile(path, "utf-8");
return new Response(packageSrcCode, {
headers: {
"Content-Type": "application/javascript",
"Cache-Control":
"public, max-age=3600, s-maxage=604800, stale-while-revalidate=3600, stale-if-error=3600",
"Access-Control-Allow-Origin": "*",
},
});
} catch (error: any) {
if (error.code === "ENOENT") {
return responses.notFoundResponse(
"package",
packageRequested,
true,
"public, max-age=600, s-maxage=600, stale-while-revalidate=600, stale-if-error=600" // 10 minutes cache for file not found errors
);
} else {
console.error("Error reading file:", error);
return responses.internalServerErrorResponse(
"internal server error",
true,
"public, max-age=600, s-maxage=600, stale-while-revalidate=600, stale-if-error=600" // 10 minutes cache for internal errors
);
}
}
};

View File

@@ -163,17 +163,61 @@ const nextConfig = {
},
],
},
{
source: "/js/(.*)",
headers: [
{
key: "Cache-Control",
value: "public, max-age=3600, s-maxage=604800, stale-while-revalidate=3600, stale-if-error=3600",
},
{
key: "Content-Type",
value: "application/javascript; charset=UTF-8",
},
{
key: "Access-Control-Allow-Origin",
value: "*",
},
],
},
// headers for /api/packages/(.*) -- the api route does not exist, but we still need the headers for the rewrites to work correctly!
{
source: "/api/packages/(.*)",
headers: [
{
key: "Cache-Control",
value: "public, max-age=3600, s-maxage=604800, stale-while-revalidate=3600, stale-if-error=3600",
},
{
key: "Content-Type",
value: "application/javascript; charset=UTF-8",
},
{
key: "Access-Control-Allow-Origin",
value: "*",
},
],
},
];
},
async rewrites() {
return [
{
source: "/api/packages/website",
destination: "/api/packages/js",
destination: "/js/formbricks.umd.cjs",
},
{
source: "/api/packages/app",
destination: "/api/packages/js",
destination: "/js/formbricks.umd.cjs",
},
{
source: "/api/packages/js",
destination: "/js/formbricks.umd.cjs",
},
{
source: "/api/packages/surveys",
destination: "/js/surveys.umd.cjs",
},
{
source: "/api/v1/client/:environmentId/website/environment",

View File

@@ -0,0 +1,40 @@
import fs from "fs-extra";
import path from "path";
import { Plugin, ResolvedConfig } from "vite";
interface CopyCompiledAssetsPluginOptions {
filename: string;
distDir: string;
}
export function copyCompiledAssetsPlugin(options: CopyCompiledAssetsPluginOptions): Plugin {
let config: ResolvedConfig;
return {
name: "copy-compiled-assets",
apply: "build",
configResolved(_config) {
config = _config;
},
async writeBundle() {
const outputDir = path.resolve(config.root, "../../apps/web/public/js");
const distDir = path.resolve(config.root, options.distDir);
// Create the output directory if it doesn't exist
fs.ensureDirSync(outputDir);
console.log(`Ensured directory exists: ${outputDir}`);
// Copy files from distDir to outputDir
const filesToCopy = fs.readdirSync(distDir);
filesToCopy.forEach((file) => {
const srcFile = path.resolve(distDir, file);
const destFile = path.resolve(outputDir, file.replace("index", options.filename));
fs.copyFileSync(srcFile, destFile);
});
console.log(`Copied ${filesToCopy.length} files to ${outputDir} (${options.filename})`);
},
};
}

View File

@@ -61,8 +61,6 @@ export const fetchPersonState = async (
const data = await response.json();
const { data: state } = data;
console.log("Person state fetched", state);
const defaultPersonState: TJsPersonState = {
expiresAt: new Date(new Date().getTime() + 1000 * 60 * 30), // 30 minutes
data: {

View File

@@ -268,7 +268,7 @@ const loadFormbricksSurveysExternally = (): Promise<typeof window.formbricksSurv
resolve(window.formbricksSurveys);
} else {
const script = document.createElement("script");
script.src = `${config.get().apiHost}/api/packages/surveys`;
script.src = `${config.get().apiHost}/js/surveys.umd.cjs`;
script.async = true;
script.onload = () => resolve(window.formbricksSurveys);
script.onerror = (error) => {

View File

@@ -2,6 +2,7 @@ import { resolve } from "path";
import { defineConfig } from "vite";
import dts from "vite-plugin-dts";
import webPackageJson from "../../apps/web/package.json";
import { copyCompiledAssetsPlugin } from "../copyCompiledAssetsPlugin/vite.config";
const config = () => {
return defineConfig({
@@ -27,6 +28,7 @@ const config = () => {
rollupTypes: true,
bundledPackages: ["@formbricks/api", "@formbricks/types"],
}),
copyCompiledAssetsPlugin({ filename: "formbricks", distDir: resolve(__dirname, "dist") }),
],
});
};

View File

@@ -2,7 +2,7 @@
<script type="text/javascript">
!(function () {
var t = document.createElement("script");
(t.type = "text/javascript"), (t.async = !0), (t.src = "http://localhost:3000/api/packages/js");
(t.type = "text/javascript"), (t.async = !0), (t.src = "http://localhost:3000/js/formbricks.umd.cjs");
var e = document.getElementsByTagName("script")[0];
e.parentNode.insertBefore(t, e),
setTimeout(function () {

View File

@@ -1,7 +1,7 @@
{
"name": "@formbricks/js",
"license": "MIT",
"version": "3.0.0",
"version": "3.0.1",
"description": "Formbricks-js allows you to connect your index to Formbricks, display surveys and trigger events.",
"homepage": "https://formbricks.com",
"repository": {

View File

@@ -10,7 +10,7 @@ let isInitialized = false;
// Load the SDK, return the result
const loadFormbricksSDK = async (apiHostParam: string): Promise<Result<void>> => {
if (!window.formbricks) {
const res = await fetch(`${apiHostParam}/api/packages/js`);
const res = await fetch(`${apiHostParam}/js/formbricks.umd.cjs`);
// Failed to fetch the app package
if (!res.ok) {
@@ -56,7 +56,6 @@ const loadFormbricksSDK = async (apiHostParam: string): Promise<Result<void>> =>
const functionsToProcess: { prop: string; args: unknown[] }[] = [];
export const loadFormbricksToProxy = async (prop: string, ...args: unknown[]): Promise<void> => {
console.log(args);
// all of this should happen when not initialized:
if (!isInitialized) {
if (prop === "init") {

View File

@@ -373,7 +373,7 @@ const renderHtml = (options: Partial<SurveyInlineProps> & { apiHost?: string }):
}
const script = document.createElement("script");
script.src = "${options.apiHost ?? "http://localhost:3000"}/api/packages/surveys";
script.src = "${options.apiHost ?? "http://localhost:3000"}/js/surveys.umd.cjs";
script.async = true;
script.onload = () => loadSurvey();
script.onerror = (error) => {

View File

@@ -3,6 +3,7 @@ import { resolve } from "path";
import { defineConfig, loadEnv } from "vite";
import dts from "vite-plugin-dts";
import tsconfigPaths from "vite-tsconfig-paths";
import { copyCompiledAssetsPlugin } from "../copyCompiledAssetsPlugin/vite.config";
const config = ({ mode }) => {
const env = loadEnv(mode, process.cwd(), "");
@@ -26,7 +27,12 @@ const config = ({ mode }) => {
fileName: "index",
},
},
plugins: [preact(), dts({ rollupTypes: true }), tsconfigPaths()],
plugins: [
preact(),
dts({ rollupTypes: true }),
tsconfigPaths(),
copyCompiledAssetsPlugin({ filename: "surveys", distDir: resolve(__dirname, "dist") }),
],
});
};

View File

@@ -21,7 +21,7 @@ export const SurveyInline = (props: Omit<SurveyInlineProps, "containerId">) => {
const loadSurveyScript: () => Promise<void> = async () => {
try {
const response = await fetch("/api/packages/surveys");
const response = await fetch("/js/surveys.umd.cjs");
if (!response.ok) {
throw new Error("Failed to load the surveys package");