chore(js): Extract Hugo params imports to single-purpose modules, fix environment-specific Hugo configs, use Hugo environments instead of specifying the config file, configure source maps and ESM for development and testing

chore-js-refactor-footer-scripts-modules
Jason Stirnaman 2025-06-09 13:59:29 -05:00
parent cd087af85d
commit 717ec5cd1d
28 changed files with 476 additions and 525 deletions

View File

@ -41,7 +41,7 @@ jobs:
- /home/circleci/bin
- run:
name: Hugo Build
command: npx hugo --config config/production/config.yml --logLevel info --minify --gc --destination workspace/public
command: yarn hugo --environment production --logLevel info --gc --destination workspace/public
- persist_to_workspace:
root: workspace
paths:

View File

@ -1730,11 +1730,30 @@ If you're adding UI functionality that requires JavaScript, follow these steps:
### Debugging JavaScript
To debug JavaScript code used in the InfluxData documentation UI:
To debug JavaScript code used in the InfluxData documentation UI, choose one of the following methods:
- Use source maps and the Chrome DevTools debugger.
- Use debug helpers that provide breakpoints and console logging as a workaround or alternative for using source maps and the Chrome DevTools debugger.
#### Using source maps and Chrome DevTools debugger
1. In VS Code, select Run > Start Debugging.
2. Select the "Debug Docs (source maps)" configuration.
3. Click the play button to start the debugger.
5. Set breakpoints in the JavaScript source files--files in the
`assets/js/ns-hugo-imp:` namespace-- in the
VS Code editor or in the Chrome Developer Tools Sources panel:
- In the VS Code Debugger panel > "Loaded Scripts" section, find the
`assets/js/ns-hugo-imp:` namespace.
- In the Chrome Developer Tools Sources panel, expand
`js/ns-hugo-imp:/<YOUR_WORKSPACE_ROOT>/assets/js/`.
#### Using debug helpers
1. In your JavaScript module, import debug helpers from `assets/js/utils/debug-helpers.js`.
These helpers provide breakpoints and console logging as a workaround for
Hugo's lack of source map support in the asset pipeline.
These helpers provide breakpoints and console logging as a workaround or alternative for
using source maps and the Chrome DevTools debugger.
2. Insert debug statements by calling the helper functions in your code--for example:
```js
@ -1757,7 +1776,7 @@ To debug JavaScript code used in the InfluxData documentation UI:
yarn hugo server
```
4. In VS Code, go to Run > Start Debugging, and select the "Debug Docs (console-based)" configuration.
4. In VS Code, go to Run > Start Debugging, and select the "Debug JS (debug-helpers)" configuration.
Your system uses the configuration in `launch.json` to launch the site in Chrome
and attach the debugger to the Developer Tools console.

33
.vscode/launch.json vendored
View File

@ -2,7 +2,7 @@
"version": "0.2.0",
"configurations": [
{
"name": "Debug Docs (console-based)",
"name": "Debug JS (debug-helpers)",
"type": "chrome",
"request": "launch",
"url": "http://localhost:1313",
@ -13,6 +13,35 @@
"sourceMaps": false,
"trace": true,
"smartStep": false
}
},
{
"name": "Debug JS (source maps)",
"type": "chrome",
"request": "launch",
"url": "http://localhost:1313",
"webRoot": "${workspaceFolder}",
"sourceMaps": true,
"sourceMapPathOverrides": {
"*": "${webRoot}/assets/js/*",
"main.js": "${webRoot}/assets/js/main.js",
"page-context.js": "${webRoot}/assets/js/page-context.js",
"ask-ai-trigger.js": "${webRoot}/assets/js/ask-ai-trigger.js",
"ask-ai.js": "${webRoot}/assets/js/ask-ai.js",
"utils/*": "${webRoot}/assets/js/utils/*",
"services/*": "${webRoot}/assets/js/services/*"
},
"skipFiles": [
"<node_internals>/**",
"node_modules/**",
"chrome-extension://**"
],
"trace": true,
"smartStep": true,
"disableNetworkCache": true,
"userDataDir": "${workspaceFolder}/.vscode/chrome-user-data",
"runtimeArgs": [
"--disable-features=VizDisplayCompositor"
]
},
]
}

View File

@ -1718,11 +1718,30 @@ If you're adding UI functionality that requires JavaScript, follow these steps:
### Debugging JavaScript
To debug JavaScript code used in the InfluxData documentation UI:
To debug JavaScript code used in the InfluxData documentation UI, choose one of the following methods:
- Use source maps and the Chrome DevTools debugger.
- Use debug helpers that provide breakpoints and console logging as a workaround or alternative for using source maps and the Chrome DevTools debugger.
#### Using source maps and Chrome DevTools debugger
1. In VS Code, select Run > Start Debugging.
2. Select the "Debug Docs (source maps)" configuration.
3. Click the play button to start the debugger.
5. Set breakpoints in the JavaScript source files--files in the
`assets/js/ns-hugo-imp:` namespace-- in the
VS Code editor or in the Chrome Developer Tools Sources panel:
- In the VS Code Debugger panel > "Loaded Scripts" section, find the
`assets/js/ns-hugo-imp:` namespace.
- In the Chrome Developer Tools Sources panel, expand
`js/ns-hugo-imp:/<YOUR_WORKSPACE_ROOT>/assets/js/`.
#### Using debug helpers
1. In your JavaScript module, import debug helpers from `assets/js/utils/debug-helpers.js`.
These helpers provide breakpoints and console logging as a workaround for
Hugo's lack of source map support in the asset pipeline.
These helpers provide breakpoints and console logging as a workaround or alternative for
using source maps and the Chrome DevTools debugger.
2. Insert debug statements by calling the helper functions in your code--for example:
```js
@ -1745,7 +1764,7 @@ To debug JavaScript code used in the InfluxData documentation UI:
yarn hugo server
```
4. In VS Code, go to Run > Start Debugging, and select the "Debug Docs (console-based)" configuration.
4. In VS Code, go to Run > Start Debugging, and select the "Debug JS (debug-helpers)" configuration.
Your system uses the configuration in `launch.json` to launch the site in Chrome
and attach the debugger to the Developer Tools console.

View File

