Skip to content

Commit

Permalink
fix: prevent vitetest/ssr errors due to defining components on the se…
Browse files Browse the repository at this point in the history
…rver (#7521)

**Related Issue:** #7486

## Summary

Enabling Stencil's
[`includeImportCustomElements`](https://stenciljs.com/docs/react#includeimportcustomelements)
option prevented SSR and vitetest users from being able to explicitly
define the custom elements on the client. This patches the Stencil build
to ensure the custom elements aren't defined while on the server.
  • Loading branch information
benelan committed Aug 15, 2023
1 parent abcc223 commit 046672e
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 0 deletions.
2 changes: 2 additions & 0 deletions packages/calcite-components-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@
"license": "SEE LICENSE.md",
"scripts": {
"build": "rimraf dist && npm run compile",
"prebuild": "npm run patch:ssr",
"clean": "rimraf dist node_modules .turbo",
"compile": "npm run tsc",
"patch:ssr": "ts-node support/patchSSR.ts",
"tsc": "tsc"
},
"main": "./dist/index.js",
Expand Down
12 changes: 12 additions & 0 deletions packages/calcite-components-react/src/auto-define.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const isBrowser = (): boolean =>
![typeof window, typeof document, typeof location].includes("undefined") &&
[typeof process, typeof global].includes("undefined") &&
window.location === location &&
window.document === document;

export function autoDefine(component: string): () => Promise<void> | undefined {
if (isBrowser()) {
return async () => (await import(`@esri/calcite-components/dist/components/${component}.js`)).defineCustomElement();
}
return undefined;
}
51 changes: 51 additions & 0 deletions packages/calcite-components-react/support/patchSSR.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// patch needed due to Stencil executing client side code on the server
// when using the includeImportCustomElements option
// https://stenciljs.com/docs/react#includeimportcustomelements
// https://github.com/Esri/calcite-design-system/issues/7486

const {
promises: { readFile, writeFile },
} = require("fs");
const { resolve } = require("path");

// Matches imports of defineCustomElement from calcite-component's custom-elements output target.
// Importing defineCustomElement on the server throws errors due to ESM/CJS conflicts and
// 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';";

// 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;

// The patched version of the createReactComponent export using the capture groups to fill in the blanks
const patchedReactWrapperExports = "createReactComponent<$1>($2$3autoDefine($2))";

// The autoDefine import is placed below this line
const reactLibImport = "import { createReactComponent } from './react-component-lib';";

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

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

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

await writeFile(filePath, patchedContents);
} catch (err) {
console.error(err);
process.exit(1);
}
})();

0 comments on commit 046672e

Please sign in to comment.