2

I'm having an issue with Sitecore JSS, where I'm upgrading from Vue 2 to Vue 3. After clicking on the specific form builder, this is the error that I receive:

enter image description here

I can't seem to find the token it's referring to.

Vue3 Server.js Server.js

import { ErrorMessages } from '@/constants.js';
import { renderToString } from '@vue/server-renderer';
import serializeJavascript from 'serialize-javascript';
import { renderMetaToString } from 'vue-meta/ssr';
import i18ninit from '../src/i18n';
import { createApp } from '../src/createApp';
import { createRouter } from '../src/routing/router';
import indexTemplate from '../dist/index.html';
import { getStates } from '@vue/apollo-ssr';
import config from '../src/temp/config';
import componentFactory from '../src/temp/componentFactory';
/** Asserts that a string replace actually replaced something */
function assertReplace(string, value, replacement) {
  let success = false;
  const result = string.replace(value, () => {
    success = true;
    return replacement;
  });

  if (!success) {
    throw new Error(
      ErrorMessages.NO_MATCH_REPLACE_TOKEN + `${value}` + ErrorMessages.SERVER_SIDE_RENDERING_FAILED
    );
  }

  return result;
}

/** Export the API key. This will be used by default in Headless mode, removing the need to manually configure the API key on the proxy. */
export const apiKey = config.sitecoreApiKey;

/** Export the app name. This will be used by default in Headless mode, removing the need to manually configure the app name on the proxy. */
export const appName = config.jssAppName;

/**
 * Main entry point to the application when run via Server-Side Rendering,
 * either in Integrated Mode, or with a Node proxy host like the node-headless-ssr-proxy sample.
 * This function will be invoked by the server to return the rendered HTML.
 * @param {Function} callback Function to call when rendering is complete. Signature callback(error, successData).
 * @param {string} path Current route path being rendered
 * @param {string} data JSON Layout service data for the rendering from Sitecore
 * @param {string} viewBag JSON view bag data from Sitecore (extensible context stuff)
 */
export function renderView(callback, path, data, viewBag) {
  try {
    const state = parseServerData(data, viewBag);

    const ctx = {};

    initializei18n(state)
      .then((i18n) => createApp(state, i18n, null))
      .then(({ app, router, graphQLProvider }) => {
        // set server-side router's location
        router.push(path);

        // since there could potentially be asynchronous route hooks or components,
        // we will be returning a Promise so that we can wait until everything is ready before rendering.
        return router.isReady().then(() => {
          // `renderToString` expects an actual `App` instance
          // `renderToString` returns a promise
          return renderToString(app, ctx).then((renderedApp) => {
            // We add the GraphQL state to the SSR state so that we can avoid refetching queries after client load
            // Not using GraphQL? Get rid of this.
            state.APOLLO_STATE = getStates({ state: graphQLProvider }).state;
            return {
              renderedApp,
              app,
            };
          });
        });
      })
      .then(async ({ app, renderedApp }) => {
        const meta = await renderMetaToString(app, ctx);

        // We remove the viewBag from the server-side state before sending it back to the client.
        // This saves bandwidth, because by default the viewBag contains the translation dictionary,
        // which is better cached as a separate client HTTP request than on every page, and HTTP context
        // information that is not meaningful to the client-side rendering.
        // If you wish to place items in the viewbag that are needed by client-side rendering, this
        // can be removed - but still delete state.viewBag.dictionary, at least.
        delete state.viewBag;

        // Inject the rendered app into the index.html template (built from /public/index.html)
        // IMPORTANT: use serialize-javascript or similar instead of JSON.stringify() to emit initial state,
        // or else you're vulnerable to XSS.
        let html = indexTemplate;
        // write the Vue app
        html = assertReplace(
          html,
          '<div id="app_forms" data-standalone="true"></div>',
          `<div id="app_forms" data-standalone="true">${renderedApp}</div>`
        );
        // write the string version of our state
        html = assertReplace(
          html,
          '<script type="application/json" id="__JSS_STATE__">null',
          `<script type="application/json" id="__JSS_STATE__">${serializeJavascript(state, {
            isJSON: true,
          })}`
        );
        // render vue-meta data
        html = assertReplace(
          html,
          '<html',
          `<html data-vue-meta-server-rendered ${meta.htmlAttrs.text()}`
        );
        html = assertReplace(
          html,
          '<head>',
          `<head>
            ${meta.meta.text()}
            ${meta.title.text()}
            ${meta.link.text()}
            ${meta.style.text()}
            ${meta.script.text()}
            ${meta.noscript.text()}
          </head>`
        );
        html = assertReplace(html, '<body>', `<body ${meta.bodyAttrs.text()}>`);

        callback(null, {
          html,
        });
      })
      .catch((error) => callback(error, null));
  } catch (err) {
    // need to ensure the callback is always invoked no matter what
    // or else SSR will hang
    callback(err, null);
  }
}

/**
 * Parses an incoming url to match against the route table. This function is implicitly used
 * by node-headless-ssr-proxy when rendering the site in headless mode. It enables rewriting the incoming path,
 * say '/en-US/hello', to the path and language to pass to Layout Service (a Sitecore item path), say
 * { sitecoreRoute: '/hello', lang: 'en-US' }.
 * This function is _not_ used in integrated mode, as Sitecore's built in route parsing is used.
 * If no URL transformations are required (i.e. single language site), then this function can be removed.
 * @param {string} url The incoming URL to the proxy server
 * @returns { sitecoreRoute?: string, lang?: string }
 */
