- based on create-react-app & rescripts with Typescript
- used environment variables to include only files needed
- uses new React Features (Suspense and Lazy)
it has the both required environment variables: NODE_PATH and REACT_APP_TL_TENANT.
NODE_PATH: let us avoid the relative path hell (../../../). video referenceREACT_APP_TL_TENANT: adds the specific tenant extension file to be imported (see the.rescriptsrc.jsfile for more explanation)
Adds the extra extension files to the webpack config on resolve.extensions. This is important to maintain the imports inside the code as clean as possible and avoid referring specific tenants or use the process.env variable. All the magic happens at build time
In conjunction with the NODE_PATH, it let us work with paths from src directory (avoid relative path hell as mentioned before)
checkout how setting cml as REACT_APP_TL_TENANT, we import:
- src/App/Config/theme/theme.foo.ts
- src/Login/Page.foo.tsx
sample code on how we import the Page component:
import React, { Suspense, lazy } from "react";
const Login = lazy(() => import(`./Page`));
/*
you see that in the dynamic import above,
we are not adding the extra `cml` to the import,
but Webpack is doing that for us...
*/
export const LoginPage = () => (
<Suspense fallback={<p>loading...</p>}>
<Login />
</Suspense>
);The tenant configuration is handled at Compile time. this means that all the code bundled depends on the environment variable we set at build time (REACT_APP_TENANT_CODE). the way we tell webpack how and what to compile is by changing the extension of the component, adding the tenant code before the real file extension (for example: App.foo.tsx). The setup is actually pretty simple. Because webpack resolve all its modules by extensions, it has a property in the webpack config that accepts an array of extensions. Thankfully for us, the order of the list matters (ir resolve the extension it finds first). What we do is telling webpack to resolve first whatever file extension + the tenant code FIRST, and then the regular file extension. Here's the setup we override with rescripts:
# .rescriptsrc.js
module.exports = {
webpack: config => {
const prevExtensions = config.resolve.extensions
const newExtensions = ["mjs", "js", "ts", "tsx", "json", "jsx"]
.map(ext => `.${process.env.REACT_APP_TENANT_CODE}.${ext}`)
.filter(ext => !ext.includes("undefined"))
.concat(prevExtensions)
config.resolve.extensions = newExtensions
return config
}
}what we are doing is adding the REACT_APP_TENANT_CODE in front of the default extensions, and then concatenate the old extensions at the end of this new array. This way if there's 2 components (App.tsx and App.foo.tsx), and aour environment variable is equal to foo, webpack will compile App.foo.tsx because the .foo.tsx has more priority (its first in the resolve.extensions array).