Embed Code Generator: Fix Preview HTML and Embed Lib path for production (#2688)
* Improve logging * Improve logging * Keep embed origin conigurable * Make embed URL and embed origin conigurable through env * Gitignore public embed * Add fingerprint to preview as well * Fix path Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
This commit is contained in:
parent
83ec6d69eb
commit
67cc3a6409
17 changed files with 180 additions and 117 deletions
|
@ -25,6 +25,7 @@ NEXT_PUBLIC_LICENSE_CONSENT=''
|
|||
NEXT_PUBLIC_WEBAPP_URL='http://localhost:3000'
|
||||
# Change to 'http://localhost:3001' if running the website simultaneously
|
||||
NEXT_PUBLIC_WEBSITE_URL='http://localhost:3000'
|
||||
NEXT_PUBLIC_EMBED_LIB_URL='http://localhost:3000/embed/embed.js'
|
||||
|
||||
# To enable SAML login, set both these variables
|
||||
# @see https://github.com/calcom/cal.com/tree/main/packages/ee#setting-up-saml-login
|
||||
|
|
3
apps/web/.gitignore
vendored
3
apps/web/.gitignore
vendored
|
@ -61,3 +61,6 @@ yarn-error.log*
|
|||
|
||||
# Typescript
|
||||
tsconfig.tsbuildinfo
|
||||
|
||||
# Autogenerated embed content
|
||||
public/embed
|
||||
|
|
|
@ -12,6 +12,7 @@ import { Button, Switch } from "@calcom/ui";
|
|||
import { Dialog, DialogContent, DialogClose } from "@calcom/ui/Dialog";
|
||||
import { InputLeading, Label, TextArea, TextField } from "@calcom/ui/form/fields";
|
||||
|
||||
import { WEBAPP_URL, EMBED_LIB_URL } from "@lib/config/constants";
|
||||
import { trpc } from "@lib/trpc";
|
||||
|
||||
import NavTabs from "@components/NavTabs";
|
||||
|
@ -216,16 +217,10 @@ const embeds: {
|
|||
];
|
||||
|
||||
function getEmbedSnippetString() {
|
||||
let embedJsUrl = "https://cal.com/embed.js";
|
||||
let isLocal = false;
|
||||
if (location.hostname === "localhost") {
|
||||
embedJsUrl = "http://localhost:3100/dist/embed.umd.js";
|
||||
isLocal = true;
|
||||
}
|
||||
// TODO: Import this string from @calcom/embed-snippet
|
||||
return `
|
||||
(function (C, A, L) { let p = function (a, ar) { a.q.push(ar); }; let d = C.document; C.Cal = C.Cal || function () { let cal = C.Cal; let ar = arguments; if (!cal.loaded) { cal.ns = {}; cal.q = cal.q || []; d.head.appendChild(d.createElement("script")).src = A; cal.loaded = true; } if (ar[0] === L) { const api = function () { p(api, arguments); }; const namespace = ar[1]; api.q = api.q || []; typeof namespace === "string" ? (cal.ns[namespace] = api) && p(api, ar) : p(cal, ar); return; } p(cal, ar); }; })(window, "${embedJsUrl}", "init");
|
||||
Cal("init"${isLocal ? ', {origin:"http://localhost:3000/"}' : ""});
|
||||
(function (C, A, L) { let p = function (a, ar) { a.q.push(ar); }; let d = C.document; C.Cal = C.Cal || function () { let cal = C.Cal; let ar = arguments; if (!cal.loaded) { cal.ns = {}; cal.q = cal.q || []; d.head.appendChild(d.createElement("script")).src = A; cal.loaded = true; } if (ar[0] === L) { const api = function () { p(api, arguments); }; const namespace = ar[1]; api.q = api.q || []; typeof namespace === "string" ? (cal.ns[namespace] = api) && p(api, ar) : p(cal, ar); return; } p(cal, ar); }; })(window, "${EMBED_LIB_URL}", "init");
|
||||
Cal("init", {origin:"${WEBAPP_URL}"});
|
||||
`;
|
||||
}
|
||||
|
||||
|
@ -815,7 +810,7 @@ ${getEmbedTypeSpecificString().trim()}
|
|||
className="border-1 h-[75vh] border"
|
||||
width="100%"
|
||||
height="100%"
|
||||
src={`http://localhost:3100/preview.html?embedType=${embedType}&calLink=${calLink}`}
|
||||
src={`${WEBAPP_URL}/embed/preview.html?embedType=${embedType}&calLink=${calLink}`}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -44,7 +44,7 @@
|
|||
}
|
||||
p(cal, ar);
|
||||
};
|
||||
})(window, "//localhost:3100/dist/embed.umd.js", "init");
|
||||
})(window, "//localhost:3000/embed/embed.js", "init");
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
|
|
@ -4,11 +4,13 @@
|
|||
"description": "This is the vanilla JS core script that embeds Cal Link",
|
||||
"main": "./index.ts",
|
||||
"scripts": {
|
||||
"build": "NEXT_PUBLIC_EMBED_FINGER_PRINT=$(git rev-parse --short HEAD) vite build && cp dist/embed.umd.js ../../../apps/website/public/embed.js && echo 'You need to commit the newly generated embed.js in apps/website'",
|
||||
"build:cal": "NEXT_PUBLIC_WEBSITE_URL='https://cal.com' yarn build",
|
||||
"__build": "yarn tailwind && vite build",
|
||||
"__dev": "yarn __build --mode development",
|
||||
"build": "NEXT_PUBLIC_EMBED_FINGER_PRINT=$(git rev-parse --short HEAD) yarn __build",
|
||||
"build-preview": "PREVIEW_BUILD=1 yarn __build ",
|
||||
"vite": "vite",
|
||||
"tailwind": "yarn tailwindcss -i ./src/styles.css -o ./src/tailwind.generated.css",
|
||||
"buildWatchAndServer": "run-p 'build --watch' 'vite --port 3100 --strict-port --open'",
|
||||
"buildWatchAndServer": "run-p '__dev' 'vite --port 3100 --strict-port --open'",
|
||||
"dev": "yarn tailwind && run-p 'tailwind --watch' 'buildWatchAndServer'",
|
||||
"dev-real": "vite dev --port 3100",
|
||||
"type-check": "tsc --pretty --noEmit",
|
||||
|
|
|
@ -1,80 +1,29 @@
|
|||
<html>
|
||||
<head>
|
||||
<style>
|
||||
.row {
|
||||
display: flex;
|
||||
}
|
||||
.cell-1 {
|
||||
border-right: 1px solid #ded9d9;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.cell-2 {
|
||||
margin: 10px;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
(function (C, A, L) {
|
||||
let p = function (a, ar) {
|
||||
a.q.push(ar);
|
||||
};
|
||||
let d = C.document;
|
||||
C.Cal =
|
||||
C.Cal ||
|
||||
function () {
|
||||
let cal = C.Cal;
|
||||
let ar = arguments;
|
||||
if (!cal.loaded) {
|
||||
cal.ns = {};
|
||||
cal.q = cal.q || [];
|
||||
d.head.appendChild(d.createElement("script")).src = A;
|
||||
cal.loaded = true;
|
||||
}
|
||||
if (ar[0] === L) {
|
||||
const api = function () {
|
||||
p(api, arguments);
|
||||
};
|
||||
const namespace = ar[1];
|
||||
api.q = api.q || [];
|
||||
typeof namespace === "string" ? (cal.ns[namespace] = api) && p(api, ar) : p(cal, ar);
|
||||
return;
|
||||
}
|
||||
p(cal, ar);
|
||||
};
|
||||
})(window, "//localhost:3100/dist/embed.umd.js", "init");
|
||||
Cal("init", {
|
||||
origin: "http://localhost:3000",
|
||||
});
|
||||
const searchParams = new URL(document.URL).searchParams;
|
||||
const embedType = searchParams.get("embedType");
|
||||
const calLink = searchParams.get("calLink");
|
||||
</script>
|
||||
<style>
|
||||
.row {
|
||||
display:flex;
|
||||
}
|
||||
.cell-1 {
|
||||
border-right:1px solid #ded9d9;
|
||||
padding-right:10px;
|
||||
}
|
||||
|
||||
.cell-2 {
|
||||
margin:10px;
|
||||
}
|
||||
|
||||
</style>
|
||||
<script>
|
||||
const searchParams= new URL(document.URL).searchParams;
|
||||
const embedType = searchParams.get("embedType");
|
||||
const calLink = searchParams.get("calLink");
|
||||
</script>
|
||||
</head>
|
||||
<script type="module" src="./src/preview.ts"></script>
|
||||
<body>
|
||||
<div id="my-embed" style="width:100%;height:100%;overflow:scroll"></div>
|
||||
<script>
|
||||
if (embedType === "inline") {
|
||||
Cal("inline", {
|
||||
elementOrSelector: "#my-embed",
|
||||
calLink,
|
||||
});
|
||||
} else if (embedType === "floating-popup") {
|
||||
Cal("floatingButton", {
|
||||
calLink,
|
||||
attributes: {
|
||||
id: "my-floating-button"
|
||||
}
|
||||
});
|
||||
} else if (embedType === "element-click") {
|
||||
const button = document.createElement('button')
|
||||
button.setAttribute("data-cal-link", calLink)
|
||||
button.innerHTML = 'I am a button that exists on your website'
|
||||
document.body.appendChild(button);
|
||||
}
|
||||
<div id="my-embed" style="width: 100%; height: 100%; overflow: scroll"></div>
|
||||
<script type="module">
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -7,6 +7,16 @@ export interface UiConfig {
|
|||
theme?: "dark" | "light" | "auto";
|
||||
styles?: EmbedStyles;
|
||||
}
|
||||
declare global {
|
||||
interface Window {
|
||||
CalEmbed: {
|
||||
__logQueue?: any[];
|
||||
embedStore: any;
|
||||
};
|
||||
CalComPageStatus: string;
|
||||
CalComPlan: string;
|
||||
}
|
||||
}
|
||||
|
||||
const embedStore = {
|
||||
// Store all embed styles here so that as and when new elements are mounted, styles can be applied to it.
|
||||
|
@ -37,6 +47,9 @@ if (isBrowser) {
|
|||
if (isSafariBrowser) {
|
||||
log("Safari Detected: Using setTimeout instead of rAF");
|
||||
}
|
||||
window.CalEmbed = window.CalEmbed || {};
|
||||
//TODO: Send postMessage to parent to get all log messages in the same queue.
|
||||
window.CalEmbed.embedStore = embedStore;
|
||||
}
|
||||
|
||||
function runAsap(fn: (...arg: any) => void) {
|
||||
|
@ -47,23 +60,11 @@ function runAsap(fn: (...arg: any) => void) {
|
|||
return requestAnimationFrame(fn);
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
CalEmbed: {
|
||||
__logQueue?: any[];
|
||||
};
|
||||
CalComPageStatus: string;
|
||||
CalComPlan: string;
|
||||
}
|
||||
}
|
||||
|
||||
function log(...args: any[]) {
|
||||
if (isBrowser) {
|
||||
const namespace = getNamespace();
|
||||
|
||||
const searchParams = new URL(document.URL).searchParams;
|
||||
//TODO: Send postMessage to parent to get all log messages in the same queue.
|
||||
window.CalEmbed = window.CalEmbed || {};
|
||||
const logQueue = (window.CalEmbed.__logQueue = window.CalEmbed.__logQueue || []);
|
||||
args.push({
|
||||
ns: namespace,
|
||||
|
|
|
@ -8,6 +8,12 @@ import css from "./embed.css";
|
|||
import { SdkActionManager } from "./sdk-action-manager";
|
||||
import allCss from "./tailwind.generated.css";
|
||||
|
||||
// HACK: Redefine and don't import WEBAPP_URL as it causes import statement to be present in built file.
|
||||
// This is happening because we are not able to generate an App and a lib using single Vite Config.
|
||||
const WEBAPP_URL =
|
||||
(import.meta.env.NEXT_PUBLIC_WEBAPP_URL_TYPO as string) ||
|
||||
`https://${import.meta.env.NEXT_PUBLIC_VERCEL_URL}`;
|
||||
|
||||
customElements.define("cal-modal-box", ModalBox);
|
||||
customElements.define("cal-floating-button", FloatingButton);
|
||||
customElements.define("cal-inline", Inline);
|
||||
|
@ -414,8 +420,8 @@ export class Cal {
|
|||
|
||||
constructor(namespace: string, q: InstructionQueue) {
|
||||
this.__config = {
|
||||
// Keep cal.com hardcoded till the time embed.js deployment to cal.com/embed.js is automated. This is to prevent accidentally pushing of localhost domain to production
|
||||
origin: /*import.meta.env.NEXT_PUBLIC_WEBSITE_URL || */ "https://app.cal.com",
|
||||
// Use WEBAPP_URL till full page reload problem with website URL is solved
|
||||
origin: WEBAPP_URL,
|
||||
};
|
||||
this.namespace = namespace;
|
||||
this.actionManager = new SdkActionManager(namespace);
|
||||
|
@ -507,7 +513,11 @@ window.addEventListener("message", (e) => {
|
|||
if (!parsedAction) {
|
||||
return;
|
||||
}
|
||||
|
||||
const actionManager = Cal.actionsManagers[parsedAction.ns];
|
||||
globalCal.__logQueue = globalCal.__logQueue || [];
|
||||
globalCal.__logQueue.push({ ...parsedAction, data: detail.data });
|
||||
|
||||
if (!actionManager) {
|
||||
throw new Error("Unhandled Action" + parsedAction);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,76 @@
|
|||
import { CalWindow } from "@calcom/embed-snippet";
|
||||
|
||||
window.addEventListener("message", (e) => {
|
||||
const WEBAPP_URL =
|
||||
import.meta.env.NEXT_PUBLIC_WEBAPP_URL || `https://${import.meta.env.NEXT_PUBLIC_VERCEL_URL}`;
|
||||
const EMBED_LIB_URL = import.meta.env.NEXT_PUBLIC_EMBED_LIB_URL || `${WEBAPP_URL}/embed/embed.js`;
|
||||
|
||||
(window as any).fingerprint = import.meta.env.NEXT_PUBLIC_EMBED_FINGER_PRINT as string;
|
||||
|
||||
// Install Cal Embed Code Snippet
|
||||
(function (C, A, L) {
|
||||
// @ts-ignore
|
||||
let p = function (a, ar) {
|
||||
a.q.push(ar);
|
||||
};
|
||||
let d = C.document;
|
||||
// @ts-ignore
|
||||
C.Cal =
|
||||
// @ts-ignore
|
||||
C.Cal ||
|
||||
function () {
|
||||
// @ts-ignore
|
||||
let cal = C.Cal;
|
||||
let ar = arguments;
|
||||
if (!cal.loaded) {
|
||||
cal.ns = {};
|
||||
cal.q = cal.q || [];
|
||||
// @ts-ignore
|
||||
d.head.appendChild(d.createElement("script")).src = A;
|
||||
cal.loaded = true;
|
||||
}
|
||||
if (ar[0] === L) {
|
||||
const api = function () {
|
||||
p(api, arguments);
|
||||
};
|
||||
const namespace = ar[1];
|
||||
// @ts-ignore
|
||||
api.q = api.q || [];
|
||||
// @ts-ignore
|
||||
typeof namespace === "string" ? (cal.ns[namespace] = api) && p(api, ar) : p(cal, ar);
|
||||
return;
|
||||
}
|
||||
p(cal, ar);
|
||||
};
|
||||
})(window, EMBED_LIB_URL, "init");
|
||||
|
||||
const previewWindow: CalWindow = window;
|
||||
|
||||
previewWindow.Cal!("init", {
|
||||
origin: WEBAPP_URL,
|
||||
});
|
||||
const searchParams = new URL(document.URL).searchParams;
|
||||
const embedType = searchParams.get("embedType");
|
||||
const calLink = searchParams.get("calLink");
|
||||
if (embedType! === "inline") {
|
||||
previewWindow.Cal!("inline", {
|
||||
elementOrSelector: "#my-embed",
|
||||
calLink: calLink,
|
||||
});
|
||||
} else if (embedType === "floating-popup") {
|
||||
previewWindow.Cal!("floatingButton", {
|
||||
calLink: calLink,
|
||||
attributes: {
|
||||
id: "my-floating-button",
|
||||
},
|
||||
});
|
||||
} else if (embedType === "element-click") {
|
||||
const button = document.createElement("button");
|
||||
button.setAttribute("data-cal-link", calLink!);
|
||||
button.innerHTML = "I am a button that exists on your website";
|
||||
document.body.appendChild(button);
|
||||
}
|
||||
|
||||
previewWindow.addEventListener("message", (e) => {
|
||||
const data = e.data;
|
||||
if (data.mode !== "cal:preview") {
|
||||
return;
|
||||
|
|
|
@ -1,23 +1,39 @@
|
|||
require("dotenv").config({ path: "../../../.env" });
|
||||
|
||||
process.env.NEXT_PUBLIC_VERCEL_URL = process.env.VERCEL_URL;
|
||||
|
||||
const path = require("path");
|
||||
const { defineConfig } = require("vite");
|
||||
module.exports = defineConfig({
|
||||
envPrefix: "NEXT_PUBLIC_",
|
||||
build: {
|
||||
minify: "terser",
|
||||
watch: {
|
||||
include: ["src/**"],
|
||||
},
|
||||
terserOptions: {
|
||||
format: {
|
||||
comments: false,
|
||||
module.exports = defineConfig((configEnv) => {
|
||||
const config = {
|
||||
envPrefix: "NEXT_PUBLIC_",
|
||||
base: "/embed/",
|
||||
build: {
|
||||
minify: "terser",
|
||||
terserOptions: {
|
||||
format: {
|
||||
comments: false,
|
||||
},
|
||||
},
|
||||
rollupOptions: {
|
||||
input: {
|
||||
preview: path.resolve(__dirname, "preview.html"),
|
||||
embed: path.resolve(__dirname, "src/embed.ts"),
|
||||
},
|
||||
output: {
|
||||
entryFileNames: "[name].js",
|
||||
//FIXME: Can't specify UMD as import because preview is an app which doesn't support `format` and this setting apply to both input
|
||||
//format: "umd",
|
||||
dir: "../../../apps/web/public/embed",
|
||||
},
|
||||
},
|
||||
},
|
||||
lib: {
|
||||
entry: path.resolve(__dirname, "src/embed.ts"),
|
||||
name: "embed",
|
||||
fileName: (format) => `embed.${format}.js`,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
if (configEnv.mode === "development") {
|
||||
config.build.watch = {
|
||||
include: ["src/**"],
|
||||
};
|
||||
}
|
||||
return config;
|
||||
});
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 35 KiB |
|
@ -17,7 +17,7 @@ function App() {
|
|||
</h1>
|
||||
<Cal
|
||||
calOrigin="http://localhost:3000"
|
||||
embedJsUrl="//localhost:3100/dist/embed.umd.js"
|
||||
embedJsUrl="//localhost:3000/embed/embed.js"
|
||||
calLink="pro"
|
||||
config={{
|
||||
name: "John Doe",
|
||||
|
|
|
@ -5,6 +5,7 @@ import { defineConfig } from "vite";
|
|||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
envPrefix: "NEXT_PUBLIC_",
|
||||
build: {
|
||||
lib: {
|
||||
entry: path.resolve(__dirname, "src/index.ts"),
|
||||
|
|
|
@ -4,6 +4,10 @@
|
|||
*/
|
||||
import type { Cal as CalClass, InstructionQueue } from "@calcom/embed-core/src/embed";
|
||||
|
||||
const WEBAPP_URL = import.meta.env.NEXT_PUBLIC_WEBAPP_URL || `https://${import.meta.env.NEXT_PUBLIC_VERCEL_URL}`;
|
||||
|
||||
const EMBED_LIB_URL = import.meta.env.NEXT_PUBLIC_EMBED_LIB_URL || `${WEBAPP_URL}/embed/embed.js`;
|
||||
|
||||
export interface GlobalCal {
|
||||
(methodName: string, arg?: any): void;
|
||||
/** Marks that the embed.js is loaded. Avoids re-downloading it. */
|
||||
|
@ -15,13 +19,14 @@ export interface GlobalCal {
|
|||
instance?: CalClass;
|
||||
__css?: string;
|
||||
fingerprint?: string;
|
||||
__logQueue?: any[];
|
||||
}
|
||||
|
||||
export interface CalWindow extends Window {
|
||||
Cal?: GlobalCal;
|
||||
}
|
||||
|
||||
export default function EmbedSnippet(url = "https://cal.com/embed.js") {
|
||||
export default function EmbedSnippet(url = EMBED_LIB_URL) {
|
||||
(function (C: CalWindow, A, L) {
|
||||
let p = function (a: any, ar: any) {
|
||||
a.q.push(ar);
|
||||
|
@ -35,6 +40,7 @@ export default function EmbedSnippet(url = "https://cal.com/embed.js") {
|
|||
if (!cal.loaded) {
|
||||
cal.ns = {};
|
||||
cal.q = cal.q || [];
|
||||
//@ts-ignore
|
||||
d.head.appendChild(d.createElement("script")).src = A;
|
||||
cal.loaded = true;
|
||||
}
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
require("dotenv").config({ path: "../../../.env" });
|
||||
|
||||
const path = require("path");
|
||||
const { defineConfig } = require("vite");
|
||||
|
||||
process.env.NEXT_PUBLIC_VERCEL_URL = process.env.VERCEL_URL;
|
||||
|
||||
module.exports = defineConfig({
|
||||
build: {
|
||||
envPrefix: "NEXT_PUBLIC_",
|
||||
lib: {
|
||||
entry: path.resolve(__dirname, "src", "index.ts"),
|
||||
name: "snippet",
|
||||
|
|
|
@ -5,6 +5,7 @@ export const WEBSITE_URL = process.env.NEXT_PUBLIC_WEBSITE_URL || "https://cal.c
|
|||
export const CONSOLE_URL = WEBAPP_URL.startsWith("http://localhost")
|
||||
? "http://localhost:3004"
|
||||
: `https://console.cal.${process.env.VERCEL_ENV === "production" ? "com" : "dev"}`;
|
||||
export const EMBED_LIB_URL = process.env.NEXT_PUBLIC_EMBED_LIB_URL || `${WEBAPP_URL}/embed/embed.js`;
|
||||
export const IS_PRODUCTION = process.env.NODE_ENV === "production";
|
||||
export const TRIAL_LIMIT_DAYS = 14;
|
||||
export const HOSTED_CAL_FEATURES = process.env.HOSTED_CAL_FEATURES || BASE_URL === "https://app.cal.com";
|
||||
|
|
|
@ -75,6 +75,9 @@
|
|||
"@calcom/web#start": {
|
||||
"dependsOn": ["@calcom/prisma#db-deploy"]
|
||||
},
|
||||
"@calcom/embed-core#build": {
|
||||
"cache": false
|
||||
},
|
||||
"@calcom/website#build": {
|
||||
"dependsOn": [
|
||||
"$NEXT_PUBLIC_INTERCOM_APP_ID",
|
||||
|
@ -100,7 +103,7 @@
|
|||
"db-seed": {},
|
||||
"deploy": {
|
||||
"cache": false,
|
||||
"dependsOn": ["@calcom/web#build"],
|
||||
"dependsOn": ["@calcom/web#build", "@calcom/embed-core#build"],
|
||||
"outputs": []
|
||||
},
|
||||
"clean": {
|
||||
|
|
Loading…
Reference in a new issue