Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve preview entries #8691

Merged
merged 9 commits into from
May 15, 2024
87 changes: 64 additions & 23 deletions scopes/preview/preview/generate-link.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import { toWindowsCompatiblePath } from '@teambit/toolbox.path.to-windows-compatible-path';
import camelcase from 'camelcase';
import type { ComponentMap } from '@teambit/component';
import { join } from 'path';
import { outputFileSync } from 'fs-extra';
import objectHash from 'object-hash';
import camelcase from 'camelcase';
import { toWindowsCompatiblePath } from '@teambit/toolbox.path.to-windows-compatible-path';
import { getPreviewDistDir } from './mk-temp-dir';

const previewDistDir = getPreviewDistDir();

export type MainModulesMap = {
/**
Expand All @@ -10,56 +16,71 @@ export type MainModulesMap = {
[envId: string]: string;
};

type ModuleLink = {
envId: string;
varName: string;
resolveFrom: string;
};

type ComponentLink = {
componentIdentifier: string;
modules: {
varName: string;
resolveFrom: string;
}[];
};

// :TODO refactor to building an AST and generate source code based on it.
export function generateLink(
prefix: string,
componentMap: ComponentMap<string[]>,
mainModulesMap?: MainModulesMap,
isSplitComponentBundle = false
isSplitComponentBundle = false,
tempPackageDir?: string
): string {
const links = componentMap.toArray().map(([component, modulePath], compIdx) => ({
const componentLinks: ComponentLink[] = componentMap.toArray().map(([component, modulePath], compIdx) => ({
componentIdentifier: component.id.fullName,
modules: modulePath.map((path, pathIdx) => ({
varName: moduleVarName(compIdx, pathIdx),
resolveFrom: toWindowsCompatiblePath(path),
})),
}));

let modulesLinks;
if (mainModulesMap) {
modulesLinks = Object.entries(mainModulesMap).map(([envId, path]) => {
const resolveFrom = toWindowsCompatiblePath(path);
const varName = getEnvVarName(envId);
return { envId, varName, resolveFrom };
});
}
const moduleLinks: ModuleLink[] = Object.entries(mainModulesMap || {}).map(([envId, path]) => {
const resolveFrom = toWindowsCompatiblePath(path);
const varName = getEnvVarName(envId);
return { envId, varName, resolveFrom };
});

return `
import { linkModules } from '@teambit/preview/dist/preview.preview.runtime.js';
const contents = `
import { linkModules } from '${previewDistDir}/preview.preview.runtime.js';

${links
.map((link) => link.modules.map((module) => `import * as ${module.varName} from "${module.resolveFrom}";`).join('\n'))
.filter((line) => line !== '') // prevent empty lines
.join('\n')}
${getModuleImports(moduleLinks, tempPackageDir)}

${modulesLinks.map((module) => `import * as ${module.varName} from "${module.resolveFrom}";`).join('\n')}
${getComponentImports(componentLinks)}

linkModules('${prefix}', {
modulesMap: {
${modulesLinks
${moduleLinks
// must include all components, including empty
.map((module) => `"${module.envId}": ${module.varName}`)
.map((moduleLink) => `"${moduleLink.envId}": ${moduleLink.varName}`)
.join(',\n ')}
},
isSplitComponentBundle: ${isSplitComponentBundle},
componentMap: {
${links
${componentLinks
// must include all components, including empty
.map((link) => ` "${link.componentIdentifier}": [${link.modules.map((module) => module.varName).join(', ')}]`)
.map(
(componentLink) =>
` "${componentLink.componentIdentifier}": [${componentLink.modules
.map((module) => module.varName)
.join(', ')}]`
)
.join(',\n')}
}
});
`;
return contents;
}

function moduleVarName(componentIdx: number, fileIdx: number) {
Expand All @@ -71,3 +92,23 @@ function getEnvVarName(envId: string) {
const varName = `${envNameFormatted}MainModule`;
return varName;
}

function getModuleImports(moduleLinks: ModuleLink[] = [], tempPackageDir?: string): string {
const hash = objectHash(moduleLinks);
const tempFileName = `preview-modules-${hash}.mjs`;
const tempFilePath = join(tempPackageDir || previewDistDir, tempFileName);
const tempFileContents = moduleLinks
.map((module) => `export * as ${module.varName} from "${module.resolveFrom}";`)
.join('\n');
outputFileSync(tempFilePath, tempFileContents);
return `import {${moduleLinks.map((moduleLink) => moduleLink.varName).join(', ')}} from "${tempFilePath}";`;
}

function getComponentImports(componentLinks: ComponentLink[] = []): string {
return componentLinks
.map((link) =>
link.modules.map((module) => `import * as ${module.varName} from "${module.resolveFrom}";`).join('\n')
)
.filter((line) => line !== '') // prevent empty lines
.join('\n');
}
11 changes: 10 additions & 1 deletion scopes/preview/preview/mk-temp-dir.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
import { mkdtempSync } from 'fs-extra';
import { tmpdir } from 'os';
import { sep } from 'path';
import { sep, join } from 'path';
import { getAspectDirFromBvm } from '@teambit/aspect-loader';

export function makeTempDir(prefix = '') {
return mkdtempSync(`${tmpdir()}${sep}${prefix}`);
}

export function getPreviewDistDir(): string {
try {
return join(getAspectDirFromBvm('@teambit/preview'), 'dist');
} catch (err) {
return __dirname;
}
}
5 changes: 4 additions & 1 deletion scopes/preview/preview/pre-bundle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ import { promisify } from 'util';
import { PreviewAspect } from './preview.aspect';
import { createWebpackConfig } from './webpack/webpack.config';
import { clearConsole } from './pre-bundle-utils';
import { getPreviewDistDir } from './mk-temp-dir';

const previewDistDir = getPreviewDistDir();

export const RUNTIME_NAME = 'preview';
export const PUBLIC_DIR = join('public', 'bit-preview');
Expand Down Expand Up @@ -131,7 +134,7 @@ export async function generateBundlePreviewEntry(rootAspectId: string, previewPr
config['teambit.harmony/bit'] = rootAspectId;

const contents = [imports, `run(${JSON.stringify(config, null, 2)});`].join('\n');
const previewRuntime = resolve(join(__dirname, `preview.entry.${sha1(contents)}.js`));
const previewRuntime = resolve(join(previewDistDir, `preview.entry.${sha1(contents)}.js`));

if (!existsSync(previewRuntime)) {
outputFileSync(previewRuntime, contents);
Expand Down
16 changes: 14 additions & 2 deletions scopes/preview/preview/preview.main.runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { CACHE_ROOT } from '@teambit/legacy/dist/constants';
import { BitError } from '@teambit/bit-error';
import objectHash from 'object-hash';
import { uniq } from 'lodash';
import { writeFileSync, existsSync, mkdirSync } from 'fs-extra';
import { writeFileSync, existsSync, mkdirSync, ensureDirSync, writeJSONSync } from 'fs-extra';
import { join } from 'path';
import { PkgAspect, PkgMain } from '@teambit/pkg';
import { AspectLoaderAspect, getAspectDir, getAspectDirFromBvm } from '@teambit/aspect-loader';
Expand Down Expand Up @@ -639,6 +639,17 @@ export class PreviewMain {
private writeHash = new Map<string, string>();
private timestamp = Date.now();

private ensureTempPackage() {
const workspacePath = this.workspace?.path;
const tempPackageDir = workspacePath ? join(workspacePath, 'node_modules', '@teambit', '_local') : '';
if (tempPackageDir) {
ensureDirSync(tempPackageDir);
writeJSONSync(join(tempPackageDir, 'package.json'), { name: '@teambit/_local' });
writeFileSync(join(tempPackageDir, 'index.js'), 'module.exports = {};');
return tempPackageDir;
}
}

/**
* write a link to load custom modules dynamically.
* @param prefix write
Expand All @@ -653,7 +664,8 @@ export class PreviewMain {
dirName: string,
isSplitComponentBundle: boolean
) {
const contents = generateLink(prefix, moduleMap, mainModulesMap, isSplitComponentBundle);
const tempPackageDir = this.ensureTempPackage();
const contents = generateLink(prefix, moduleMap, mainModulesMap, isSplitComponentBundle, tempPackageDir);
return this.writeLinkContents(contents, dirName, prefix);
}

Expand Down