Skip to content

Commit

Permalink
Merge pull request #1656 from Sitecore/feature/JSS-747
Browse files Browse the repository at this point in the history
[Nextjs] Allow empty string for getPublicUrl
  • Loading branch information
yavorsk authored Nov 23, 2023
2 parents 401f9b4 + 46002f0 commit 7b26ef9
Show file tree
Hide file tree
Showing 15 changed files with 83 additions and 61 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ Our versioning strategy is as follows:
* `[templates/nextjs]` Fix custom headers. Now in cors-header plugin extends custom headers from next.config.js file. ([#1637](https://github.com/Sitecore/jss/pull/1637))
* `[sitecore-jss-react]` Fix PlaceholderCommon with using two and more dynamic placeholders. ([#1641](https://github.com/Sitecore/jss/pull/1641))
* `[templates/nextjs-multisite]` Fix site info fetch errors (now skipped) on XM Cloud rendering/editing host builds. ([#1649](https://github.com/Sitecore/jss/pull/1649)), ([#1653](https://github.com/Sitecore/jss/pull/1653))

* `[templates/nextjs]` `[sitecore-jss-nextjs]` Fix making a fetch to a nextjs api route in an editing environment, by adding additional variable publicUrl in runtime config ([#1656](https://github.com/Sitecore/jss/pull/1656))
*
### 🛠 Breaking Changes

* `[create-sitecore-jss]` The `nextjs-personalize` initializer add-on template has been removed and is replaced by the `nextjs-xmcloud` initializer template. You can use the interactive prompts or the `--xmcloud` argument to include this template. ([#1653](https://github.com/Sitecore/jss/pull/1653))
Expand All @@ -49,6 +50,7 @@ Our versioning strategy is as follows:
* _PersonalizeMiddleware_ has transitioned to utilizing the _CloudSDK_ package, replacing the previous dependency on _Engage_.
* Introduced _Context_ class, that is used to initialize the application Context and shared Software Development Kits (SDKs). Accessible within the _@sitecore-jss/sitecore-jss-nextjs/context_ submodule.
* Point of Sale resolution is fully removed, now it's handled by Sitecore Edge Proxy
* `[templates/nextjs]` `[sitecore-jss-nextjs]` The behavior of getPublicUrl() function has been changed - empty string is now considered valid value for PUBLIC_URL environment variable and, if defined, PUBLIC_URL will take precedence over the Vercel/Netlify env variables; the values of these variables should be adjusted as needed; PUBLIC_URL is commented out by default in .env; ([#1656](https://github.com/Sitecore/jss/pull/1656));

## 21.5.0

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import Link from 'next/link';
import { useI18n } from 'next-localization';
import { getPublicUrl } from '@sitecore-jss/sitecore-jss-nextjs/utils';
import config from 'temp/config';

// Prefix public assets with a public URL to enable compatibility with Sitecore Experience Editor.
// If you're not supporting the Experience Editor, you can remove this.
const publicUrl = getPublicUrl();
// Prefix public assets with a public URL to enable compatibility with Sitecore editors.
// If you're not supporting Sitecore editors, you can remove this.
const publicUrl = config.publicUrl;

const Navigation = (): JSX.Element => {
const { t } = useI18n();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import Link from 'next/link';
import { useI18n } from 'next-localization';
import { getPublicUrl } from '@sitecore-jss/sitecore-jss-nextjs/utils';
import config from 'temp/config';

// Prefix public assets with a public URL to enable compatibility with Sitecore Experience Editor.
// If you're not supporting the Experience Editor, you can remove this.
const publicUrl = getPublicUrl();
// Prefix public assets with a public URL to enable compatibility with Sitecore editors.
// If you're not supporting Sitecore editors, you can remove this.
const publicUrl = config.publicUrl;

const Navigation = (): JSX.Element => {
const { t } = useI18n();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,13 @@
*/
import React from 'react';
import Head from 'next/head';
import {
Placeholder,
LayoutServiceData,
Field,
HTMLLink,
} from '@sitecore-jss/sitecore-jss-nextjs';
import { getPublicUrl } from '@sitecore-jss/sitecore-jss-nextjs/utils';
import { Placeholder, LayoutServiceData, Field, HTMLLink } from '@sitecore-jss/sitecore-jss-nextjs';
import config from 'temp/config';
import Scripts from 'src/Scripts';

// Prefix public assets with a public URL to enable compatibility with Sitecore Experience Editor.
// If you're not supporting the Experience Editor, you can remove this.
const publicUrl = getPublicUrl();
const publicUrl = config.publicUrl;

interface LayoutProps {
layoutData: LayoutServiceData;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { AxiosDataFetcher, GraphQLSitemapXmlService, AxiosResponse } from '@sitecore-jss/sitecore-jss-nextjs';
import { getPublicUrl } from '@sitecore-jss/sitecore-jss-nextjs/utils';
import { siteResolver } from 'lib/site-resolver';
import config from 'temp/config';

Expand Down Expand Up @@ -51,14 +50,16 @@ const sitemapApi = async (
if (!sitemaps.length) {
return res.redirect('/404');
}


const reqtHost = req.headers.host;
const reqProtocol = req.headers['x-forwarded-proto'] || 'https';
const SitemapLinks = sitemaps
.map((item) => {
const parseUrl = item.split('/');
const lastSegment = parseUrl[parseUrl.length - 1];

return `<sitemap>
<loc>${getPublicUrl()}/${lastSegment}</loc>
<loc>${reqProtocol}://${reqtHost}/${lastSegment}</loc>
</sitemap>`;
})
.join('');
Expand Down
4 changes: 3 additions & 1 deletion packages/create-sitecore-jss/src/templates/nextjs/.env
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
# in your Sitecore configuration (see \sitecore\config\<%- appName %>.config).
# Be sure to update these values accordingly as your public endpoint changes.
# See https://jss.sitecore.com/docs/fundamentals/services/view-engine
PUBLIC_URL=http://localhost:3000
# If undefined, http://localhost:3000 is used by default.
# In production non-editing environments it is desirable to use relative URLs, so this may be set to empty string.
#PUBLIC_URL=

# To secure the Sitecore editor endpoint exposed by your Next.js app
# (`/api/editing/render` by default), a secret token is used. This (client-side)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
const jssConfig = require('./src/temp/config');
const { getPublicUrl } = require('@sitecore-jss/sitecore-jss-nextjs/utils');
const plugins = require('./src/temp/next-config-plugins') || {};

const publicUrl = getPublicUrl();
const publicUrl = jssConfig.publicUrl;

/**
* @type {import('next').NextConfig}
Expand All @@ -27,7 +26,7 @@ const nextConfig = {
// prefixed path e.g. `/styleguide`.
defaultLocale: jssConfig.defaultLanguage,
},

// Enable React Strict Mode
reactStrictMode: true,

Expand All @@ -53,12 +52,12 @@ const nextConfig = {
{
source: '/sitecore/service/:path*',
destination: `${jssConfig.sitecoreApiHost}/sitecore/service/:path*`,
},
},
];
},
};

module.exports = () => {
// Run the base config through any configured plugins
return Object.values(plugins).reduce((acc, plugin) => plugin(acc), nextConfig);
}
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { JssConfig } from 'lib/config';
import { ConfigPlugin } from '..';
import { getPublicUrl } from '@sitecore-jss/sitecore-jss-nextjs/utils';

/**
* This config will set fallback values for properties that were left empty
Expand All @@ -14,6 +15,7 @@ class FallbackPlugin implements ConfigPlugin {
defaultLanguage: config.defaultLanguage || 'en',
sitecoreApiKey: config.sitecoreApiKey || 'no-api-key-set',
layoutServiceConfigurationName: config.layoutServiceConfigurationName || 'default',
publicUrl: config.publicUrl || getPublicUrl(),
});
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const defaultConfig: JssConfig = {
defaultLanguage: process.env[`${constantCase('defaultLanguage')}`],
graphQLEndpoint: process.env[`${constantCase('graphQLEndpoint')}`],
layoutServiceConfigurationName: process.env[`${constantCase('layoutServiceConfigurationName')}`],
publicUrl: process.env[`${constantCase('publicUrl')}`],
};

generateConfig(defaultConfig);
Expand Down Expand Up @@ -56,6 +57,7 @@ const config = {};\n`;
Object.keys(config).forEach(prop => {
configText += `config.${prop} = process.env.${constantCase(prop)} || '${config[prop]}',\n`;
});

configText += `module.exports = config;`;

const configPath = path.resolve('src/temp/config.js');
Expand Down
11 changes: 3 additions & 8 deletions packages/create-sitecore-jss/src/templates/nextjs/src/Layout.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
import React from 'react';
import Head from 'next/head';
import {
Placeholder,
LayoutServiceData,
Field,
HTMLLink,
} from '@sitecore-jss/sitecore-jss-nextjs';
import { getPublicUrl } from '@sitecore-jss/sitecore-jss-nextjs/utils';
import { Placeholder, LayoutServiceData, Field, HTMLLink } from '@sitecore-jss/sitecore-jss-nextjs';
import config from 'temp/config';
import Navigation from 'src/Navigation';
import Scripts from 'src/Scripts';

// Prefix public assets with a public URL to enable compatibility with Sitecore editors.
// If you're not supporting Sitecore editors, you can remove this.
const publicUrl = getPublicUrl();
const publicUrl = config.publicUrl;

interface LayoutProps {
layoutData: LayoutServiceData;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { getPublicUrl } from '@sitecore-jss/sitecore-jss-nextjs/utils';
import config from 'temp/config';

// Prefix public assets with a public URL to enable compatibility with Sitecore editors.
// If you're not supporting Sitecore editors, you can remove this.
const publicUrl = getPublicUrl();
const publicUrl = config.publicUrl;

const Navigation = (): JSX.Element => (
<div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ export interface JssConfig extends Record<string, string | undefined> {
defaultLanguage?: string;
graphQLEndpoint?: string;
layoutServiceConfigurationName?: string;
publicUrl?: string;
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,27 @@ const corsHeaderPlugin = (nextConfig = {}) => {
}
return Object.assign({}, nextConfig, {
async headers() {
const extendHeaders = typeof nextConfig.headers === "function" ? await nextConfig.headers() : [];
const extendHeaders =
typeof nextConfig.headers === 'function' ? await nextConfig.headers() : [];
return [
...await extendHeaders,
...(await extendHeaders),
{
source: '/_next/:path*',
headers: [{ key: 'Access-Control-Allow-Origin', value: config.sitecoreApiHost }],
headers: [
{
key: 'Access-Control-Allow-Origin',
value: config.sitecoreApiHost.replace(/\/$/, ''),
},
],
},
{
source: '/api/:path*',
headers: [
{
key: 'Access-Control-Allow-Origin',
value: config.sitecoreApiHost.replace(/\/$/, ''),
},
],
},
];
},
Expand Down
29 changes: 23 additions & 6 deletions packages/sitecore-jss-nextjs/src/utils/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { getPublicUrl, getJssEditingSecret } from './utils';

describe('utils', () => {
describe('getPublicUrl', () => {
after(() => {
afterEach(() => {
delete process.env.PUBLIC_URL;
delete process.env.VERCEL_URL;
});
Expand All @@ -28,15 +28,32 @@ describe('utils', () => {
expect(result).to.equal('http://test.com/foo');
});

it('should throw for invalid URL', () => {
process.env.PUBLIC_URL = 'nope';
expect(() => getPublicUrl()).to.throw();
it('should use VERCEL_URL if PUBLIC_URL is not defined', () => {
process.env.VERCEL_URL = 'jss.uniqueid.vercel.com';
const result = getPublicUrl();
expect(result).to.equal('https://jss.uniqueid.vercel.com');
});

it('should use VERCEL_URL', () => {
it('should use PUBLIC_URL if PUBLIC_URL and VERCEL_URL are defined', () => {
process.env.VERCEL_URL = 'jss.uniqueid.vercel.com';
process.env.PUBLIC_URL = 'http://test.com';
const result = getPublicUrl();
expect(result).to.equal('https://jss.uniqueid.vercel.com');
expect(result).to.equal('http://test.com');
});

it('should use PUBLIC_URL if it is an empty string and VERCEL_URL is defined', () => {
process.env.VERCEL_URL = 'jss.uniqueid.vercel.com';
const publicUrl = '';
process.env.PUBLIC_URL = publicUrl;
const result = getPublicUrl();
expect(result).to.equal(publicUrl);
});

it('should use PUBLIC_URL if it is defined and is empty string', () => {
const publicUrl = '';
process.env.PUBLIC_URL = publicUrl;
const result = getPublicUrl();
expect(result).to.equal(publicUrl);
});
});
describe('getJssEditingSecret', () => {
Expand Down
23 changes: 7 additions & 16 deletions packages/sitecore-jss-nextjs/src/utils/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import chalk from 'chalk';
import { isEditorActive, resetEditorChromes } from '@sitecore-jss/sitecore-jss/utils';

/**
Expand All @@ -8,28 +7,20 @@ import { isEditorActive, resetEditorChromes } from '@sitecore-jss/sitecore-jss/u
* VERCEL_URL is provided by Vercel in case if we are in Preview deployment (deployment based on the custom branch),
* preview deployment has unique url, we don't know exact url.
* Similarly, DEPLOY_URL is provided by Netlify and would give us the deploy URL
* In production non-editing environments it is desirable to use relative urls, so in that case set PUBLIC_URL = ''
*/
export const getPublicUrl = (): string => {
if (process.env.NETLIFY && process.env.DEPLOY_URL) return process.env.DEPLOY_URL;
if (process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}`;

let url = process.env.PUBLIC_URL;

if (url === undefined) {
console.warn(
`${chalk.yellow.bold(
'Warning:'
)} An PUBLIC_URL environment variable is not defined. Falling back to http://localhost:3000.`
);
if (process.env.NETLIFY && process.env.DEPLOY_URL) return process.env.DEPLOY_URL;
if (process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}`;

url = 'http://localhost:3000';
} else {
try {
new URL(url);
} catch (error) {
throw new Error(`The PUBLIC_URL environment variable '${url}' is not a valid URL.`);
}
}

// Ensure no trailing slash
return url.toString().replace(/\/$/, '');
return url.replace(/\/$/, '');
};

/**
Expand Down

0 comments on commit 7b26ef9

Please sign in to comment.