From bb13f5759eaf1cd500713b8e024c59ced98ad62c Mon Sep 17 00:00:00 2001 From: Konstantinos Leimonis Date: Fri, 13 Oct 2023 16:23:41 +0100 Subject: [PATCH 1/3] add(prebundle): workspace & scope aspect --- scopes/scope/scope/scope.ui-root.ts | 2 +- scopes/ui-foundation/ui/bundle-ui.task.ts | 33 ++++++-- scopes/ui-foundation/ui/start.cmd.tsx | 15 +++- scopes/ui-foundation/ui/ui-server.ts | 1 + scopes/ui-foundation/ui/ui.main.runtime.ts | 94 ++++++++++++---------- 5 files changed, 89 insertions(+), 56 deletions(-) diff --git a/scopes/scope/scope/scope.ui-root.ts b/scopes/scope/scope/scope.ui-root.ts index fdba37e9da85..7e2d977c25be 100644 --- a/scopes/scope/scope/scope.ui-root.ts +++ b/scopes/scope/scope/scope.ui-root.ts @@ -30,7 +30,7 @@ export class ScopeUIRoot implements UIRoot { buildOptions = { ssr: true, - prebundle: false, + prebundle: true, }; resolveAspects(runtime: string, componentIds?: ComponentID[], opts?: ResolveAspectsOptions) { diff --git a/scopes/ui-foundation/ui/bundle-ui.task.ts b/scopes/ui-foundation/ui/bundle-ui.task.ts index fa41447d3fd0..a039fc949597 100644 --- a/scopes/ui-foundation/ui/bundle-ui.task.ts +++ b/scopes/ui-foundation/ui/bundle-ui.task.ts @@ -7,6 +7,14 @@ import { UIAspect, UiMain } from '@teambit/ui'; export const BUNDLE_UI_TASK_NAME = 'BundleUI'; export const BUNDLE_UI_DIR = 'ui-bundle'; +export const UIROOT_ASPECT_IDS = { + SCOPE: 'teambit.scope/scope', + WORKSPACE: 'teambit.workspace/workspace', +}; +export const BUNDLE_UIROOT_DIR = { + [UIROOT_ASPECT_IDS.SCOPE]: 'scope', + [UIROOT_ASPECT_IDS.WORKSPACE]: 'workspace', +}; export const BUNDLE_UI_HASH_FILENAME = '.hash'; export class BundleUiTask implements BuildTask { @@ -24,11 +32,15 @@ export class BundleUiTask implements BuildTask { return { componentsResults: [] }; } - const outputPath = join(capsule.path, BundleUiTask.getArtifactDirectory()); - this.logger.info(`Generating UI bundle at ${outputPath}...`); try { - await this.ui.build(undefined, outputPath); - await this.generateHash(outputPath); + await Promise.all( + Object.values(UIROOT_ASPECT_IDS).map(async (uiRootAspectId) => { + const outputPath = join(capsule.path, BundleUiTask.getArtifactDirectory(uiRootAspectId)); + this.logger.info(`Generating UI bundle at ${outputPath}...`); + await this.ui.build(uiRootAspectId, outputPath); + await this.generateHash(outputPath); + }) + ); } catch (error) { this.logger.error('Generating UI bundle failed'); throw new Error('Generating UI bundle failed'); @@ -51,16 +63,21 @@ export class BundleUiTask implements BuildTask { writeFileSync(join(outputPath, BUNDLE_UI_HASH_FILENAME), hash); } - static getArtifactDirectory() { - return join('artifacts', BUNDLE_UI_DIR); + static getArtifactDirectory(uiRootAspectId) { + return join('artifacts', BUNDLE_UI_DIR, BUNDLE_UIROOT_DIR[uiRootAspectId]); } static getArtifactDef() { return [ { - name: BUNDLE_UI_DIR, + name: `${BUNDLE_UI_DIR}-${BUNDLE_UIROOT_DIR[UIROOT_ASPECT_IDS.SCOPE]}`, globPatterns: ['**'], - rootDir: BundleUiTask.getArtifactDirectory(), + rootDir: BundleUiTask.getArtifactDirectory(UIROOT_ASPECT_IDS.SCOPE), + }, + { + name: `${BUNDLE_UI_DIR}-${BUNDLE_UIROOT_DIR[UIROOT_ASPECT_IDS.WORKSPACE]}`, + globPatterns: ['**'], + rootDir: BundleUiTask.getArtifactDirectory(UIROOT_ASPECT_IDS.WORKSPACE), }, ]; } diff --git a/scopes/ui-foundation/ui/start.cmd.tsx b/scopes/ui-foundation/ui/start.cmd.tsx index 333308337601..d770656d6783 100644 --- a/scopes/ui-foundation/ui/start.cmd.tsx +++ b/scopes/ui-foundation/ui/start.cmd.tsx @@ -76,7 +76,16 @@ export class StartCmd implements Command { async render( [userPattern]: StartArgs, - { dev, port, rebuild, verbose, noBrowser, skipCompilation, skipUiBuild, uiRootName }: StartFlags + { + dev, + port, + rebuild, + verbose, + noBrowser, + skipCompilation, + skipUiBuild, + uiRootName: uiRootAspectIdOrName, + }: StartFlags ): Promise { this.logger.off(); if (!this.ui.isHostAvailable()) { @@ -84,10 +93,10 @@ export class StartCmd implements Command { `bit start can only be run inside a bit workspace or a bit scope - please ensure you are running the command in the correct directory` ); } - const appName = this.ui.getUiName(uiRootName); + const appName = this.ui.getUiName(uiRootAspectIdOrName); await this.ui.invokePreStart({ skipCompilation }); const uiServer = this.ui.createRuntime({ - uiRootName, + uiRootAspectIdOrName, skipUiBuild, pattern: userPattern, dev, diff --git a/scopes/ui-foundation/ui/ui-server.ts b/scopes/ui-foundation/ui/ui-server.ts index 0e264fbbf1ba..30464761e6d5 100644 --- a/scopes/ui-foundation/ui/ui-server.ts +++ b/scopes/ui-foundation/ui/ui-server.ts @@ -101,6 +101,7 @@ export class UIServer { const publicDir = `/${this.publicDir}`; const defaultRoot = join(this.uiRoot.path, publicDir); const root = bundleUiRoot || defaultRoot; + this.logger.debug(`UiServer, start from ${root}`); const server = await this.graphql.createServer({ app }); // set up proxy, for things like preview, e.g. '/preview/teambit.react/react' diff --git a/scopes/ui-foundation/ui/ui.main.runtime.ts b/scopes/ui-foundation/ui/ui.main.runtime.ts index cd12650270af..9cb7a19a8415 100644 --- a/scopes/ui-foundation/ui/ui.main.runtime.ts +++ b/scopes/ui-foundation/ui/ui.main.runtime.ts @@ -92,7 +92,7 @@ export type RuntimeOptions = { /** * name of the UI root to load. */ - uiRootName?: string; + uiRootAspectIdOrName?: string; /** * component selector pattern to load. @@ -213,17 +213,17 @@ export class UiMain { /** * create a build of the given UI root. */ - async build(uiRootName?: string, customOutputPath?: string): Promise { + async build(uiRootAspectIdOrName?: string, customOutputPath?: string): Promise { // TODO: change to MultiStats from webpack once they export it in their types - this.logger.debug(`build, uiRootName: "${uiRootName}"`); - const maybeUiRoot = this.getUi(uiRootName); + this.logger.debug(`build, uiRootAspectIdOrName: "${uiRootAspectIdOrName}"`); + const maybeUiRoot = this.getUi(uiRootAspectIdOrName); - if (!maybeUiRoot) throw new UnknownUI(uiRootName, this.possibleUis()); - const [name, uiRoot] = maybeUiRoot; + if (!maybeUiRoot) throw new UnknownUI(uiRootAspectIdOrName, this.possibleUis()); + const [uiRootAspectId, uiRoot] = maybeUiRoot; // TODO: @uri refactor all dev server related code to use the bundler extension instead. const ssr = uiRoot.buildOptions?.ssr || false; - const mainEntry = await this.generateRoot(await uiRoot.resolveAspects(UIRuntime.name), name); + const mainEntry = await this.generateRoot(await uiRoot.resolveAspects(UIRuntime.name), uiRootAspectId); const outputPath = customOutputPath || uiRoot.path; const browserConfig = createWebpackConfig(outputPath, [mainEntry], uiRoot.name, await this.publicDir(uiRoot)); @@ -231,10 +231,10 @@ export class UiMain { const config = [browserConfig, ssrConfig].filter((x) => !!x) as webpack.Configuration[]; const compiler = webpack(config); - this.logger.debug(`build, uiRootName: "${uiRootName}" running webpack`); + this.logger.debug(`build, uiRootAspectIdOrName: "${uiRootAspectIdOrName}" running webpack`); const compilerRun = promisify(compiler.run.bind(compiler)); const results = await compilerRun(); - this.logger.debug(`build, uiRootName: "${uiRootName}" completed webpack`); + this.logger.debug(`build, uiRootAspectIdOrName: "${uiRootAspectIdOrName}" completed webpack`); if (!results) throw new UnknownBuildError(); if (results?.hasErrors()) { this.clearConsole(); @@ -258,25 +258,25 @@ export class UiMain { /** * create a Bit UI runtime. */ - async createRuntime({ uiRootName, pattern, dev, port, rebuild, verbose, skipUiBuild }: RuntimeOptions) { - const maybeUiRoot = this.getUi(uiRootName); - if (!maybeUiRoot) throw new UnknownUI(uiRootName, this.possibleUis()); + async createRuntime({ uiRootAspectIdOrName, pattern, dev, port, rebuild, verbose, skipUiBuild }: RuntimeOptions) { + const maybeUiRoot = this.getUi(uiRootAspectIdOrName); + if (!maybeUiRoot) throw new UnknownUI(uiRootAspectIdOrName, this.possibleUis()); - const [name, uiRoot] = maybeUiRoot; + const [uiRootAspectId, uiRoot] = maybeUiRoot; const plugins = await this.initiatePlugins({ verbose, pattern, }); - if (this.componentExtension.isHost(name)) this.componentExtension.setHostPriority(name); + if (this.componentExtension.isHost(uiRootAspectId)) this.componentExtension.setHostPriority(uiRootAspectId); const publicDir = await this.publicDir(uiRoot); const uiServer = UIServer.create({ express: this.express, graphql: this.graphql, uiRoot, - uiRootExtension: name, + uiRootExtension: uiRootAspectId, ui: this, logger: this.logger, publicDir, @@ -288,13 +288,15 @@ export class UiMain { if (dev) { await uiServer.dev({ portRange: port || this.config.portRange }); } else { - if (!skipUiBuild) await this.buildUI(name, uiRoot, rebuild); - const bundleUiPath = this.getBundleUiPath(); + if (!skipUiBuild) await this.buildUI(uiRootAspectId, uiRoot, rebuild); + const bundleUiPath = this.getBundleUiPath(uiRootAspectId); const bundleUiPublicPath = bundleUiPath ? join(bundleUiPath, publicDir) : undefined; const bundleUiRoot = this._isBundleUiServed && bundleUiPublicPath && existsSync(bundleUiPublicPath || '') ? bundleUiPublicPath : undefined; + if (bundleUiRoot) + this.logger.debug(`UI createRuntime of ${uiRootAspectId}, bundle will be served from ${bundleUiRoot}`); await uiServer.start({ portRange: port || this.config.portRange, bundleUiRoot }); } @@ -396,11 +398,11 @@ export class UiMain { /** * get a UI runtime instance. */ - getUi(uiRootName?: string): [string, UIRoot] | undefined { - if (uiRootName) { - const root = this.uiRootSlot.get(uiRootName) || this.getUiByName(uiRootName); + getUi(uiRootAspectIdOrName?: string): [string, UIRoot] | undefined { + if (uiRootAspectIdOrName) { + const root = this.uiRootSlot.get(uiRootAspectIdOrName) || this.getUiByName(uiRootAspectIdOrName); if (!root) return undefined; - return [uiRootName, root]; + return [uiRootAspectIdOrName, root]; } const uis = this.uiRootSlot.toArray(); if (uis.length === 1) return uis[0]; @@ -411,8 +413,8 @@ export class UiMain { return Boolean(this.componentExtension.getHost()); } - getUiName(uiRootName?: string): string | undefined { - const [, ui] = this.getUi(uiRootName) || []; + getUiName(uiRootAspectIdOrName?: string): string | undefined { + const [, ui] = this.getUi(uiRootAspectIdOrName) || []; if (!ui) return undefined; return ui.name; @@ -461,42 +463,46 @@ export class UiMain { return port; } - private async buildUI(name: string, uiRoot: UIRoot, rebuild?: boolean): Promise { - this.logger.debug(`buildUI, name ${name}`); + private async buildUI(uiRootAspectId: string, uiRoot: UIRoot, rebuild?: boolean): Promise { + this.logger.debug(`buildUI, uiRootAspectId ${uiRootAspectId}`); const overwrite = this.getOverwriteBuildFn(); - if (overwrite) return overwrite(name, uiRoot, rebuild); + if (overwrite) return overwrite(uiRootAspectId, uiRoot, rebuild); - this._isBundleUiServed = await this.shouldServeBundleUi(uiRoot, rebuild); - await this.buildIfChanged(name, uiRoot, rebuild); - await this.buildIfNoBundle(name, uiRoot); + this._isBundleUiServed = await this.shouldServeBundleUi(uiRootAspectId, uiRoot, rebuild); + await this.buildIfChanged(uiRootAspectId, uiRoot, rebuild); + await this.buildIfNoBundle(uiRootAspectId, uiRoot); return ''; } - private async shouldServeBundleUi(uiRoot: UIRoot, force: boolean | undefined): Promise { + private async shouldServeBundleUi( + uiRootAspectId: string, + uiRoot: UIRoot, + force: boolean | undefined + ): Promise { if (!uiRoot.buildOptions?.prebundle) { return false; } const currentBundleUiHash = await this.createBundleUiHash(uiRoot); - const cachedBundleUiHash = this.readBundleUiHash(); + const cachedBundleUiHash = this.readBundleUiHash(uiRootAspectId); const isLocalBuildAvailable = existsSync(join(uiRoot.path, await this.publicDir(uiRoot))); return currentBundleUiHash === cachedBundleUiHash && !isLocalBuildAvailable && !force; } - async buildIfChanged(name: string, uiRoot: UIRoot, force: boolean | undefined): Promise { - this.logger.debug(`buildIfChanged, name ${name}`); + async buildIfChanged(uiRootAspectId: string, uiRoot: UIRoot, force: boolean | undefined): Promise { + this.logger.debug(`buildIfChanged, uiRootAspectId ${uiRootAspectId}`); if (this._isBundleUiServed) { - this.logger.debug(`buildIfChanged, name ${name}, returned from ui bundle cache`); + this.logger.debug(`buildIfChanged, uiRootAspectId ${uiRootAspectId}, returned from ui bundle cache`); return false; } const currentBuildUiHash = await this.createBuildUiHash(uiRoot); const cachedBuildUiHash = await this.cache.get(uiRoot.path); if (currentBuildUiHash === cachedBuildUiHash && !force) { - this.logger.debug(`buildIfChanged, name ${name}, returned from ui build cache`); + this.logger.debug(`buildIfChanged, uiRootAspectId ${uiRootAspectId}, returned from ui build cache`); return false; } @@ -514,7 +520,7 @@ export class UiMain { ); } - await this.build(name); + await this.build(uiRootAspectId); await this.cache.set(uiRoot.path, currentBuildUiHash); return true; } @@ -543,8 +549,8 @@ export class UiMain { return sha1(aspectIds.join('')); } - private readBundleUiHash() { - const bundleUiPathFromBvm = this.getBundleUiPath(); + private readBundleUiHash(uiRootAspectId: string) { + const bundleUiPathFromBvm = this.getBundleUiPath(uiRootAspectId); if (!bundleUiPathFromBvm) { return ''; } @@ -555,28 +561,28 @@ export class UiMain { return ''; } - private getBundleUiPath(): string | undefined { + private getBundleUiPath(uiRootAspectId: string): string | undefined { try { const uiPathFromBvm = getAspectDirFromBvm(UIAspect.id); - return join(uiPathFromBvm, BundleUiTask.getArtifactDirectory()); + return join(uiPathFromBvm, BundleUiTask.getArtifactDirectory(uiRootAspectId)); } catch (err) { - this.logger.info(`getBundleUiPath, getAspectDirFromBvm failed with err: ${err}`); + this.logger.error(`getBundleUiPath, getAspectDirFromBvm failed with err: ${err}`); return undefined; } } - private async buildIfNoBundle(name: string, uiRoot: UIRoot): Promise { + private async buildIfNoBundle(uiRootAspectId: string, uiRoot: UIRoot): Promise { if (this._isBundleUiServed) return false; const config = createWebpackConfig( uiRoot.path, - [await this.generateRoot(await uiRoot.resolveAspects(UIRuntime.name), name)], + [await this.generateRoot(await uiRoot.resolveAspects(UIRuntime.name), uiRootAspectId)], uiRoot.name, await this.publicDir(uiRoot) ); if (config.output?.path && fs.pathExistsSync(config.output.path)) return false; const hash = await this.createBuildUiHash(uiRoot); - await this.build(name); + await this.build(uiRootAspectId); await this.cache.set(uiRoot.path, hash); return true; } From 74d9e035c8787eee7598befe990006679ad44339 Mon Sep 17 00:00:00 2001 From: Konstantinos Leimonis Date: Mon, 16 Oct 2023 10:39:34 +0100 Subject: [PATCH 2/3] update(bundle-ui): use of glob pattern instead of rootDir --- scopes/ui-foundation/ui/bundle-ui.task.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scopes/ui-foundation/ui/bundle-ui.task.ts b/scopes/ui-foundation/ui/bundle-ui.task.ts index a039fc949597..4b4b1ba650f3 100644 --- a/scopes/ui-foundation/ui/bundle-ui.task.ts +++ b/scopes/ui-foundation/ui/bundle-ui.task.ts @@ -68,16 +68,16 @@ export class BundleUiTask implements BuildTask { } static getArtifactDef() { + const scopeRootDir = BundleUiTask.getArtifactDirectory(UIROOT_ASPECT_IDS.SCOPE); + const workspaceRootDir = BundleUiTask.getArtifactDirectory(UIROOT_ASPECT_IDS.WORKSPACE); return [ { name: `${BUNDLE_UI_DIR}-${BUNDLE_UIROOT_DIR[UIROOT_ASPECT_IDS.SCOPE]}`, - globPatterns: ['**'], - rootDir: BundleUiTask.getArtifactDirectory(UIROOT_ASPECT_IDS.SCOPE), + globPatterns: [`${scopeRootDir}/**`], }, { name: `${BUNDLE_UI_DIR}-${BUNDLE_UIROOT_DIR[UIROOT_ASPECT_IDS.WORKSPACE]}`, - globPatterns: ['**'], - rootDir: BundleUiTask.getArtifactDirectory(UIROOT_ASPECT_IDS.WORKSPACE), + globPatterns: [`${workspaceRootDir}/**`], }, ]; } From 6689b714ee7d06eeba84d693fe40bddc4f8e20ce Mon Sep 17 00:00:00 2001 From: Konstantinos Leimonis Date: Mon, 16 Oct 2023 11:39:02 +0100 Subject: [PATCH 3/3] deprecate(ui-server): uiRootName runtime option key --- scopes/ui-foundation/ui/ui.main.runtime.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/scopes/ui-foundation/ui/ui.main.runtime.ts b/scopes/ui-foundation/ui/ui.main.runtime.ts index 9cb7a19a8415..4075e57aa65d 100644 --- a/scopes/ui-foundation/ui/ui.main.runtime.ts +++ b/scopes/ui-foundation/ui/ui.main.runtime.ts @@ -92,6 +92,7 @@ export type RuntimeOptions = { /** * name of the UI root to load. */ + uiRootName?: string; uiRootAspectIdOrName?: string; /** @@ -258,7 +259,18 @@ export class UiMain { /** * create a Bit UI runtime. */ - async createRuntime({ uiRootAspectIdOrName, pattern, dev, port, rebuild, verbose, skipUiBuild }: RuntimeOptions) { + async createRuntime({ + uiRootName, + uiRootAspectIdOrName, + pattern, + dev, + port, + rebuild, + verbose, + skipUiBuild, + }: RuntimeOptions) { + // uiRootName to be deprecated + uiRootAspectIdOrName = uiRootName || uiRootAspectIdOrName; const maybeUiRoot = this.getUi(uiRootAspectIdOrName); if (!maybeUiRoot) throw new UnknownUI(uiRootAspectIdOrName, this.possibleUis());