Skip to content

Commit

Permalink
fix: use single instance of react dependencies on studio mode
Browse files Browse the repository at this point in the history
  • Loading branch information
acaldas committed Oct 16, 2024
1 parent ffbc9dc commit 42b0ed7
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 67 deletions.
7 changes: 6 additions & 1 deletion studio/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,18 @@ export async function startServer() {
open: true,
},
plugins: [
viteConnectDevStudioPlugin(),
viteConnectDevStudioPlugin(true),
viteEnvs({
declarationFile: join(studioDirname, '../.env'),
computedEnv: studioConfig,
}),
runShellScriptPlugin(viteEnvsScript),
],
build: {
rollupOptions: {
input: 'index.html',
},
},
};

const server = await createServer(config);
Expand Down
178 changes: 122 additions & 56 deletions studio/vite-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,16 @@ import {
Alias,
AliasOptions,
Plugin,
PluginOption,
ViteDevServer,
normalizePath,
} from 'vite';

// matches react, react-dom, and all it's sub-imports like react-dom/client
export const externalIds = /^react(-dom)?(\/.*)?$/;
// used to find react imports in the code for text replacement
export const externalImports = /react(-dom)?(\/.*)?/;

export const LOCAL_DOCUMENT_MODELS_IMPORT = 'LOCAL_DOCUMENT_MODELS';
export const LOCAL_DOCUMENT_EDITORS_IMPORT = 'LOCAL_DOCUMENT_EDITORS';

Expand Down Expand Up @@ -98,9 +104,66 @@ export function watchLocalFiles(
}
}

