Skip to content

Commit

Permalink
fix: resolve vite errors due to using expressions in dynamic imports (#…
Browse files Browse the repository at this point in the history
…7671)

**Related Issue:** #7575

## Summary

Vite uses
[`@rollup/plugin-dynamic-import-vars`](https://github.com/rollup/plugins/tree/master/packages/dynamic-import-vars)
for resolving dynamic imports that use an expression for the path,
rather than a fixed string. The rollup plugin has a
[limitation](https://github.com/rollup/plugins/tree/master/packages/dynamic-import-vars#imports-must-start-with--or-)
that requires dynamic imports with expressions to use relative paths.

> All imports must start relative to the importing file. The import
should not start with a variable, an absolute path or a bare import

Changing the module path to a fixed string in the patch script resolves
the errors. It also ensures that only the components being used in the
app are bundled.
  • Loading branch information
benelan authored Sep 6, 2023
1 parent 1f4cd97 commit b4c1038
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 32 deletions.
19 changes: 0 additions & 19 deletions packages/calcite-components-react/src/auto-define.ts

This file was deleted.

40 changes: 27 additions & 13 deletions packages/calcite-components-react/support/patchSSR.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,33 +13,47 @@ const { resolve } = require("path");
// attempting to use browser APIs, which don't exist on the server.
const defineCustomElementImports = /import { defineCustomElement as defineCalcite.*(\r\n|\r|\n)/gm;

// The removed imports are replaced with autoDefine, which is a wrapper around defineCustomElement
// to make sure it's only called on the client.
const autoDefineImport = "import { autoDefine } from './auto-define';";
// The removed imports are replaced with utils that check the environment
// to make sure the components are only defined on the browser.
const browserCheckUtils = `
// CodeSandbox exposes 'process', which makes it look like NodeJS. The only way to determine it should be
// be treated as the browser is the non-standard value they use for 'process.platform'.
// https://nodejs.org/api/process.html#processplatform
type CodeSandboxWorkaround = NodeJS.Platform | "browser";
// Matches createReactComponent exports to add autoDefine instead of defineCustomElement.
// The regex creates capture groups for the component name and other parts of the line
// that shouldn't be replaced/removed.
const reactWrapperExports = /createReactComponent<(.*)>.*\((['|\w|-]*)(.*)(defineCalcite\w*)\)/g;
// https://github.com/flexdinesh/browser-or-node/blob/master/src/index.js
const isBrowser = typeof window !== "undefined" && typeof window.document !== "undefined";
const isNode =
typeof process !== "undefined" &&
process.versions != null &&
process.versions.node != null &&
(process?.platform as CodeSandboxWorkaround) !== "browser";`;

// The patched version of the createReactComponent export using the capture groups to fill in the blanks
const patchedReactWrapperExports = "createReactComponent<$1>($2$3autoDefine($2))";
// Matches createReactComponent exports to replace the defineCustomElement
// arguments with inlined, anonymous functions that dynamically import the
// components when in the browser. The regex creates capture groups for the
// component name and other arguments that shouldn't be replaced/removed.
const reactWrapperExports = /createReactComponent<(.*)>.*\('([\w|-]*)'(.*)(defineCalcite\w*)\)/g;

// The autoDefine import is placed below this line
const reactLibImport = "import { createReactComponent } from './react-component-lib';";
// The patched version of the createReactComponent export using capture groups to fill in the blanks.
const patchedReactWrapperExports =
"createReactComponent<$1>('$2'$3isBrowser && !isNode ? async () => (await import('@esri/calcite-components/dist/components/$2.js')).defineCustomElement() : undefined)";

// The isBrowser and isNode utils are placed below this line
const jsxTypeImport = "import type { JSX } from '@esri/calcite-components/dist/components';";

(async () => {
try {
const filePath = resolve(`${__dirname}/../src/components.ts`);
const contents = await readFile(filePath, { encoding: "utf8" });

if (contents.includes(autoDefineImport)) {
if (contents.includes("isBrowser")) {
console.log("SSR patch: skipping, components.ts is already patched");
return;
}

const patchedContents = contents
.replace(reactLibImport, `$&\n${autoDefineImport}`)
.replace(jsxTypeImport, `$&\n${browserCheckUtils}`)
.replace(defineCustomElementImports, "")
.replace(reactWrapperExports, patchedReactWrapperExports);

Expand Down

0 comments on commit b4c1038

Please sign in to comment.