@ -2,7 +2,7 @@
///////////////// Preferred Client Library programming language ///////////////
////////////////////////////////////////////////////////////////////////////////
import { activateTabs, updateBtnURLs } from './tabbed-content.js';
import { getPreference, setPreference } from './local-storage.js';
import { getPreference, setPreference } from './services/local-storage.js';
function getVisitedApiLib() {
const path = window.location.pathname.match(

View File

@ -8,29 +8,31 @@ function setUser(userid, email) {
window[NAMESPACE] = {
user: {
uniqueClientId: userid,
email: email,
}
}
email: email,
},
};
}
// Initialize the chat widget
function initializeChat({onChatLoad, chatAttributes}) {
/* See https://docs.kapa.ai/integrations/website-widget/configuration for
function initializeChat({ onChatLoad, chatAttributes }) {
/* See https://docs.kapa.ai/integrations/website-widget/configuration for
* available configuration options.
* All values are strings.
*/
// If you make changes to data attributes here, you also need to port the changes to the api-docs/template.hbs API reference template.
// If you make changes to data attributes here, you also need to
// port the changes to the api-docs/template.hbs API reference template.
const requiredAttributes = {
websiteId: 'a02bca75-1dd3-411e-95c0-79ee1139be4d',
projectName: 'InfluxDB',
projectColor: '#020a47',
projectLogo: '/img/influx-logo-cubo-white.png',
}
};
const optionalAttributes = {
modalDisclaimer: 'This AI can access [documentation for InfluxDB, clients, and related tools](https://docs.influxdata.com). Information you submit is used in accordance with our [Privacy Policy](https://www.influxdata.com/legal/privacy-policy/).',
modalExampleQuestions: 'Use Python to write data to InfluxDB 3,How do I query using SQL?,How do I use MQTT with Telegraf?',
modalDisclaimer:
'This AI can access [documentation for InfluxDB, clients, and related tools](https://docs.influxdata.com). Information you submit is used in accordance with our [Privacy Policy](https://www.influxdata.com/legal/privacy-policy/).',
modalExampleQuestions:
'Use Python to write data to InfluxDB 3,How do I query using SQL?,How do I use MQTT with Telegraf?',
buttonHide: 'true',
exampleQuestionButtonWidth: 'auto',
modalOpenOnCommandK: 'true',
@ -52,28 +54,32 @@ function initializeChat({onChatLoad, chatAttributes}) {
modalHeaderBorderBottom: 'none',
modalTitleColor: '#fff',
modalTitleFontSize: '1.25rem',
}
};
const scriptUrl = 'https://widget.kapa.ai/kapa-widget.bundle.js';
const script = document.createElement('script');
script.async = true;
script.src = scriptUrl;
script.onload = function() {
script.onload = function () {
onChatLoad();
window.influxdatadocs.AskAI = AskAI;
};
script.onerror = function() {
script.onerror = function () {
console.error('Error loading AI chat widget script');
};
const dataset = {...requiredAttributes, ...optionalAttributes, ...chatAttributes};
Object.keys(dataset).forEach(key => {
// Assign dataset attributes from the object
const dataset = {
...requiredAttributes,
...optionalAttributes,
...chatAttributes,
};
Object.keys(dataset).forEach((key) => {
// Assign dataset attributes from the object
script.dataset[key] = dataset[key];
});
// Check for an existing script element to remove
const oldScript= document.querySelector(`script[src="${scriptUrl}"]`);
const oldScript = document.querySelector(`script[src="${scriptUrl}"]`);
if (oldScript) {
oldScript.remove();
}
@ -82,22 +88,21 @@ function initializeChat({onChatLoad, chatAttributes}) {
function getProductExampleQuestions() {
const questions = productData?.product?.ai_sample_questions;
return questions?.join(',') || '';
return questions?.join(',') || '';
}
/**
/**
* chatParams: specify custom (for example, page-specific) attribute values for the chat, pass the dataset key-values (collected in ...chatParams). See https://docs.kapa.ai/integrations/website-widget/configuration for available configuration options.
* onChatLoad: function to call when the chat widget has loaded
* userid: optional, a unique user ID for the user (not currently used for public docs)
*/
*/
export default function AskAI({ userid, email, onChatLoad, ...chatParams }) {
const modalExampleQuestions = getProductExampleQuestions();
const chatAttributes = {
...(modalExampleQuestions && { modalExampleQuestions }),
...chatParams,
}
initializeChat({onChatLoad, chatAttributes});
};
initializeChat({ onChatLoad, chatAttributes });
if (userid) {
setUser(userid, email);

View File

@ -1,8 +1,9 @@
import $ from 'jquery';
import { debugBreak } from './utils/debug-helpers.js';
function initialize() {
var codeBlockSelector = '.article--content pre';
var codeBlocks = $(codeBlockSelector);
var $codeBlocks = $(codeBlockSelector);
var appendHTML = `
<div class="code-controls">
@ -15,7 +16,7 @@ function initialize() {
`;
// Wrap all codeblocks with a new 'codeblock' div
$(codeBlocks).each(function () {
$codeBlocks.each(function () {
$(this).wrap("<div class='codeblock'></div>");
});
@ -68,7 +69,9 @@ function initialize() {
// Trigger copy failure state lifecycle
$('.copy-code').click(function () {
let text = $(this).closest('.code-controls').prevAll('pre:has(code)')[0].innerText;
let text = $(this)
.closest('.code-controls')
.prevAll('pre:has(code)')[0].innerText;
const copyContent = async () => {
try {
@ -90,7 +93,10 @@ Disable scrolling on the body.
Disable user selection on everything but the fullscreen codeblock.
*/
$('.fullscreen-toggle').click(function () {
var code = $(this).closest('.code-controls').prevAll('pre:has(code)').clone();
var code = $(this)
.closest('.code-controls')
.prevAll('pre:has(code)')
.clone();
$('#fullscreen-code-placeholder').replaceWith(code[0]);
$('body').css('overflow', 'hidden');

View File

@ -1,7 +1,7 @@
import $ from 'jquery';
import { Datepicker } from 'vanillajs-datepicker';
import { toggleModal } from './modals.js';
import * as localStorage from './local-storage.js';
import * as localStorage from './services/local-storage.js';
// Placeholder start date used in InfluxDB custom timestamps
const defaultStartDate = '2022-01-01';
@ -53,65 +53,65 @@ function timeToUnixSeconds(time) {
return unixSeconds;
}
// Default time values in getting started sample data
const defaultTimes = [
{
rfc3339: `${defaultStartDate}T08:00:00Z`,
unix: timeToUnixSeconds(`${defaultStartDate}T08:00:00Z`),
}, // 1641024000
{
rfc3339: `${defaultStartDate}T09:00:00Z`,
unix: timeToUnixSeconds(`${defaultStartDate}T09:00:00Z`),
}, // 1641027600
{
rfc3339: `${defaultStartDate}T10:00:00Z`,
unix: timeToUnixSeconds(`${defaultStartDate}T10:00:00Z`),
}, // 1641031200
{
rfc3339: `${defaultStartDate}T11:00:00Z`,
unix: timeToUnixSeconds(`${defaultStartDate}T11:00:00Z`),
}, // 1641034800
{
rfc3339: `${defaultStartDate}T12:00:00Z`,
unix: timeToUnixSeconds(`${defaultStartDate}T12:00:00Z`),
}, // 1641038400
{
rfc3339: `${defaultStartDate}T13:00:00Z`,
unix: timeToUnixSeconds(`${defaultStartDate}T13:00:00Z`),
}, // 1641042000
{
rfc3339: `${defaultStartDate}T14:00:00Z`,
unix: timeToUnixSeconds(`${defaultStartDate}T14:00:00Z`),
}, // 1641045600
{
rfc3339: `${defaultStartDate}T15:00:00Z`,
unix: timeToUnixSeconds(`${defaultStartDate}T15:00:00Z`),
}, // 1641049200
{
rfc3339: `${defaultStartDate}T16:00:00Z`,
unix: timeToUnixSeconds(`${defaultStartDate}T16:00:00Z`),
}, // 1641052800
{
rfc3339: `${defaultStartDate}T17:00:00Z`,
unix: timeToUnixSeconds(`${defaultStartDate}T17:00:00Z`),
}, // 1641056400
{
rfc3339: `${defaultStartDate}T18:00:00Z`,
unix: timeToUnixSeconds(`${defaultStartDate}T18:00:00Z`),
}, // 1641060000
{
rfc3339: `${defaultStartDate}T19:00:00Z`,
unix: timeToUnixSeconds(`${defaultStartDate}T19:00:00Z`),
}, // 1641063600
{
rfc3339: `${defaultStartDate}T20:00:00Z`,
unix: timeToUnixSeconds(`${defaultStartDate}T20:00:00Z`),
}, // 1641067200
];
// Default time values in getting started sample data
const defaultTimes = [
{
rfc3339: `${defaultStartDate}T08:00:00Z`,
unix: timeToUnixSeconds(`${defaultStartDate}T08:00:00Z`),
}, // 1641024000
{
rfc3339: `${defaultStartDate}T09:00:00Z`,
unix: timeToUnixSeconds(`${defaultStartDate}T09:00:00Z`),
}, // 1641027600
{
rfc3339: `${defaultStartDate}T10:00:00Z`,
unix: timeToUnixSeconds(`${defaultStartDate}T10:00:00Z`),
}, // 1641031200
{
rfc3339: `${defaultStartDate}T11:00:00Z`,
unix: timeToUnixSeconds(`${defaultStartDate}T11:00:00Z`),
}, // 1641034800
{
rfc3339: `${defaultStartDate}T12:00:00Z`,
unix: timeToUnixSeconds(`${defaultStartDate}T12:00:00Z`),
}, // 1641038400
{
rfc3339: `${defaultStartDate}T13:00:00Z`,
unix: timeToUnixSeconds(`${defaultStartDate}T13:00:00Z`),
}, // 1641042000
{
rfc3339: `${defaultStartDate}T14:00:00Z`,
unix: timeToUnixSeconds(`${defaultStartDate}T14:00:00Z`),
}, // 1641045600
{
rfc3339: `${defaultStartDate}T15:00:00Z`,
unix: timeToUnixSeconds(`${defaultStartDate}T15:00:00Z`),
}, // 1641049200
{
rfc3339: `${defaultStartDate}T16:00:00Z`,
unix: timeToUnixSeconds(`${defaultStartDate}T16:00:00Z`),
}, // 1641052800
{
rfc3339: `${defaultStartDate}T17:00:00Z`,
unix: timeToUnixSeconds(`${defaultStartDate}T17:00:00Z`),
}, // 1641056400
{
rfc3339: `${defaultStartDate}T18:00:00Z`,
unix: timeToUnixSeconds(`${defaultStartDate}T18:00:00Z`),
}, // 1641060000
{
rfc3339: `${defaultStartDate}T19:00:00Z`,
unix: timeToUnixSeconds(`${defaultStartDate}T19:00:00Z`),
}, // 1641063600
{
rfc3339: `${defaultStartDate}T20:00:00Z`,
unix: timeToUnixSeconds(`${defaultStartDate}T20:00:00Z`),
}, // 1641067200
];
function updateTimestamps (newStartDate, seedTimes=defaultTimes) {
function updateTimestamps(newStartDate, seedTimes = defaultTimes) {
// Update the times array with replacement times
const times = seedTimes.map(x => {
const times = seedTimes.map((x) => {
var newStartTimestamp = x.rfc3339.replace(/^.*T/, newStartDate + 'T');
return {
@ -178,7 +178,7 @@ function updateTimestamps (newStartDate, seedTimes=defaultTimes) {
/////////////////////// MODAL INTERACTIONS / DATE PICKER ///////////////////////
function CustomTimeTrigger({component}) {
function CustomTimeTrigger({ component }) {
const $component = $(component);
$component
.find('a[data-action="open"]:first')
@ -212,7 +212,7 @@ function CustomTimeTrigger({component}) {
if (newDate != undefined) {
newDate = formatDate(newDate);
// Update the last updated timestamps with the new date
// and reassign the updated times.
updatedTimes = updateTimestamps(newDate, updatedTimes);

View File

@ -6,7 +6,7 @@
*/
import $ from 'jquery';
import * as LocalStorageAPI from './local-storage.js';
import * as LocalStorageAPI from './services/local-storage.js';
// Get notification ID
function getCalloutID(el) {

View File

@ -3,7 +3,6 @@
///////////////////////// INFLUXDB URL PREFERENCE /////////////////////////////
////////////////////////////////////////////////////////////////////////////////
*/
import * as pageParams from '@params';
import {
DEFAULT_STORAGE_URLS,
getPreference,
@ -12,15 +11,18 @@ import {
removeInfluxDBUrl,
getInfluxDBUrl,
getInfluxDBUrls,
} from './local-storage.js';
} from './services/local-storage.js';
import $ from 'jquery';
import { context as PRODUCT_CONTEXT, referrerHost } from './page-context.js';
import { influxdbUrls } from './services/influxdb-urls.js';
import { delay } from './helpers.js';
import { toggleModal } from './modals.js';
let CLOUD_URLS = [];
if (pageParams && pageParams.influxdb_urls) {
CLOUD_URLS = Object.values(pageParams.influxdb_urls.cloud.providers).flatMap((provider) => provider.regions?.map((region) => region.url));
if (influxdbUrls?.cloud) {
CLOUD_URLS = Object.values(influxdbUrls.cloud.providers).flatMap((provider) =>
provider.regions?.map((region) => region.url)
);
}
export { CLOUD_URLS };
@ -28,7 +30,7 @@ export function InfluxDBUrl() {
const UNIQUE_URL_PRODUCTS = ['dedicated', 'clustered'];
const IS_UNIQUE_URL_PRODUCT = UNIQUE_URL_PRODUCTS.includes(PRODUCT_CONTEXT);
// Add actual cloud URLs as needed
// Add actual cloud URLs as needed
const elementSelector = '.article--content pre:not(.preserve)';
///////////////////// Stored preference management ///////////////////////
@ -118,11 +120,12 @@ export function InfluxDBUrl() {
});
}
// Retrieve the currently selected URLs from the urls local storage object.
function getUrls() {
const { cloud, oss, core, enterprise, serverless, dedicated, clustered } = getInfluxDBUrls();
return { oss, cloud, core, enterprise, serverless, dedicated, clustered };
}
// Retrieve the currently selected URLs from the urls local storage object.
function getUrls() {
const { cloud, oss, core, enterprise, serverless, dedicated, clustered } =
getInfluxDBUrls();
return { oss, cloud, core, enterprise, serverless, dedicated, clustered };
}
// Retrieve the previously selected URLs from the from the urls local storage object.
// This is used to update URLs whenever you switch between browser tabs.
@ -289,15 +292,17 @@ export function InfluxDBUrl() {
}
// Append the URL selector button to each codeblock containing a placeholder URL
function appendUrlSelector(urls={
cloud: '',
oss: '',
core: '',
enterprise: '',
serverless: '',
dedicated: '',
clustered: '',
}) {
function appendUrlSelector(
urls = {
cloud: '',
oss: '',
core: '',
enterprise: '',
serverless: '',
dedicated: '',
clustered: '',
}
) {
const appendToUrls = Object.values(urls);
const getBtnText = (context) => {
@ -315,7 +320,7 @@ export function InfluxDBUrl() {
return contextText[context];
};
appendToUrls.forEach(function (url) {
appendToUrls.forEach(function (url) {
$(elementSelector).each(function () {
var code = $(this).html();
if (code.includes(url)) {
@ -330,20 +335,32 @@ export function InfluxDBUrl() {
});
}
////////////////////////////////////////////////////////////////////////////
////////////////// Initialize InfluxDB URL interactions ////////////////////
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
////////////////// Initialize InfluxDB URL interactions ////////////////////
////////////////////////////////////////////////////////////////////////////
// Add the preserve tag to code blocks that shouldn't be updated
addPreserve();
const { cloud, oss, core, enterprise, serverless, dedicated, clustered } = DEFAULT_STORAGE_URLS;
const { cloud, oss, core, enterprise, serverless, dedicated, clustered } =
DEFAULT_STORAGE_URLS;
// Append URL selector buttons to code blocks
appendUrlSelector({ cloud, oss, core, enterprise, serverless, dedicated, clustered });
appendUrlSelector({
cloud,
oss,
core,
enterprise,
serverless,
dedicated,
clustered,
});
// Update URLs on load
updateUrls({ cloud, oss, core, enterprise, serverless, dedicated, clustered }, getUrls());
updateUrls(
{ cloud, oss, core, enterprise, serverless, dedicated, clustered },
getUrls()
);
// Set active radio button on page load
setRadioButtons(getUrls());

View File

@ -1,8 +1,5 @@
// assets/js/main.js
// If you need to pass parameters from the calling Hugo page, you can import them here like so:
// import * as pageParams from '@params';
// Import dependencies that we still need to load in the global scope
import $ from 'jquery';
@ -15,7 +12,7 @@ import * as contentInteractions from './content-interactions.js';
import * as datetime from './datetime.js';
import { delay } from './helpers.js';
import { InfluxDBUrl } from './influxdb-url.js';
import * as localStorage from './local-storage.js';
import * as localStorage from './services/local-storage.js';
import * as modals from './modals.js';
import * as notifications from './notifications.js';
import * as pageContext from './page-context.js';

View File

@ -1,34 +1,80 @@
/** This module retrieves browser context information and site data for the
* current page, version, and product.
*/
import { products, influxdb_urls } from '@params';
const safeProducts = products || {};
const safeUrls = influxdb_urls || {};
import { products } from './services/influxdata-products.js';
import { influxdbUrls } from './services/influxdb-urls.js';
function getCurrentProductData() {
const path = window.location.pathname;
const mappings = [
{ pattern: /\/influxdb\/cloud\//, product: safeProducts.cloud, urls: safeUrls.influxdb_cloud },
{ pattern: /\/influxdb3\/core/, product: safeProducts.influxdb3_core, urls: safeUrls.core },
{ pattern: /\/influxdb3\/enterprise/, product: safeProducts.influxdb3_enterprise, urls: safeUrls.enterprise },
{ pattern: /\/influxdb3\/cloud-serverless/, product: safeProducts.influxdb3_cloud_serverless, urls: safeUrls.cloud },
{ pattern: /\/influxdb3\/cloud-dedicated/, product: safeProducts.influxdb3_cloud_dedicated, urls: safeUrls.dedicated },
{ pattern: /\/influxdb3\/clustered/, product: safeProducts.influxdb3_clustered, urls: safeUrls.clustered },
{ pattern: /\/enterprise_v1\//, product: safeProducts.enterprise_influxdb, urls: safeUrls.oss },
{ pattern: /\/influxdb.*v1\//, product: safeProducts.influxdb, urls: safeUrls.oss },
{ pattern: /\/influxdb.*v2\//, product: safeProducts.influxdb, urls: safeUrls.oss },
{ pattern: /\/kapacitor\//, product: safeProducts.kapacitor, urls: safeUrls.oss },
{ pattern: /\/telegraf\//, product: safeProducts.telegraf, urls: safeUrls.oss },
{ pattern: /\/chronograf\//, product: safeProducts.chronograf, urls: safeUrls.oss },
{ pattern: /\/flux\//, product: safeProducts.flux, urls: safeUrls.oss },
{
pattern: /\/influxdb\/cloud\//,
product: products.cloud,
urls: influxdbUrls.influxdb_cloud,
},
{
pattern: /\/influxdb3\/core/,
product: products.influxdb3_core,
urls: influxdbUrls.core,
},
{
pattern: /\/influxdb3\/enterprise/,
product: products.influxdb3_enterprise,
urls: influxdbUrls.enterprise,
},
{
pattern: /\/influxdb3\/cloud-serverless/,
product: products.influxdb3_cloud_serverless,
urls: influxdbUrls.cloud,
},
{
pattern: /\/influxdb3\/cloud-dedicated/,
product: products.influxdb3_cloud_dedicated,
urls: influxdbUrls.dedicated,
},
{
pattern: /\/influxdb3\/clustered/,
product: products.influxdb3_clustered,
urls: influxdbUrls.clustered,
},
{
pattern: /\/enterprise_v1\//,
product: products.enterprise_influxdb,
urls: influxdbUrls.oss,
},
{
pattern: /\/influxdb.*v1\//,
product: products.influxdb,
urls: influxdbUrls.oss,
},
{
pattern: /\/influxdb.*v2\//,
product: products.influxdb,
urls: influxdbUrls.oss,
},
{
pattern: /\/kapacitor\//,
product: products.kapacitor,
urls: influxdbUrls.oss,
},
{
pattern: /\/telegraf\//,
product: products.telegraf,
urls: influxdbUrls.oss,
},
{
pattern: /\/chronograf\//,
product: products.chronograf,
urls: influxdbUrls.oss,
},
{ pattern: /\/flux\//, product: products.flux, urls: influxdbUrls.oss },
];
for (const { pattern, product, urls } of mappings) {
if (pattern.test(path)) {
return {
product: product || 'unknown',
urls: urls || {}
return {
product: product || 'unknown',
urls: urls || {},
};
}
}
@ -36,7 +82,8 @@ function getCurrentProductData() {
return { product: 'other', urls: {} };
}
// Return the page context (cloud, serverless, oss/enterprise, dedicated, clustered, other)
// Return the page context
// (cloud, serverless, oss/enterprise, dedicated, clustered, other)
function getContext() {
if (/\/influxdb\/cloud\//.test(window.location.pathname)) {
return 'cloud';
@ -78,8 +125,12 @@ const context = getContext(),
protocol = location.protocol,
referrer = document.referrer === '' ? 'direct' : document.referrer,
referrerHost = getReferrerHost(),
// TODO: Verify this still does what we want since the addition of InfluxDB 3 naming and the Core and Enterprise versions.
version = (/^v\d/.test(pathArr[1]) || pathArr[1]?.includes('cloud') ? pathArr[1].replace(/^v/, '') : "n/a")
// TODO: Verify this works since the addition of InfluxDB 3 naming
// and the Core and Enterprise versions.
version =
/^v\d/.test(pathArr[1]) || pathArr[1]?.includes('cloud')
? pathArr[1].replace(/^v/, '')
: 'n/a';
export {
context,
@ -92,4 +143,4 @@ export {
referrer,
referrerHost,
version,
};
};

View File

@ -5,33 +5,23 @@
* release notes pages.
*/
export default function ReleaseToc({ component }) {
// Use jQuery filter to get an array of all the *release* h2 elements
const releases = $('h2').filter(
(_i, el) => !el.id.match(/checkpoint-releases/)
// Get all h2 elements that are not checkpoint-releases
const releases = Array.from(document.querySelectorAll('h2')).filter(
(el) => !el.id.match(/checkpoint-releases/)
);
// Extract data about each release from the array of releases
const releaseData = releases.map((_i, el) => ({
const releaseData = releases.map((el) => ({
name: el.textContent,
id: el.id,
class: el.getAttribute('class'),
date: el.getAttribute('date'),
}));
// Use release data to generate a list item for each release
getReleaseItem = (releaseData) => {
var li = document.createElement('li');
if (releaseData.class !== null) {
li.className = releaseData.class;
}
li.innerHTML = `<a href="#${releaseData.id}">${releaseData.name}</a>`;
li.setAttribute('date', releaseData.date);
return li;
};
// Use jQuery each to build the release table of contents
releaseData.each((_i, release) => {
$('#release-toc ul')[0].appendChild(getReleaseItem(release));
// Build the release table of contents
const releaseTocUl = component.querySelector('#release-toc ul');
releaseData.forEach((release) => {
releaseTocUl.appendChild(getReleaseItem(release));
});
/*
@ -39,21 +29,43 @@ export default function ReleaseToc({ component }) {
* number specified in the `show` attribute of `ul.release-list`.
* Once all the release items are visible, the "Show More" button is hidden.
*/
$('#release-toc .show-more').click(function () {
const itemHeight = 1.885; // Item height in rem
const releaseNum = releaseData.length;
const maxHeight = releaseNum * itemHeight;
const releaseIncrement = Number($('#release-list')[0].getAttribute('show'));
const currentHeight = Number(
$('#release-list')[0].style.height.match(/\d+\.?\d+/)[0]
);
const potentialHeight = currentHeight + releaseIncrement * itemHeight;
const newHeight = potentialHeight > maxHeight ? maxHeight : potentialHeight;
const showMoreBtn = component.querySelector('.show-more');
if (showMoreBtn) {
showMoreBtn.addEventListener('click', function () {
const itemHeight = 1.885; // Item height in rem
const releaseNum = releaseData.length;
const maxHeight = releaseNum * itemHeight;
const releaseList = document.getElementById('release-list');
const releaseIncrement = Number(releaseList.getAttribute('show'));
const currentHeightMatch = releaseList.style.height.match(/\d+\.?\d+/);
const currentHeight = currentHeightMatch
? Number(currentHeightMatch[0])
: 0;
const potentialHeight = currentHeight + releaseIncrement * itemHeight;
const newHeight =
potentialHeight > maxHeight ? maxHeight : potentialHeight;
$('#release-list')[0].style.height = `${newHeight}rem`;
releaseList.style.height = `${newHeight}rem`;
if (newHeight >= maxHeight) {
$('#release-toc .show-more').fadeOut(100);
}
});
if (newHeight >= maxHeight) {
// Simple fade out
showMoreBtn.style.transition = 'opacity 0.1s';
showMoreBtn.style.opacity = 0;
setTimeout(() => {
showMoreBtn.style.display = 'none';
}, 100);
}
});
}
}
// Use release data to generate a list item for each release
function getReleaseItem(releaseData) {
const li = document.createElement('li');
if (releaseData.class !== null) {
li.className = releaseData.class;
}
li.innerHTML = `<a href="#${releaseData.id}">${releaseData.name}</a>`;
li.setAttribute('date', releaseData.date);
return li;
}

View File

@ -0,0 +1,3 @@
import { products as productsParam } from '@params';
export const products = productsParam || {};

View File

@ -0,0 +1,3 @@
import { influxdb_urls as influxdbUrlsParam } from '@params';
export const influxdbUrls = influxdbUrlsParam || {};

View File

@ -10,7 +10,8 @@
- messages: Messages (data/notifications.yaml) that have been seen (array)
- callouts: Feature callouts that have been seen (array)
*/
import * as pageParams from '@params';
import { influxdbUrls } from './influxdb-urls.js';
// Prefix for all InfluxData docs local storage
const storagePrefix = 'influxdata_docs_';
@ -83,16 +84,11 @@ function getPreferences() {
////////////////////////////////////////////////////////////////////////////////
const defaultUrls = {};
// Guard against pageParams being null/undefined and safely access nested properties
if (pageParams && pageParams.influxdb_urls) {
Object.entries(pageParams.influxdb_urls).forEach(
([product, { providers }]) => {
defaultUrls[product] =
providers.filter((provider) => provider.name === 'Default')[0]
?.regions[0]?.url || 'https://cloud2.influxdata.com';
}
);
}
Object.entries(influxdbUrls).forEach(([product, { providers }]) => {
defaultUrls[product] =
providers.filter((provider) => provider.name === 'Default')[0]?.regions[0]
?.url || 'https://cloud2.influxdata.com';
});
export const DEFAULT_STORAGE_URLS = {
oss: defaultUrls.oss,

View File

@ -3,7 +3,7 @@
http://www.thesitewizard.com/javascripts/change-style-sheets.shtml
*/
import * as localStorage from './local-storage.js';
import * as localStorage from './services/local-storage.js';
// *** TO BE CUSTOMISED ***
var sidebar_state_preference_name = 'sidebar_state';

View File

@ -1,4 +1,4 @@
import { getPreference, setPreference } from './local-storage.js';
import { getPreference, setPreference } from './services/local-storage.js';
const PROPS = {
style_preference_name: 'theme',
@ -6,19 +6,22 @@ const PROPS = {
style_domain: 'docs.influxdata.com',
};
function getPreferredTheme () {
function getPreferredTheme() {
return `${getPreference(PROPS.style_preference_name)}-theme`;
}
function switchStyle({ styles_element, css_title }) {
// Disable all other theme stylesheets
styles_element.querySelectorAll('link[rel*="stylesheet"][title*="theme"]')
.forEach(function (link) {
link.disabled = true;
});
styles_element
.querySelectorAll('link[rel*="stylesheet"][title*="theme"]')
.forEach(function (link) {
link.disabled = true;
});
// Enable the stylesheet with the specified title
const link = styles_element.querySelector(`link[rel*="stylesheet"][title="${css_title}"]`);
const link = styles_element.querySelector(
`link[rel*="stylesheet"][title="${css_title}"]`
);
link && (link.disabled = false);
setPreference(PROPS.style_preference_name, css_title.replace(/-theme/, ''));
@ -38,5 +41,4 @@ export default function Theme({ component, style }) {
if (component.dataset?.themeCallback === 'setVisibility') {
setVisibility(component);
}
}

View File

@ -1,6 +1,14 @@
import { CLOUD_URLS } from './influxdb-url.js';
import * as localStorage from './local-storage.js';
import { context, host, hostname, path, protocol, referrer, referrerHost } from './page-context.js';
import * as localStorage from './services/local-storage.js';
import {
context,
host,
hostname,
path,
protocol,
referrer,
referrerHost,
} from './page-context.js';
/**
* Builds a referrer whitelist array that includes the current page host and all
@ -69,8 +77,6 @@ function setWayfindingInputState() {
}
function submitWayfindingData(engine, action) {
// Build lp using page data and engine data
const lp = `ioxwayfinding,host=${hostname},path=${path},referrer=${referrer},engine=${engine} action="${action}"`;
@ -81,10 +87,7 @@ function submitWayfindingData(engine, action) {
'https://j32dswat7l.execute-api.us-east-1.amazonaws.com/prod/wayfinding'
);
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
xhr.setRequestHeader(
'Access-Control-Allow-Origin',
`${protocol}//${host}`
);
xhr.setRequestHeader('Access-Control-Allow-Origin', `${protocol}//${host}`);
xhr.setRequestHeader('Content-Type', 'text/plain; charset=utf-8');
xhr.setRequestHeader('Accept', 'application/json');
xhr.send(lp);

View File

@ -1,2 +0,0 @@
import:
- hugo.yml

View File

@ -1,4 +1,4 @@
baseURL: 'https://docs.influxdata.com/'
baseURL: https://docs.influxdata.com/
languageCode: en-us
title: InfluxDB Documentation
@ -58,7 +58,11 @@ outputFormats:
# Asset processing configuration for development
build:
writeStats: false
# Ensure Hugo correctly processes JavaScript modules
jsConfig:
nodeEnv: "development"
# Development asset processing
writeStats: false
useResourceCacheWhen: "fallback"
noJSConfigInAssets: false
@ -72,7 +76,7 @@ module:
- source: node_modules
target: assets/node_modules
# Development settings
# Environment parameters
params:
env: development
environment: development

View File

@ -0,0 +1,17 @@
build:
writeStats: false
useResourceCacheWhen: "fallback"
buildOptions:
sourcemap: false
target: "es2015"
minify:
disableJS: false
disableCSS: false
disableHTML: false
minifyOutput: true
params:
env: production
environment: production
server: {
disableLiveReload: true
}

18
config/staging/hugo.yml Normal file
View File

@ -0,0 +1,18 @@
baseURL: https://test2.docs.influxdata.com/
build:
writeStats: false
useResourceCacheWhen: "fallback"
buildOptions:
sourcemap: false
target: "es2015"
minify:
disableJS: false
disableCSS: false
disableHTML: false
minifyOutput: true
params:
env: staging
environment: staging
server:
disableLiveReload: true

View File

@ -1,106 +0,0 @@
baseURL: 'http://localhost:1315/'
languageCode: en-us
title: InfluxDB Documentation
# Git history information for lastMod-type functionality
enableGitInfo: true
# Syntax Highlighting
pygmentsCodefences: true
pygmentsUseClasses: true
# Preserve case in article tags
preserveTaxonomyNames: true
# Generate a robots.txt
enableRobotsTXT: true
# Markdown rendering options
blackfriday:
hrefTargetBlank: true
smartDashes: false
# Copy taxonomies from main config
taxonomies:
influxdb/v2/tag: influxdb/v2/tags
influxdb/cloud/tag: influxdb/cloud/tags
influxdb3/cloud-serverless/tag: influxdb/cloud-serverless/tags
influxdb3/cloud-dedicated/tag: influxdb/cloud-dedicated/tags
influxdb3/clustered/tag: influxdb3/clustered/tags
influxdb3/core/tag: influxdb3/core/tags
influxdb3/enterprise/tag: influxdb3/enterprise/tags
flux/v0/tag: flux/v0/tags
markup:
goldmark:
renderer:
unsafe: true
extensions:
linkify: false
parser:
attribute:
block: true
privacy:
googleAnalytics:
anonymizeIP: false
disable: false
respectDoNotTrack: true
useSessionStorage: false
youtube:
disable: false
privacyEnhanced: true
outputFormats:
json:
mediaType: application/json
baseName: pages
isPlainText: true
# Asset processing for testing (disable minification)
build:
writeStats: false
useResourceCacheWhen: "fallback"
noJSConfigInAssets: false
# Asset processing configuration
assetDir: "assets"
module:
mounts:
- source: assets
target: assets
- source: node_modules
target: assets/node_modules
# Override settings for testing
buildFuture: true
# Configure what content is built in testing env
params:
env: testing
environment: testing
buildTestContent: true
# Configure the server for testing
server:
port: 1315
baseURL: 'http://localhost:1315/'
watchChanges: true
disableLiveReload: false
# Keep your shared content exclusions
ignoreFiles:
- "content/shared/.*"
# Ignore specific warning logs
ignoreLogs:
- warning-goldmark-raw-html
# Disable minification for testing
minify:
disableJS: true
disableCSS: true
disableHTML: true
minifyOutput: false

View File

@ -2,8 +2,10 @@ import { spawn } from 'child_process';
import fs from 'fs';
import http from 'http';
import net from 'net';
import process from 'process';
// Hugo server constants
export const HUGO_ENVIRONMENT = 'testing';
export const HUGO_PORT = 1315;
export const HUGO_LOG_FILE = '/tmp/hugo_server.log';
@ -28,7 +30,8 @@ export async function isPortInUse(port) {
/**
* Start the Hugo server with the specified options
* @param {Object} options - Configuration options for Hugo
* @param {string} options.configFile - Path to Hugo config file (e.g., 'config/testing/config.yml')
* @param {string} options.configFile - Path to Hugo config file
* @param {string} options.environment - Environment to run Hugo in
* @param {number} options.port - Port number for Hugo server
* @param {boolean} options.buildDrafts - Whether to build draft content
* @param {boolean} options.noHTTPCache - Whether to disable HTTP caching
@ -36,9 +39,10 @@ export async function isPortInUse(port) {
* @returns {Promise<Object>} Child process object
*/
export async function startHugoServer({
configFile = 'config/testing/config.yml',
configFile = 'config/_default/hugo.yml',
port = HUGO_PORT,
buildDrafts = true,
environment = 'testing',
buildDrafts = false,
noHTTPCache = true,
logFile = HUGO_LOG_FILE,
} = {}) {
@ -48,6 +52,8 @@ export async function startHugoServer({
const hugoArgs = [
'hugo',
'server',
'--environment',
environment,
'--config',
configFile,
'--port',
@ -64,16 +70,16 @@ export async function startHugoServer({
return new Promise((resolve, reject) => {
try {
// Use npx to find and execute Hugo, which will work regardless of installation method
console.log(`Running Hugo with npx: npx ${hugoArgs.join(' ')}`);
const hugoProc = spawn('npx', hugoArgs, {
// Use yarn to find and execute Hugo, which will work regardless of installation method
console.log(`Running Hugo with yarn: yarn ${hugoArgs.join(' ')}`);
const hugoProc = spawn('yarn', hugoArgs, {
stdio: ['ignore', 'pipe', 'pipe'],
shell: true,
});
// Check if the process started successfully
if (!hugoProc || !hugoProc.pid) {
return reject(new Error('Failed to start Hugo server via npx'));
return reject(new Error('Failed to start Hugo server via yarn'));
}
// Set up logging

View File

@ -38,9 +38,10 @@ import fs from 'fs';
import path from 'path';
import cypress from 'cypress';
import net from 'net';
import matter from 'gray-matter';
import { Buffer } from 'buffer';
import { displayBrokenLinksReport, initializeReport } from './link-reporter.js';
import {
HUGO_ENVIRONMENT,
HUGO_PORT,
HUGO_LOG_FILE,
startHugoServer,
@ -90,28 +91,6 @@ async function isPortInUse(port) {
});
}
/**
* Extract source information from frontmatter
* @param {string} filePath - Path to the markdown file
* @returns {string|null} Source information if present
*/
function getSourceFromFrontmatter(filePath) {
if (!fs.existsSync(filePath)) {
return null;
}
try {
const fileContent = fs.readFileSync(filePath, 'utf8');
const { data } = matter(fileContent);
return data.source || null;
} catch (err) {
console.warn(
`Warning: Could not extract frontmatter from ${filePath}: ${err.message}`
);
return null;
}
}
/**
* Ensures a directory exists, creating it if necessary
* Also creates an empty file to ensure the directory is not empty
@ -296,7 +275,7 @@ async function main() {
});
console.log('Hugo is available on the system');
} catch (checkErr) {
} catch {
console.log(
'Hugo not found on PATH, will use project-local Hugo via yarn'
);
@ -304,9 +283,8 @@ async function main() {
// Use the startHugoServer function from hugo-server.js
hugoProc = await startHugoServer({
configFile: 'config/testing/config.yml',
environment: HUGO_ENVIRONMENT,
port: HUGO_PORT,
buildDrafts: true,
noHTTPCache: true,
logFile: HUGO_LOG_FILE,
});
@ -412,7 +390,7 @@ async function main() {
` Note: ${testFailureCount} test(s) failed but no broken links were detected in the report.`
);
console.warn(
` This usually indicates test errors unrelated to link validation.`
' This usually indicates test errors unrelated to link validation.'
);
// We should not consider special case domains (those with expected errors) as failures

View File

@ -1,79 +0,0 @@
baseURL: https://test2.docs.influxdata.com/
languageCode: en-us
title: InfluxDB Documentation
# Git history information for lastMod-type functionality
enableGitInfo: true
# Syntax Highlighting
pygmentsCodefences: true
pygmentsUseClasses: true
# Preserve case in article tags
preserveTaxonomyNames: true
# Generate a robots.txt
enableRobotsTXT: true
# Custom staging params
params:
env: staging
environment: staging
server:
disableLiveReload: true
# Staging uses the same asset processing as production
# Enable minification for staging
minify:
disableJS: false
disableCSS: false
disableHTML: false
minifyOutput: true
# Staging asset processing
build:
writeStats: false
useResourceCacheWhen: "always"
buildOptions:
sourcemap: false
target: "es2015"
# Markdown rendering options
blackfriday:
hrefTargetBlank: true
smartDashes: false
taxonomies:
influxdb/v2/tag: influxdb/v2/tags
influxdb/cloud/tag: influxdb/cloud/tags
influxdb3/cloud-serverless/tag: influxdb/cloud-serverless/tags
influxdb/cloud-dedicated/tag: influxdb/cloud-dedicated/tags
influxdb/clustered/tag: influxdb/clustered/tags
influxdb3/core/tag: influxdb3/core/tags
influxdb3/enterprise/tag: influxdb3/enterprise/tags
flux/v0/tag: flux/v0/tags
markup:
goldmark:
renderer:
unsafe: true
extensions:
linkify: false
parser:
attribute:
block: true
privacy:
googleAnalytics:
anonymizeIP: false
disable: false
respectDoNotTrack: true
useSessionStorage: false
youtube:
disable: false
privacyEnhanced: true
outputFormats:
json:
mediaType: application/json
baseName: pages
isPlainText: true

View File

@ -13,96 +13,49 @@
{{ $products := .Site.Data.products }}
{{ $influxdb_urls := .Site.Data.influxdb_urls }}
{{ $isDevelopmentOrTesting := or (eq .Site.Params.environment "development") (eq .Site.Params.environment "testing") (eq (getenv "HUGO_ENV") "development") (eq (getenv "HUGO_ENV") "testing") (not hugo.IsProduction) }}
<!-- Build main.js -->
{{ $isDevelopment := false }}
{{ $isTesting := false }}
{{ if $isDevelopmentOrTesting }}
{{/* Load individual JS files for debugging with ESM format */}}
{{ $sharedParams := dict "product" $product "currentVersion" $currentVersion "isServer" hugo.IsServer "products" $products "influxdb_urls" $influxdb_urls "cloudUrls" $cloudUrls }}
{{/* Load main.js first to ensure proper initialization */}}
{{ $mainJS := resources.Get "js/main.js" }}
{{ if $mainJS }}
{{ $opts := dict
"sourceMap" "external"
"sourcesContent" true
"targetPath" "js/main.js"
"format" "esm"
"bundle" false
"minify" false
"target" "es2017"
"write" true
"params" $sharedParams
}}
{{ $processed := $mainJS | js.Build $opts }}
{{ if $processed }}
<script type="module" src="{{ $processed.RelPermalink }}"></script>
{{ end }}
{{ with hugo }}
{{ if .IsDevelopment }}
{{ $isDevelopment = .IsDevelopment }}
{{ end }}
{{/* Load other individual JS files for debugging with error handling */}}
{{ $jsDir := "assets/js" }}
{{ if fileExists $jsDir }}
{{ range $file := (readDir $jsDir) }}
{{ if and (strings.HasSuffix $file.Name ".js") (ne $file.Name "main.js") }}
{{ $jsPath := printf "js/%s" $file.Name }}
{{ $jsRes := resources.Get $jsPath }}
{{ with $jsRes }}
{{ $opts := dict
"sourceMap" ""
"targetPath" $jsPath
"format" "esm"
"bundle" false
"minify" false
"target" "es2015"
"write" true
"params" $sharedParams
}}
{{ $out := . | js.Build $opts }}
{{/* Add descriptive debug comments at the beginning of each file */}}
{{ $debugHeader := printf "// DEBUG MODE: %s\n// Original file: assets/js/%s\n\n" $file.Name $file.Name }}
{{ $modifiedContent := printf "%s%s" $debugHeader $out.Content }}
{{ $out = resources.FromString $jsPath $modifiedContent }}
{{ end }}
<script type="module" src="{{ $out.RelPermalink }}"></script>
{{ end }}
{{ end }}
{{ end }}
{{ end }}
{{ else }}
{{/* Production environment: Use IIFE for better compatibility */}}
{{ $mainJS := resources.Get "js/main.js" }}
{{ if $mainJS }}
{{ $sharedParams := dict "product" $product "currentVersion" $currentVersion "isServer" hugo.IsServer "products" $products "influxdb_urls" $influxdb_urls "cloudUrls" $cloudUrls }}
{{ $opts := dict
"minify" hugo.IsProduction
"sourceMap" ""
"targetPath" "js/main.js"
"params" $sharedParams
"format" "iife"
"target" "es2019"
"splitting" false
"external" (slice "*")
"define" (dict
"process.env.NODE_ENV" "\"production\""
)
}}
{{ $processed := "" }}
{{ with $mainJS }}
{{ $processed = . | js.Build $opts }}
{{ end }}
{{ if $processed }}
{{ if hugo.IsProduction }}
{{ $fingerprinted := $processed | fingerprint }}
{{ if $fingerprinted }}
<script defer src="{{ $fingerprinted.RelPermalink }}" integrity="{{ $fingerprinted.Data.Integrity }}" crossorigin="anonymous"></script>
{{ end }}
{{ else }}
<script defer src="{{ $processed.RelPermalink }}"></script>
{{ if eq .Site.Params.env "testing" }}
{{ $isTesting = true }}
{{ end }}
{{ if eq .Site.Params.environment "testing" }}
{{ $isTesting = true }}
{{ end }}
{{ $isDevelopmentOrTesting := or $isDevelopment $isTesting }}
{{ with resources.Get "js/main.js" }}
{{ $opts := dict
"minify" hugo.IsProduction
"sourceMap" (cond $isDevelopmentOrTesting "inline" "")
"format" (cond $isDevelopmentOrTesting "esm" "iife")
"bundle" true
"targetPath" "js/main.js"
"params" (dict
"product" $product
"currentVersion" $currentVersion
"isServer" hugo.IsServer
"products" $products
"influxdb_urls" $influxdb_urls
"cloudUrls" $cloudUrls
"isDevelopment" $isDevelopmentOrTesting
)
}}
{{ with . | js.Build $opts }}
{{ if hugo.IsProduction }}
{{ with . | fingerprint }}
<script src="{{ .RelPermalink }}" integrity="{{ .Data.Integrity }}" crossorigin="anonymous"></script>
{{ end }}
{{ else }}
<script type="module" src="{{ .RelPermalink }}"></script>
{{ end }}
{{ end }}
{{ end }}