// https://github.com/vitejs/vite/issues/6393#issuecomment-1006819717
// vite dev server doesn't support setting dependencies as external
// as when building the app.
function viteIgnoreStaticImport(importKeys: (string | RegExp)[]): Plugin {
return {
name: 'vite-plugin-ignore-static-import',
enforce: 'pre',
// vite will still append /@id/ to an external import
// so this will rewrite the 'vite:import-analysis' prefix
configResolved(resolvedConfig) {
const VALID_ID_PREFIX = `/@id/`;
const values = importKeys.map(key =>
typeof key === 'string' ? key : key.source,
);
const reg = new RegExp(
`("|')${VALID_ID_PREFIX}${values.length === 1 ? values[0] : `(${values.join('|')})("|')`}`,
'g',
);

(resolvedConfig.plugins as Plugin[]).push({
name: 'vite-plugin-ignore-static-import-replace-idprefix',
transform: code => {
const matches = code.matchAll(reg);
for (const match of matches) {
code = code.replaceAll(
match[0],
match[0].replace('/@id/', ''),
);
}
return code;
},
});
},
// prevents the external import from being transformed to 'node_modules/...'
resolveId: id => {
if (
importKeys.some(key =>
typeof key === 'string' ? key === id : key.test(id),
)
) {
return { id, external: true };
}
},
// returns empty string to prevent "Pre-transform error: Failed to load url"
load(id) {
if (
importKeys.some(key =>
typeof key === 'string' ? key === id : key.test(id),
)
) {
return '';
}
},
};
}

export function viteConnectDevStudioPlugin(
enabled = false,
env?: Record<string, string>,
): Plugin {
): PluginOption[] {
const studioConfig = getStudioConfig(env);
const importKeys = [
LOCAL_DOCUMENT_MODELS_IMPORT,
Expand All @@ -110,64 +173,67 @@ export function viteConnectDevStudioPlugin(
const localDocumentEditorsPath =
studioConfig[LOCAL_DOCUMENT_EDITORS_IMPORT];

return {
name: 'vite-plugin-connect-dev-studio',
enforce: 'pre',
config(config) {
if (!localDocumentModelsPath && !localDocumentEditorsPath) {
return;
}

// adds the provided paths to be resolved by vite
const resolve = config.resolve ?? {};
const alias = resolve.alias;
let resolvedAlias: AliasOptions | undefined;
if (Array.isArray(alias)) {
const arrayAlias = [...(alias as Alias[])];

if (localDocumentModelsPath) {
arrayAlias.push({
find: LOCAL_DOCUMENT_MODELS_IMPORT,
replacement: localDocumentModelsPath,
});
return [
enabled && viteIgnoreStaticImport([externalImports]),
{
name: 'vite-plugin-connect-dev-studio',
enforce: 'pre',
config(config) {
if (!localDocumentModelsPath && !localDocumentEditorsPath) {
return;
}

if (localDocumentEditorsPath) {
arrayAlias.push({
find: LOCAL_DOCUMENT_EDITORS_IMPORT,
replacement: localDocumentEditorsPath,
});
// adds the provided paths to be resolved by vite
const resolve = config.resolve ?? {};
const alias = resolve.alias;
let resolvedAlias: AliasOptions | undefined;
if (Array.isArray(alias)) {
const arrayAlias = [...(alias as Alias[])];

if (localDocumentModelsPath) {
arrayAlias.push({
find: LOCAL_DOCUMENT_MODELS_IMPORT,
replacement: localDocumentModelsPath,
});
}

if (localDocumentEditorsPath) {
arrayAlias.push({
find: LOCAL_DOCUMENT_EDITORS_IMPORT,
replacement: localDocumentEditorsPath,
});
}
resolvedAlias = arrayAlias;
} else if (typeof alias === 'object') {
resolvedAlias = { ...alias, ...studioConfig };
} else if (typeof alias === 'undefined') {
resolvedAlias = { ...studioConfig };
} else {
console.error('resolve.alias was not recognized');
}
resolvedAlias = arrayAlias;
} else if (typeof alias === 'object') {
resolvedAlias = { ...alias, ...studioConfig };
} else if (typeof alias === 'undefined') {
resolvedAlias = { ...studioConfig };
} else {
console.error('resolve.alias was not recognized');
}

if (resolvedAlias) {
resolve.alias = resolvedAlias;
config.resolve = resolve;
}
},
configureServer(server) {
watchLocalFiles(
server,
localDocumentModelsPath,
localDocumentEditorsPath,
);
},
resolveId: id => {
// if the path was not provided then declares the local
// imports as external so that vite ignores them
if (importKeys.includes(id)) {
return {
id,
external: true,
};
}
if (resolvedAlias) {
resolve.alias = resolvedAlias;
config.resolve = resolve;
}
},
configureServer(server) {
watchLocalFiles(
server,
localDocumentModelsPath,
localDocumentEditorsPath,
);
},
resolveId: id => {
// if the path was not provided then declares the local
// imports as external so that vite ignores them
if (importKeys.includes(id)) {
return {
id,
external: true,
};
}
},
},
};
];
}
13 changes: 3 additions & 10 deletions vite.renderer.config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { createHtmlPlugin } from 'vite-plugin-html';
import svgr from 'vite-plugin-svgr';
import clientConfig from './client.config';
import pkg from './package.json';
import { viteConnectDevStudioPlugin } from './studio/vite-plugin';
import { externalIds, viteConnectDevStudioPlugin } from './studio/vite-plugin';

export default defineConfig(({ mode }) => {
const isProd = mode === 'production';
Expand Down Expand Up @@ -38,7 +38,7 @@ export default defineConfig(({ mode }) => {
const uploadSentrySourcemaps = authToken && org && project;

const plugins: PluginOption[] = [
viteConnectDevStudioPlugin(env),
viteConnectDevStudioPlugin(false, env),
react({
include: 'src/**/*.tsx',
babel: {
Expand Down Expand Up @@ -106,14 +106,7 @@ export default defineConfig(({ mode }) => {
? `${chunk.name}.js`
: 'assets/[name].[hash].js',
},
external: [
'node:crypto',
'react',
'react/jsx-runtime',
'react/jsx-dev-runtime',
'react-dom',
'react-dom/client',
],
external: ['node:crypto', externalIds],
},
},
resolve: {
Expand Down

0 comments on commit 42b0ed7

Please sign in to comment.