Skip to content

Commit 3e1a9ca

Browse files
amir-zahediclaude
andauthored
feat(gdu): add Overdrive barrel-splitting Vite plugin (#408)
* feat(gdu): add Overdrive barrel-splitting Vite plugin Rewrites `import { X } from '@autoguru/overdrive'` into deep component imports (e.g. `@autoguru/overdrive/components/Box`) during Vite builds, enabling granular tree-shaking and eliminating Vanilla Extract CSS side-effect leakage from unused components. Includes a 136-entry export manifest covering all components, hooks, styles, themes, and utils — with a generator script for regeneration when Overdrive updates. Validated: fmo-booking builds identically (227 JS chunks, 82 CSS files, zero barrel imports in output, build-manifest.json unchanged). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(gdu): fix dev mode env token replacement for Vite 8/OXC Vite 8 with OXC does not replace custom process.env.X dotted member expressions via the define config. In production the mfeEnvTokens plugin handles this, but dev mode had no equivalent — causing process.env.mfeBasePath etc. to evaluate to undefined and breaking Router basename resolution. - Add devEnvReplace plugin to runSPA-vite.ts that inlines env values via pre-transform string replacement in dev mode - Spread envDefines (not remainingDefines) into base define so the dev plugin can extract process.env.X entries - Add apply: 'build' to overdriveBarrelSplit to skip it in dev mode Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(gdu): address lint errors and review feedback - Extract OverdriveCategory type alias (sonarjs/use-type-alias) - Reduce processTopLevelBarrel cognitive complexity by extracting category detection helper (sonarjs/cognitive-complexity) - Use String.raw for regex escape string (unicorn/prefer-string-raw) - Flip negated ternary condition (unicorn/no-negated-condition) - Add empty line between import groups (import/order) - Use JSON.stringify for manifest output values to handle special chars - Use full path in regeneration command doc comment - Use proper JS identifier regex pattern for $ in export names - Remove non-portable shebang from manifest generator Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 75b3143 commit 3e1a9ca

File tree

5 files changed

+869
-10
lines changed

5 files changed

+869
-10
lines changed

packages/gdu/commands/start/runSPA-vite.ts

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,46 @@ function relayCjsToEsmPlugin(): VitePlugin {
8181
};
8282
}
8383

84+
/**
85+
* Vite 8 with OXC does not replace custom `process.env.X` dotted member
86+
* expressions via the `define` config — only simple identifiers and the
87+
* special-cased `NODE_ENV` work. In production the `mfeEnvTokens` plugin
88+
* (apply: 'build') handles this; for dev mode we need an equivalent
89+
* pre-transform that inlines the literal values.
90+
*/
91+
function devEnvReplace(define: Record<string, any>): VitePlugin {
92+
const envMap: Record<string, string> = {};
93+
for (const [key, value] of Object.entries(define)) {
94+
const match = /^process\.env\.(.+)$/.exec(key);
95+
if (match) {
96+
envMap[match[1]] = String(value);
97+
}
98+
}
99+
100+
const keys = Object.keys(envMap);
101+
if (keys.length === 0) {
102+
return { name: 'gdu-dev-env-replace' };
103+
}
104+
105+
const escapedKeys = keys.map((k) =>
106+
k.replace(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`),
107+
);
108+
const pattern = new RegExp(
109+
`\\bprocess\\.env\\.(${escapedKeys.join('|')})\\b`,
110+
'g',
111+
);
112+
113+
return {
114+
name: 'gdu-dev-env-replace',
115+
enforce: 'pre',
116+
transform(code) {
117+
if (!code.includes('process.env.')) return null;
118+
const result = code.replace(pattern, (_, key) => envMap[key]);
119+
return result === code ? null : { code: result, map: null };
120+
},
121+
};
122+
}
123+
84124
function spaHtmlPlugin(guruConfig: GuruConfig): VitePlugin {
85125
const entryPath = join(gduEntryPath, 'spa', 'client.js');
86126
const templatePath = getConsumerHtmlTemplate(guruConfig);
@@ -215,17 +255,21 @@ export const runSPAVite = async (guruConfig: GuruConfig, isDebug: boolean) => {
215255
}
216256
}
217257

258+
// Build the full define map so devEnvReplace sees the dev overrides
259+
// (e.g. NODE_ENV = 'development') rather than the production defaults.
260+
const devDefine = {
261+
...base.define,
262+
'process.env.NODE_ENV': JSON.stringify('development'),
263+
__DEV__: JSON.stringify(true),
264+
__DEBUG__: JSON.stringify(!!isDebug),
265+
};
266+
218267
const server = await createServer({
219268
root: PROJECT_ROOT,
220269

221270
resolve: base.resolve,
222271

223-
define: {
224-
...base.define,
225-
'process.env.NODE_ENV': JSON.stringify('development'),
226-
__DEV__: JSON.stringify(true),
227-
__DEBUG__: JSON.stringify(!!isDebug),
228-
},
272+
define: devDefine,
229273

230274
css: {
231275
devSourcemap: true,
@@ -293,6 +337,7 @@ export const runSPAVite = async (guruConfig: GuruConfig, isDebug: boolean) => {
293337
appType: 'custom',
294338

295339
plugins: [
340+
devEnvReplace(devDefine),
296341
...(vanillaExtractPlugin ? [vanillaExtractPlugin()] : []),
297342
...(relayTransformPlugin ? [relayTransformPlugin] : []),
298343
relayCjsToEsmPlugin(),

0 commit comments

Comments
 (0)