diff --git a/README.md b/README.md
index c86c9369f..8422bbe48 100644
--- a/README.md
+++ b/README.md
@@ -87,6 +87,38 @@ Set environment variables via `.env`.
- `STATUS_PAGE_SCRIPT_URI` (optional): Used for enabling the status page; used with `pnpm run build:inject-statuspage`.
- `SMARTBANNER_APP_NAME`, `SMARTBANNER_ORG_NAME`, `SMARTBANNER_ICON_URL`, `SMARTBANNER_APPSTORE_URL` (optional): Used for enabling the smart app banner; used with `pnpm run build:inject-smartbanner`.
+## Part 5: Configure entry points
+
+### HTML files
+
+Edit `scripts/generate-entry-points.js` and set up entry points according to your SEO needs. At least one entry point must be configured,
+i.e. at least one element must be present in the `ENTRY_POINTS` array. This array consists of objects of the form:
+
+```
+{
+ title: 'Page title',
+ description: 'Page description.',
+ fileName: 'HTML entry point file name, e.g.: index.html',
+},
+```
+
+The build script will traverse these entries and create files in `entry-points` directory, modifying the `template.html` file accordingly
+for each entry. The `rollupOptions` config option in `vite.config.ts` informs the framework about the location of all the entry points
+created above.
+
+### Rewrite rules
+
+Edit `vercel.json` and configure the `rewrites` configuration option. It is an array of objects of the form:
+
+```
+ {
+ "source": "Regexp for matching the URL path, e.g.: /portfolio(/?.*)",
+ "destination": "Entry point file to use, e.g.: /entry-points/portfolio.html"
+ },
+```
+
+Note: The first matching rule takes precedence over anything defined afterwards in the array.
+
# Testing
## Unit testing
diff --git a/package.json b/package.json
index 45b88552e..391c451e7 100644
--- a/package.json
+++ b/package.json
@@ -9,13 +9,14 @@
},
"scripts": {
"dev": "vite",
- "build": "tsc && vite build",
+ "build": "pnpm run build:generate-entry-points && tsc && vite build",
"build:inject-app-deeplinks": "sh scripts/inject-app-deeplinks.sh",
"build:inject-amplitude": "node scripts/inject-amplitude.js",
"build:inject-bugsnag": "node scripts/inject-bugsnag.js",
"build:inject-intercom": "node scripts/inject-intercom.js",
"build:inject-statuspage": "node scripts/inject-statuspage.js",
"build:inject-smartbanner": "node scripts/inject-smartbanner.js",
+ "build:generate-entry-points": "node scripts/generate-entry-points.js",
"deploy:ipfs": "node scripts/upload-ipfs.js --verbose",
"deploy:update-ipns": "node scripts/update-ipns.js",
"deploy:update-dnslink": "node scripts/update-dnslink.js",
diff --git a/scripts/generate-entry-points.js b/scripts/generate-entry-points.js
new file mode 100755
index 000000000..609ed3c92
--- /dev/null
+++ b/scripts/generate-entry-points.js
@@ -0,0 +1,32 @@
+import fs from 'fs/promises';
+import path from 'path';
+import { fileURLToPath } from 'url';
+
+const currentPath = fileURLToPath(import.meta.url);
+const projectRoot = path.dirname(currentPath);
+const templateFilePath = path.resolve(projectRoot, '../template.html');
+const entryPointsDir = path.resolve(projectRoot, '../entry-points');
+
+const ENTRY_POINTS = [
+ {
+ title: 'dYdX',
+ description: 'dYdX',
+ fileName: 'index.html',
+ },
+];
+
+try {
+ fs.mkdir(entryPointsDir, { recursive: true });
+
+ for (const entryPoint of ENTRY_POINTS) {
+ const html = await fs.readFile(templateFilePath, 'utf-8');
+ const destinationFilePath = path.resolve(entryPointsDir, entryPoint.fileName);
+ const injectedHtml = html.replace(
+ '
dYdX',
+ `${entryPoint.title}\n `
+ );
+ await fs.writeFile(destinationFilePath, injectedHtml, 'utf-8');
+ }
+} catch (err) {
+ console.error('Error generating entry points:', err);
+}
diff --git a/scripts/inject-amplitude.js b/scripts/inject-amplitude.js
index b1bdc79c8..3caa733f6 100644
--- a/scripts/inject-amplitude.js
+++ b/scripts/inject-amplitude.js
@@ -7,56 +7,62 @@ const AMPLITUDE_SERVER_URL = process.env.AMPLITUDE_SERVER_URL;
const currentPath = fileURLToPath(import.meta.url);
const projectRoot = path.dirname(currentPath);
-const htmlFilePath = path.resolve(projectRoot, '../dist/index.html');
if (AMPLITUDE_API_KEY) {
try {
- const html = await fs.readFile(htmlFilePath, 'utf-8');
-
- const amplitudeCdnScript = `
- `;
-
- const amplitudeListenerScript = ``;
-
- const injectedHtml = html.replace(
- '',
- `\n${amplitudeCdnScript}\n${amplitudeListenerScript}`
- );
-
- await fs.writeFile(htmlFilePath, injectedHtml, 'utf-8');
-
- console.log('Amplitude scripts successfully injected.');
+ const files = await fs.readdir('entry-points');
+ for (const file of files) {
+ inject(file);
+ };
} catch (err) {
console.error('Error injecting Amplitude scripts:', err);
}
}
+
+async function inject(fileName) {
+ const htmlFilePath = path.resolve(projectRoot, `../dist/entry-points/${fileName}`);
+ const html = await fs.readFile(htmlFilePath, 'utf-8');
+
+ const amplitudeCdnScript = `
+ `;
+
+ const amplitudeListenerScript = ``;
+
+ const injectedHtml = html.replace(
+ '',
+ `\n${amplitudeCdnScript}\n${amplitudeListenerScript}`
+ );
+
+ await fs.writeFile(htmlFilePath, injectedHtml, 'utf-8');
+
+ console.log(`Amplitude scripts successfully injected (${fileName}).`);
+}
\ No newline at end of file
diff --git a/scripts/inject-bugsnag.js b/scripts/inject-bugsnag.js
index 305bab523..7dff7cef5 100644
--- a/scripts/inject-bugsnag.js
+++ b/scripts/inject-bugsnag.js
@@ -6,73 +6,80 @@ const BUGSNAG_API_KEY = process.env.BUGSNAG_API_KEY;
const currentPath = fileURLToPath(import.meta.url);
const projectRoot = path.dirname(currentPath);
-const htmlFilePath = path.resolve(projectRoot, '../dist/index.html');
try {
+ const files = await fs.readdir('entry-points');
+ for (const file of files) {
+ inject(file);
+ };
+} catch (err) {
+ console.error('Error injecting Bugsnag scripts:', err);
+}
+
+async function inject(fileName) {
+ const htmlFilePath = path.resolve(projectRoot, `../dist/entry-points/${fileName}`);
const html = await fs.readFile(htmlFilePath, 'utf-8');
const scripts = `
-
+
-
-
- `;
+ globalThis.addEventListener('dydx:log', function (event) {
+ var error = event.detail.error;
+ var metadata = event.detail.metadata;
+ var location = event.detail.location;
+
+ if (BUGSNAG_API_KEY && Bugsnag.isStarted()) {
+ Bugsnag.notify(error, function (event) {
+ event.context = location;
+ if (metadata) {
+ event.addMetadata('metadata', metadata);
+ }
+ if (walletType) {
+ event.addMetadata('walletType', walletType);
+ }
+ });
+ } else {
+ console.warn(location, error, metadata);
+ }
+ });
+ })();
+
+
+ `;
const injectedHtml = html.replace('', `\n${scripts}\n`);
await fs.writeFile(htmlFilePath, injectedHtml, 'utf-8');
- console.log('Bugsnag scripts successfully injected.');
-} catch (err) {
- console.error('Error injecting Bugsnag scripts:', err);
-}
+ console.log(`Bugsnag scripts successfully injected (${fileName}).`);
+}
\ No newline at end of file
diff --git a/scripts/inject-intercom.js b/scripts/inject-intercom.js
index f344e92f5..f6442d9de 100644
--- a/scripts/inject-intercom.js
+++ b/scripts/inject-intercom.js
@@ -7,70 +7,77 @@ const INTERCOM_APP_ID = process.env.INTERCOM_APP_ID;
const currentPath = fileURLToPath(import.meta.url);
const projectRoot = path.dirname(currentPath);
-const htmlFilePath = path.resolve(projectRoot, '../dist/index.html');
if (INTERCOM_APP_ID) {
try {
- const html = await fs.readFile(htmlFilePath, 'utf-8');
+ const files = await fs.readdir('entry-points');
+ for (const file of files) {
+ inject(file);
+ };
+ } catch (err) {
+ console.error('Error injecting Intercom scripts:', err);
+ }
+}
- const intercomScripts = `
-
-
+async function inject(fileName) {
+ const htmlFilePath = path.resolve(projectRoot, `../dist/entry-points/${fileName}`);
+ const html = await fs.readFile(htmlFilePath, 'utf-8');
+
+ const intercomScripts = `
+
+
-
- `;
+ }
+ })();
+
+ `;
- const injectedHtml = html.replace(
- '',
- `\n${intercomScripts}\n`
- );
+ const injectedHtml = html.replace(
+ '',
+ `\n${intercomScripts}\n`
+ );
- await fs.writeFile(htmlFilePath, injectedHtml, 'utf-8');
+ await fs.writeFile(htmlFilePath, injectedHtml, 'utf-8');
- console.log('Intercom scripts successfully injected.');
- } catch (err) {
- console.error('Error injecting Intercom scripts:', err);
- }
-}
+ console.log(`Intercom scripts successfully injected (${fileName}).`);
+}
\ No newline at end of file
diff --git a/scripts/inject-smartbanner.js b/scripts/inject-smartbanner.js
index b5f4767e7..639fbe7fe 100644
--- a/scripts/inject-smartbanner.js
+++ b/scripts/inject-smartbanner.js
@@ -11,7 +11,6 @@ const SMARTBANNER_GOOGLEPLAY_URL = process.env.SMARTBANNER_GOOGLEPLAY_URL;
const currentPath = fileURLToPath(import.meta.url);
const projectRoot = path.dirname(currentPath);
-const htmlFilePath = path.resolve(projectRoot, '../dist/index.html');
const smartbannerFilePath = path.resolve(projectRoot, '../dist/smartbanner.html');
if (
@@ -21,40 +20,48 @@ if (
(SMARTBANNER_APPSTORE_URL || SMARTBANNER_GOOGLEPLAY_URL)
) {
try {
- const html = await fs.readFile(htmlFilePath, 'utf-8');
- let smartbanner = await fs.readFile(smartbannerFilePath, 'utf-8');
- smartbanner = smartbanner
- .replace('SMARTBANNER_APP_NAME', SMARTBANNER_APP_NAME)
- .replace('SMARTBANNER_ORG_NAME', SMARTBANNER_ORG_NAME)
- .replace('SMARTBANNER_ICON_URL', SMARTBANNER_ICON_URL)
- .replace('SMARTBANNER_ICON_URL', SMARTBANNER_ICON_URL);
-
- /* hardcoded injection depending on whether the app is available on App Store and/or Google Play */
-
- if (SMARTBANNER_APPSTORE_URL) {
- smartbanner = `\t\n` + smartbanner;
- }
+ const files = await fs.readdir('entry-points');
+ for (const file of files) {
+ inject(file);
+ };
+ } catch (err) {
+ console.error('Error injecting Smartbanner scripts:', err);
+ }
+}
+
+async function inject(fileName) {
+ const htmlFilePath = path.resolve(projectRoot, `../dist/entry-points/${fileName}`);
+ const html = await fs.readFile(htmlFilePath, 'utf-8');
+ let smartbanner = await fs.readFile(smartbannerFilePath, 'utf-8');
+ smartbanner = smartbanner
+ .replace('SMARTBANNER_APP_NAME', SMARTBANNER_APP_NAME)
+ .replace('SMARTBANNER_ORG_NAME', SMARTBANNER_ORG_NAME)
+ .replace('SMARTBANNER_ICON_URL', SMARTBANNER_ICON_URL)
+ .replace('SMARTBANNER_ICON_URL', SMARTBANNER_ICON_URL);
+
+ /* hardcoded injection depending on whether the app is available on App Store and/or Google Play */
+
+ if (SMARTBANNER_APPSTORE_URL) {
+ smartbanner = `\t\n` + smartbanner;
+ }
+ if (SMARTBANNER_GOOGLEPLAY_URL) {
+ smartbanner = `\t\n` + smartbanner;
+ }
+ if (SMARTBANNER_APPSTORE_URL) {
if (SMARTBANNER_GOOGLEPLAY_URL) {
- smartbanner = `\t\n` + smartbanner;
- }
- if (SMARTBANNER_APPSTORE_URL) {
- if (SMARTBANNER_GOOGLEPLAY_URL) {
- smartbanner = `\t\n` + smartbanner;
- } else {
- smartbanner = `\t\n` + smartbanner;
- }
+ smartbanner = `\t\n` + smartbanner;
} else {
- if (SMARTBANNER_GOOGLEPLAY_URL) {
- smartbanner = `\t\n` + smartbanner;
- }
+ smartbanner = `\t\n` + smartbanner;
}
+ } else {
+ if (SMARTBANNER_GOOGLEPLAY_URL) {
+ smartbanner = `\t\n` + smartbanner;
+ }
+ }
- const injectedHtml = html.replace('', `${smartbanner}\n`);
+ const injectedHtml = html.replace('', `${smartbanner}\n`);
- await fs.writeFile(htmlFilePath, injectedHtml, 'utf-8');
+ await fs.writeFile(htmlFilePath, injectedHtml, 'utf-8');
- console.log('Smartbanner scripts successfully injected.');
- } catch (err) {
- console.error('Error injecting Smartbanner scripts:', err);
- }
-}
+ console.log(`Smartbanner scripts successfully injected (${fileName}).`);
+}
\ No newline at end of file
diff --git a/scripts/inject-statuspage.js b/scripts/inject-statuspage.js
index 0315cbfba..34d9d82b8 100644
--- a/scripts/inject-statuspage.js
+++ b/scripts/inject-statuspage.js
@@ -6,23 +6,29 @@ const STATUS_PAGE_SCRIPT_URI = process.env.STATUS_PAGE_SCRIPT_URI;
const currentPath = fileURLToPath(import.meta.url);
const projectRoot = path.dirname(currentPath);
-const htmlFilePath = path.resolve(projectRoot, '../dist/index.html');
if (STATUS_PAGE_SCRIPT_URI) {
try {
- const html = await fs.readFile(htmlFilePath, 'utf-8');
+ const files = await fs.readdir('entry-points');
+ for (const file of files) {
+ inject(file);
+ };
+ } catch (err) {
+ console.error('Error injecting StatusPage scripts:', err);
+ }
+}
- const statusPageScript = ``;
+async function inject(fileName) {
+ const htmlFilePath = path.resolve(projectRoot, `../dist/entry-points/${fileName}`);
+ const html = await fs.readFile(htmlFilePath, 'utf-8');
+ const statusPageScript = ``;
- const injectedHtml = html.replace(
- '',
- `\n${statusPageScript}\n`
- );
+ const injectedHtml = html.replace(
+ '',
+ `\n${statusPageScript}\n`
+ );
- await fs.writeFile(htmlFilePath, injectedHtml, 'utf-8');
+ await fs.writeFile(htmlFilePath, injectedHtml, 'utf-8');
- console.log('StatusPage script successfully injected.');
- } catch (err) {
- console.error('Error injecting StatusPage scripts:', err);
- }
+ console.log(`StatusPage script successfully injected (${fileName}).`);
}
diff --git a/index.html b/template.html
similarity index 99%
rename from index.html
rename to template.html
index 3b1873fe1..88fbc243f 100644
--- a/index.html
+++ b/template.html
@@ -36,4 +36,4 @@