export function parseRouteUrl(url) {
  if (!url) {
    return null;
  }

  // use vue-router to find the route matching the incoming URL then return its match params.
  // note: createRouter() creates a router with mode set to 'history'. This is for client-side rendering.
  // vue-router will automatically switch to 'abstract' mode when not running in browser (i.e. server-side rendering).
  const router = createRouter();
  const match = router.match(url);
  return match && match.params ? match.params : null;
}

function parseServerData(data, viewBag) {
  const parsedData = data instanceof Object ? data : JSON.parse(data);
  const parsedViewBag = viewBag instanceof Object ? viewBag : JSON.parse(viewBag);

  return {
    viewBag: parsedViewBag,
    sitecore: parsedData && parsedData.sitecore,
  };
}

function resolveComponentsInRoute(sitecoreData) {
  if (!sitecoreData.route) return [];

  const components = new Map();

  getComponents(sitecoreData.route, components);

  const result = [];
  components.forEach((component) => result.push(component));

  return result;
}

function getComponents(routeData, componentsMap) {
  if (!routeData.placeholders) return;

  Object.keys(routeData.placeholders).forEach((placeholderKey) => {
    routeData.placeholders[placeholderKey].forEach((componentInPlaceholder) => {
      if (
        !componentInPlaceholder.componentName ||
        componentsMap.get(componentInPlaceholder.placeholderName)
      ) {
        // component was raw, or we already knew about it in the known components map
        return;
      }

      // add this component's name and instance to the map
      const componentInstance = componentFactory(componentInPlaceholder.componentName);

      if (componentInstance) {
        componentsMap.set(componentInPlaceholder.componentName, componentInstance);
      }

      // add child placeholders to the map
      if (componentInPlaceholder.placeholders) {
        getComponents(componentInPlaceholder, componentsMap);
      }
    });
  });
}

function initializei18n(state) {
  // don't init i18n for not found routes
  if (!state || !state.sitecore || !state.sitecore.context) return Promise.resolve();

  return i18ninit(state.sitecore.context.language, state.viewBag.dictionary);
}

Vue 2 Server.js

This was the old function from the old version of the server.js file. So I think the issue is here somewhere.

initializei18n(state)
      .then((i18n) => createApp(state, i18n, null))
      .then(({ app, router, graphQLProvider }) => {
        // set server-side router's location
        router.push(path);

        // since there could potentially be asynchronous route hooks or components,
        // we will be returning a Promise so that we can wait until everything is ready before rendering.
        return new Promise((resolve, reject) => {
          router.onReady(() => {
            const matchedComponents = router.getMatchedComponents();
            // no matched routes, reject
            if (!matchedComponents.length) {
              reject(new Error(ErrorMessages.NO_MATCHED_COMPONENTS + `${path}`));
            }
            resolve(matchedComponents);
          }, reject);
        })
          .then((matchedComponents) => {
            // The Vue SSR renderer does not wait for async actions, invoked by components, to complete.
            // vue-apollo handles this by "pre-fetching" GraphQL queries attached to Vue components (via the `.apollo` property).
            // So when those components are rendering, the GraphQL queries use the apollo-client cache for their data.
            // The biggest caveat to this approach is that when pre-fetching, the queries don't have access
            // to their Vue component instance. In other words, SSR pre-fetch queries can't set query variables
            // based on component props, state, etc...
            // Prefetch queries only have access to the context data that are provided via the `prefetchAll`
            // method below. In this scenario we provide current route and state data provided by the server.

            // only these components will be prefetched, so we find JSS components in the route data,
            // as well as static components in the route from the router
            const apolloComponentsToSSR = [
              ...matchedComponents,
              ...resolveComponentsInRoute(state.sitecore),
              // got static components etc that need to prefetch GraphQL queries? Add them here.
            ].filter((component) => component.apollo);

            return ApolloSSR.prefetchAll(graphQLProvider, apolloComponentsToSSR, {
              route: router.currentRoute,
              state,
            });
          })
          .then(() => {
            const renderer = createRenderer();
            // `renderToString` expects an actual `Vue` instance (e.g. `new Vue()`), not just a component definition
            // `renderToString` returns a promise
            return renderer.renderToString(app).then((renderedApp) => {
              // We add the GraphQL state to the SSR state so that we can avoid refetching queries after client load
              // Not using GraphQL? Get rid of this.
              state.APOLLO_STATE = ApolloSSR.getStates(graphQLProvider);
              return {
                renderedApp,
                app,
              };
            });
          });
      })

Adding version since this might help:

Web App JSS Versions:

    "@sitecore-jss/sitecore-jss-forms": "^21.2.4",
    "@sitecore-jss/sitecore-jss-vue": "~21.2.4",
    "@sitecore-jss/sitecore-jss-cli": "~21.2.4",
    "@sitecore-jss/sitecore-jss-dev-tools": "~21.2.4",
    "@sitecore-jss/sitecore-jss-manifest": "^19.0.2",
    "@sitecore-jss/sitecore-jss-tracking": "^19.0.2",

Sitecore Version: 9.3

1
  • 1
    Please include your error message as text, not an image. Search Engines need something to pick up. Your question title should also be relevant to the error; not just "an issue with something"
    – Mark Cassidy
    Oct 24, 2023 at 10:03

1 Answer 1

0

The error message you're encountering, "SyntaxError: Unexpected token *", is pointing towards an issue in your JavaScript code where a generator function is being defined using the syntax function* (with an asterisk). However, the JavaScript engine executing the code is unable to understand this syntax, most likely due to the version of Node.js running on your server being outdated and not supporting this ES6 feature.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.