diff --git a/.bitmap b/.bitmap index 16f9b3c0..2e633e7a 100644 --- a/.bitmap +++ b/.bitmap @@ -10,144 +10,175 @@ { "angular-env": { + "name": "angular-env", "scope": "bitdev.angular", "version": "1.0.1", "mainFile": "index.ts", "rootDir": "angular/envs/angular-env" }, "app-types/angular-app-type": { + "name": "app-types/angular-app-type", "scope": "bitdev.angular", "version": "1.0.0", "mainFile": "index.ts", "rootDir": "angular/app-types/angular-app-type" }, "dev-services/common": { + "name": "dev-services/common", "scope": "bitdev.angular", "version": "1.0.0", "mainFile": "index.ts", "rootDir": "angular/devkit/common" }, "dev-services/compiler/elements": { + "name": "dev-services/compiler/elements", "scope": "bitdev.angular", "version": "1.0.0", "mainFile": "index.ts", "rootDir": "angular/devkit/compiler/elements" }, "dev-services/compiler/multi-compiler": { + "name": "dev-services/compiler/multi-compiler", "scope": "bitdev.angular", "version": "1.0.0", "mainFile": "index.ts", "rootDir": "angular/devkit/compiler/multi-compiler" }, "dev-services/compiler/ng-packagr": { + "name": "dev-services/compiler/ng-packagr", "scope": "bitdev.angular", "version": "1.0.0", "mainFile": "index.ts", "rootDir": "angular/devkit/compiler/ng-packagr" }, "dev-services/linter/eslint": { + "name": "dev-services/linter/eslint", "scope": "bitdev.angular", "version": "1.0.0", "mainFile": "index.js", "rootDir": "angular/devkit/linter/eslint" }, "dev-services/ngcc": { + "name": "dev-services/ngcc", "scope": "bitdev.angular", "version": "1.0.0", "mainFile": "index.ts", "rootDir": "angular/devkit/ngcc" }, "dev-services/preview/mounter": { + "name": "dev-services/preview/mounter", "scope": "bitdev.angular", "version": "1.0.0", "mainFile": "index.ts", "rootDir": "angular/devkit/preview/mounter" }, "dev-services/preview/preview": { + "name": "dev-services/preview/preview", "scope": "bitdev.angular", "version": "1.0.0", "mainFile": "index.ts", "rootDir": "angular/devkit/preview/preview" }, "dev-services/preview/runtime": { + "name": "dev-services/preview/runtime", "scope": "bitdev.angular", "version": "1.0.0", "mainFile": "index.ts", "rootDir": "angular/devkit/preview/runtime" }, + "dev-services/vite": { + "name": "dev-services/vite", + "scope": "", + "version": "", + "mainFile": "index.ts", + "rootDir": "angular/devkit/vite" + }, "dev-services/webpack": { + "name": "dev-services/webpack", "scope": "bitdev.angular", "version": "1.0.0", "mainFile": "index.ts", "rootDir": "angular/devkit/webpack" }, "envs/angular-v12-env": { + "name": "envs/angular-v12-env", "scope": "bitdev.angular", "version": "1.0.1", "mainFile": "index.ts", "rootDir": "angular/envs/angular-v12-env" }, "envs/angular-v13-env": { + "name": "envs/angular-v13-env", "scope": "bitdev.angular", "version": "1.0.1", "mainFile": "index.ts", "rootDir": "angular/envs/angular-v13-env" }, "envs/angular-v14-env": { + "name": "envs/angular-v14-env", "scope": "bitdev.angular", "version": "1.0.1", "mainFile": "index.ts", "rootDir": "angular/envs/angular-v14-env" }, "envs/angular-v15-env": { + "name": "envs/angular-v15-env", "scope": "bitdev.angular", "version": "1.0.1", "mainFile": "index.ts", "rootDir": "angular/envs/angular-v15-env" }, "envs/angular-v16-env": { + "name": "envs/angular-v16-env", "scope": "bitdev.angular", "version": "1.0.1", "mainFile": "index.ts", "rootDir": "angular/envs/angular-v16-env" }, "envs/base-env": { + "name": "envs/base-env", "scope": "bitdev.angular", "version": "1.0.1", "mainFile": "index.ts", "rootDir": "angular/envs/base-env" }, "examples/my-angular-env": { + "name": "examples/my-angular-env", "scope": "bitdev.angular", "version": "1.0.1", "mainFile": "index.ts", "rootDir": "angular/examples/my-angular-env" }, "examples/my-angular-v12-env": { + "name": "examples/my-angular-v12-env", "scope": "bitdev.angular", "version": "1.0.1", "mainFile": "index.ts", "rootDir": "angular/examples/my-angular-v12-env" }, "examples/my-angular-v13-env": { + "name": "examples/my-angular-v13-env", "scope": "bitdev.angular", "version": "1.0.1", "mainFile": "index.ts", "rootDir": "angular/examples/my-angular-v13-env" }, "examples/my-angular-v14-env": { + "name": "examples/my-angular-v14-env", "scope": "bitdev.angular", "version": "1.0.1", "mainFile": "index.ts", "rootDir": "angular/examples/my-angular-v14-env" }, "examples/my-angular-v15-env": { + "name": "examples/my-angular-v15-env", "scope": "bitdev.angular", "version": "1.0.1", "mainFile": "index.ts", "rootDir": "angular/examples/my-angular-v15-env" }, "examples/my-angular-v16-env": { + "name": "examples/my-angular-v16-env", "scope": "bitdev.angular", "version": "1.0.1", "mainFile": "index.ts", @@ -160,16 +191,18 @@ "rootDir": "angular/readme" },*/ "templates/generators": { + "name": "templates/generators", "scope": "bitdev.angular", "version": "1.0.0", "mainFile": "index.ts", "rootDir": "angular/templates/generators" }, "templates/starters": { + "name": "templates/starters", "scope": "bitdev.angular", "version": "1.0.1", "mainFile": "index.ts", "rootDir": "angular/templates/starters" }, - "$schema-version": "16.0.0" + "$schema-version": "17.0.0" } diff --git a/angular/app-types/angular-app-type/angular-app-options.ts b/angular/app-types/angular-app-type/angular-app-options.ts index f80a233f..b0b9466a 100644 --- a/angular/app-types/angular-app-type/angular-app-options.ts +++ b/angular/app-types/angular-app-type/angular-app-options.ts @@ -18,7 +18,7 @@ export type AngularAppOptions = { /** * Instance of bundler to use. default is Webpack. */ - bundler?: Bundler; + bundler?: Bundler | string; /** * Set webpack build transformers diff --git a/angular/app-types/angular-app-type/angular.app-type.ts b/angular/app-types/angular-app-type/angular.app-type.ts index 319a2f51..9e3f0351 100644 --- a/angular/app-types/angular-app-type/angular.app-type.ts +++ b/angular/app-types/angular-app-type/angular.app-type.ts @@ -1,11 +1,10 @@ -import { GenericAngularEnv, getWorkspace } from '@bitdev/angular.dev-services.common'; +import { GenericAngularEnv, getWorkspace, NG_APP_NAME } from '@bitdev/angular.dev-services.common'; import { Application, ApplicationType } from '@teambit/application'; import { DependencyResolverAspect, DependencyResolverMain } from '@teambit/dependency-resolver'; import { EnvContext, EnvHandler } from '@teambit/envs'; import { Workspace } from '@teambit/workspace'; import { AngularAppOptions } from './angular-app-options'; import { AngularApp } from './angular.application'; -import { NG_APP_NAME } from './utils'; interface AngularAppTypeOptions { name?: string; diff --git a/angular/app-types/angular-app-type/angular.application.ts b/angular/app-types/angular-app-type/angular.application.ts index 9e6029ec..f2413699 100644 --- a/angular/app-types/angular-app-type/angular.application.ts +++ b/angular/app-types/angular-app-type/angular.application.ts @@ -55,10 +55,10 @@ export class AngularApp implements Application { return join(artifactsDir, this.name); } - private getDevServerContext(context: AppContext): DevServerContext { + private getDevServerContext(context: AppContext, appRootPath: string): DevServerContext { return Object.assign(cloneDeep(context), { entry: [], - rootPath: '', + rootPath: appRootPath, publicPath: `${this.publicDir}/${this.options.name}`, title: this.options.name }); @@ -80,28 +80,23 @@ export class AngularApp implements Application { }); } - private async generateTsConfig(appRootPath: string, tsconfigPath: string): Promise { + private generateTsConfig(bitCmps: Component[], appRootPath: string, tsconfigPath: string): string { const tsconfigJSON = readConfigFile(tsconfigPath, sys.readFile).config; // Add the paths to tsconfig to remap bit components to local folders tsconfigJSON.compilerOptions.paths = tsconfigJSON.compilerOptions.paths || {}; - if (this.workspace) { - const workspaceCmpsIDs = await this.workspace.listIds(); - const bitCmps = await this.workspace.getMany(workspaceCmpsIDs); - bitCmps.forEach((dep: Component) => { - let componentDir = this.workspace?.componentDir(dep.id, { - ignoreScopeAndVersion: true, - ignoreVersion: true - }); - if (componentDir) { - componentDir = pathNormalizeToLinux(componentDir); - const pkgName = this.depsResolver.getPackageName(dep); - // TODO we should find a way to use the real entry file based on the component config because people can change it - tsconfigJSON.compilerOptions.paths[pkgName] = [`${componentDir}/public-api.ts`, `${componentDir}`]; - tsconfigJSON.compilerOptions.paths[`${pkgName}/*`] = [`${componentDir}/*`]; - } - }); - } + bitCmps.forEach((dep: Component) => { + let componentDir = this.workspace?.componentDir(dep.id, { + ignoreVersion: true + }); + if (componentDir) { + componentDir = pathNormalizeToLinux(componentDir); + const pkgName = this.depsResolver.getPackageName(dep); + // TODO we should find a way to use the real entry file based on the component config because people can change it + tsconfigJSON.compilerOptions.paths[pkgName] = [`${componentDir}/public-api.ts`, `${componentDir}`]; + tsconfigJSON.compilerOptions.paths[`${pkgName}/*`] = [`${componentDir}/*`]; + } + }); const tsconfigContent = expandIncludeExclude(tsconfigJSON, this.tsconfigPath, [appRootPath]); const hash = objectHash(tsconfigContent); @@ -116,13 +111,17 @@ export class AngularApp implements Application { } async getDevServer(context: AppContext): Promise { - const appRootPath = this.workspace?.componentDir(context.appComponent.id, { - ignoreScopeAndVersion: true, + if(!this.workspace) { + throw new Error('workspace is not defined'); + } + const appRootPath = this.workspace.componentDir(context.appComponent.id, { ignoreVersion: true }) || ''; const tsconfigPath = join(appRootPath, this.options.angularServeOptions.tsConfig); - await this.generateTsConfig(appRootPath, tsconfigPath); - const devServerContext = this.getDevServerContext(context); + const workspaceCmpsIDs = await this.workspace.listIds(); + const bitCmps = await this.workspace.getMany(workspaceCmpsIDs); + this.generateTsConfig(bitCmps, appRootPath, tsconfigPath); + const devServerContext = this.getDevServerContext(context, this.options.bundler === 'vite' ? '' : appRootPath); const preview = this.preview(this.envContext); return preview.getDevServer(devServerContext)(this.envContext); @@ -136,8 +135,12 @@ export class AngularApp implements Application { } async getBundler(context: AppBuildContext): Promise { - if (this.options.bundler) { - return this.options.bundler; + if (typeof this.options.bundler !== 'string') { + return this.options.bundler as Bundler; + } + + if (this.options.bundler === 'vite') { + throw new Error('implement vite bundler'); } const { capsule, artifactsDir } = context; @@ -145,7 +148,7 @@ export class AngularApp implements Application { const outputPath = pathNormalizeToLinux(join(capsule.path, publicDir)); const appRootPath = context.capsule.path; const tsconfigPath = join(appRootPath, this.options.angularBuildOptions.tsConfig); - await this.generateTsConfig(appRootPath, tsconfigPath); + this.generateTsConfig([capsule?.component], appRootPath, tsconfigPath); const preview = this.preview(this.envContext) as AngularPreview; const bundlerContext: BundlerContext = Object.assign(cloneDeep(context), { diff --git a/angular/app-types/angular-app-type/index.ts b/angular/app-types/angular-app-type/index.ts index 1dc5b210..72be5184 100644 --- a/angular/app-types/angular-app-type/index.ts +++ b/angular/app-types/angular-app-type/index.ts @@ -1,4 +1,4 @@ export { AngularAppType } from './angular.app-type'; export type { AngularDeployContext } from './deploy-context'; export type { AngularAppOptions } from './angular-app-options'; -export { NG_APP_NAME, NG_APP_PATTERN, componentIsApp } from './utils'; +export { NG_APP_NAME, NG_APP_PATTERN } from '@bitdev/angular.dev-services.common'; diff --git a/angular/app-types/angular-app-type/utils.ts b/angular/app-types/angular-app-type/utils.ts index f95cc50b..e4fc58b8 100644 --- a/angular/app-types/angular-app-type/utils.ts +++ b/angular/app-types/angular-app-type/utils.ts @@ -1,20 +1,7 @@ -import { ApplicationMain } from "@teambit/application"; -import { Component } from "@teambit/component"; import { pathNormalizeToLinux } from "@teambit/legacy/dist/utils"; import { flatten } from 'lodash'; import { relative, dirname } from 'path'; -export const NG_APP_NAME = 'ng-app'; -export const NG_APP_PATTERN = `*.${NG_APP_NAME}.*`; - -export function componentIsApp(component: Component, application: ApplicationMain): boolean { - // We first check if the component is registered as an app - return !!application.listAppsById(component.id) - // If it returns false, it might be because the app has never been compiled and has not been detected as an app yet - // In this case we check all the existing files for the ng app pattern - || component.filesystem.byGlob([NG_APP_PATTERN]).length > 0; -} - /** * Takes a tsconfig.json file, a list of component directories, and returns a new tsconfig.json file with the include * and exclude properties expanded to include all the component directories diff --git a/angular/devkit/common/env-options.ts b/angular/devkit/common/env-options.ts index 9682ebfa..0637c412 100644 --- a/angular/devkit/common/env-options.ts +++ b/angular/devkit/common/env-options.ts @@ -1,4 +1,4 @@ -import { WebpackConfigWithDevServer } from "@teambit/webpack"; +import { WebpackConfigWithDevServer } from '@teambit/webpack'; export type WebpackConfigFactory = (opts: any) => Promise; @@ -14,7 +14,6 @@ export type AngularEnvOptions = { * Whether ngcc should be run as part of postinstall / compile / build ... */ useNgcc?: boolean; - jestConfigPath: string; jestModulePath: string; ngPackagrModulePath: string; @@ -23,4 +22,17 @@ export type AngularEnvOptions = { webpackConfigFactory?: WebpackConfigFactory; webpackDevServerModulePath?: string; webpackModulePath?: string; + /** + * The dev server to use: webpack or vite. + * Vite only works for apps, not preview yet. + * @default 'webpack' + */ + devServer?: 'webpack' | 'vite'; + /** + * The bundler to use: webpack or vite. + * Vite only works for apps, not preview yet. + * @default 'webpack' + */ + // TODO: enable this once we have a working vite bundler + // bundler?: 'webpack' | 'vite'; }; diff --git a/angular/devkit/common/index.ts b/angular/devkit/common/index.ts index 4136a2e9..5b9be709 100644 --- a/angular/devkit/common/index.ts +++ b/angular/devkit/common/index.ts @@ -1,4 +1,4 @@ -export type { AngularEnvOptions } from './env-options'; +export type { AngularEnvOptions, WebpackConfigFactory } from './env-options'; export { GenericAngularEnv, DevServerOptions, BrowserOptions } from './generic-angular-env'; export * from './utils'; export { AngularComponentTemplateOptions } from './templates'; diff --git a/angular/devkit/common/utils.ts b/angular/devkit/common/utils.ts index 23ec8600..0d387e80 100644 --- a/angular/devkit/common/utils.ts +++ b/angular/devkit/common/utils.ts @@ -1,8 +1,33 @@ -import { ComponentID } from '@teambit/component'; +import { AppBuildContext, AppContext, ApplicationMain } from '@teambit/application'; +import { BundlerContext, DevServerContext } from '@teambit/bundler'; +import { Component, ComponentID } from '@teambit/component'; +import { DevFilesMain } from '@teambit/dev-files'; import { EnvContext } from '@teambit/envs'; import { IsolatorMain } from '@teambit/isolator'; -import { Workspace, WorkspaceAspect } from '@teambit/workspace'; -import { resolve } from 'path'; +import { pathNormalizeToLinux } from '@teambit/legacy/dist/utils'; +import { PkgMain } from '@teambit/pkg'; +import TesterAspect from '@teambit/tester'; +import WorkspaceAspect, { Workspace } from '@teambit/workspace'; +import { existsSync, mkdirSync, writeFileSync } from 'fs-extra'; +import objectHash from 'object-hash'; +import { join, posix, resolve } from 'path'; +import { readConfigFile, sys } from 'typescript'; + +export const NG_APP_NAME = 'ng-app'; +export const NG_APP_PATTERN = `*.${NG_APP_NAME}.*`; + +export enum BundlerSetup { + Serve = 'serve', + Build = 'build', +} + +export function componentIsApp(component: Component, application: ApplicationMain): boolean { + // We first check if the component is registered as an app + return !!application.listAppsById(component.id) + // If it returns false, it might be because the app has never been compiled and has not been detected as an app yet + // In this case we check all the existing files for the ng app pattern + || component.filesystem.byGlob([NG_APP_PATTERN]).length > 0; +} /** * Returns the workspace instance from the context, if it's available, or undefined otherwise. @@ -58,6 +83,18 @@ export function optionValue(value: T | undefined, defaultValue: T) { return typeof value === 'undefined' ? defaultValue : value; } +/** + * This uses a dynamic import to load a module which may be ESM. + * CommonJS code can load ESM code via a dynamic import. Unfortunately, TypeScript + * will currently, unconditionally downlevel dynamic import into a require call. + * require calls cannot load ESM code and will result in a runtime error. To work around + * this, a Function constructor is used to prevent TypeScript from changing the dynamic import. + * Once TypeScript provides support for keeping the dynamic import, this workaround can + * be dropped. + * + * @param modulePath The path of the module to load. + * @returns A Promise that resolves to the dynamically imported module. + */ export async function loadEsmModule(modulePath: string): Promise { try { return await import(modulePath); @@ -74,3 +111,127 @@ export function cmpIdToPkgName(componentId: ComponentID) { const partsToJoin = scope ? [scope, name] : [name]; return `@${partsToJoin.join('.')}`; } + +export function isBuildContext(context: DevServerContext | BundlerContext): context is BundlerContext { + return (context as BundlerContext).capsuleNetwork !== undefined; +} + +export function isAppDevContext(context: DevServerContext | AppContext): context is DevServerContext & AppContext { + return (context as any).appName !== undefined; +} + +export function isAppBuildContext( + context: BundlerContext | AppBuildContext +): context is BundlerContext & AppBuildContext { + return (context as any).appName !== undefined; +} + + +const writeHash = new Map(); +const timestamp = Date.now(); + +/** + * Add the list of files to include in the typescript compilation as absolute paths + */ +export function generateTsConfig( + appPath: string, + includePaths: string[], + excludePaths: string[] = [], + tsPaths: { [key: string]: string[] } +): string { + const tsconfigPath = join(appPath, 'tsconfig.app.json'); + const tsconfigJSON = readConfigFile(tsconfigPath, sys.readFile).config; + const pAppPath = pathNormalizeToLinux(appPath); + + // tsconfigJSON.config.angularCompilerOptions.enableIvy = this.enableIvy; + tsconfigJSON.files = tsconfigJSON.files.map((file: string) => posix.join(pAppPath, file)); + tsconfigJSON.include = [ + ...tsconfigJSON.include.map((file: string) => posix.join(pAppPath, file)), + ...includePaths.map((path) => posix.join(path, '**/*.ts')) + ]; + tsconfigJSON.exclude = [ + ...tsconfigJSON.exclude.map((file: string) => posix.join(pAppPath, file)), + ...excludePaths, + ]; + tsconfigJSON.compilerOptions.paths = tsPaths; + + return JSON.stringify(tsconfigJSON, undefined, 2); +} + +/** + * write a link to load custom modules dynamically. + */ +export function writeTsconfig( + context: DevServerContext | BundlerContext, + rootPath: string, + tempFolder: string, + application: ApplicationMain, + pkg: PkgMain, + devFilesMain: DevFilesMain, + workspace?: Workspace +): string { + const tsPaths: { [key: string]: string[] } = {}; + const includePaths = new Set(); + const excludePaths = new Set(); + const dirPath = join(tempFolder, context.id); + if (!existsSync(dirPath)) { + mkdirSync(dirPath, { recursive: true }); + } + + // get the list of files for existing component compositions to include in the compilation + context.components.forEach((component: Component) => { + let outputPath: string; + + const isApp = componentIsApp(component, application); + if (isApp) { + return; + } + if (isBuildContext(context)) { + const capsules = context.capsuleNetwork.graphCapsules; + const capsule = capsules.getCapsule(component.id); + if (!capsule) { + throw new Error(`No capsule found for ${component.id} in network graph`); + } + outputPath = pathNormalizeToLinux(capsule.path); + } else { + outputPath = pathNormalizeToLinux(workspace?.componentDir(component.id, { + ignoreVersion: true + }) || ''); + } + // map the package names to the workspace component paths for typescript in case a package references another local package + const pkgName = pkg.getPackageName(component); + tsPaths[pkgName] = [`${outputPath}/public-api.ts`]; + tsPaths[`${pkgName}/*`] = [`${outputPath}/*`]; + + includePaths.add(outputPath); + + // get the list of spec patterns + const devPatterns: string[] = devFilesMain.getDevPatterns(component, TesterAspect.id); + devPatterns.forEach(specPattern => { + excludePaths.add(posix.join(outputPath, specPattern)); + }); + }); + + const content = generateTsConfig(rootPath, Array.from(includePaths), Array.from(excludePaths), tsPaths); + const hash = objectHash(content); + const targetPath = join(dirPath, `__tsconfig-${timestamp}.json`); + + // write only if the link has changed (prevents triggering fs watches) + if (writeHash.get(targetPath) !== hash) { + writeFileSync(targetPath, content); + writeHash.set(targetPath, hash); + } + + return pathNormalizeToLinux(targetPath); +} + +export function getPreviewRootPath(workspace?: Workspace): string { + try { + const rootPath = workspace?.componentDir(ComponentID.fromString('bitdev.angular/dev-services/preview/preview'), { + ignoreVersion: true + }, { relative: false }) || ''; + return join(rootPath, 'preview-app'); + } catch (e) { + return resolve(require.resolve('@bitdev/angular.dev-services.preview.preview'), '../../preview-app/'); + } +} diff --git a/angular/devkit/compiler/elements/angular-elements.compiler.ts b/angular/devkit/compiler/elements/angular-elements.compiler.ts index b657be4a..f691e65e 100644 --- a/angular/devkit/compiler/elements/angular-elements.compiler.ts +++ b/angular/devkit/compiler/elements/angular-elements.compiler.ts @@ -1,7 +1,11 @@ import type { AngularCompilerOptions } from '@angular/compiler-cli'; -import { componentIsApp } from '@bitdev/angular.app-types.angular-app-type'; import type { AngularEnvOptions } from '@bitdev/angular.dev-services.common'; -import { getNodeModulesPaths, getWorkspace } from '@bitdev/angular.dev-services.common'; +import { + componentIsApp, + getNodeModulesPaths, + getWorkspace +} from '@bitdev/angular.dev-services.common'; +import { NgccProcessor } from '@bitdev/angular.dev-services.ngcc'; import { ApplicationAspect, ApplicationMain } from '@teambit/application'; import { ArtifactDefinition, @@ -16,7 +20,6 @@ import { EnvContext, EnvHandler } from '@teambit/envs'; import { IsolatorAspect, IsolatorMain } from '@teambit/isolator'; import { Timer } from '@teambit/legacy/dist/toolbox/timer'; import { Logger } from '@teambit/logger'; -import { NgccProcessor } from '@bitdev/angular.dev-services.ngcc'; import { Workspace } from '@teambit/workspace'; import chalk from 'chalk'; import { extname, join } from 'path'; diff --git a/angular/devkit/compiler/elements/preview/docs.tsx b/angular/devkit/compiler/elements/preview/docs.tsx index be454565..9643d20d 100644 --- a/angular/devkit/compiler/elements/preview/docs.tsx +++ b/angular/devkit/compiler/elements/preview/docs.tsx @@ -1,5 +1,5 @@ -import { Type } from '@angular/core'; -import { RenderingContext } from '@teambit/preview'; +import type { Type } from '@angular/core'; +import type { RenderingContext } from '@teambit/preview'; import DocsRoot from '@teambit/react.ui.docs-app'; // required here to make sure that this is loaded before the compositions file import '@angular/compiler'; diff --git a/angular/devkit/compiler/multi-compiler/ng-multi-compiler.ts b/angular/devkit/compiler/multi-compiler/ng-multi-compiler.ts index 42f559ea..25338d61 100644 --- a/angular/devkit/compiler/multi-compiler/ng-multi-compiler.ts +++ b/angular/devkit/compiler/multi-compiler/ng-multi-compiler.ts @@ -1,7 +1,8 @@ import type { AngularCompilerOptions } from '@angular/compiler-cli'; -import { componentIsApp, NG_APP_PATTERN } from '@bitdev/angular.app-types.angular-app-type'; import type { AngularEnvOptions } from '@bitdev/angular.dev-services.common'; +import { componentIsApp, NG_APP_PATTERN } from '@bitdev/angular.dev-services.common'; import { AngularElementsCompiler } from '@bitdev/angular.dev-services.compiler.elements'; +import { NgPackagrCompiler } from '@bitdev/angular.dev-services.compiler.ng-packagr'; import { ApplicationAspect, ApplicationMain } from '@teambit/application'; import { BabelAspect, BabelCompiler, BabelMain } from '@teambit/babel'; import { @@ -22,7 +23,6 @@ import { import { Component } from '@teambit/component'; import { EnvContext, EnvHandler } from '@teambit/envs'; import { Capsule } from '@teambit/isolator'; -import { NgPackagrCompiler } from '@bitdev/angular.dev-services.compiler.ng-packagr'; import { outputFileSync } from 'fs-extra'; import minimatch from 'minimatch'; import { join } from 'path'; diff --git a/angular/devkit/compiler/ng-packagr/ng-packagr.compiler.ts b/angular/devkit/compiler/ng-packagr/ng-packagr.compiler.ts index b49b3412..f202e747 100644 --- a/angular/devkit/compiler/ng-packagr/ng-packagr.compiler.ts +++ b/angular/devkit/compiler/ng-packagr/ng-packagr.compiler.ts @@ -1,6 +1,11 @@ import type { AngularCompilerOptions, ParsedConfiguration } from '@angular/compiler-cli'; -import { componentIsApp } from '@bitdev/angular.app-types.angular-app-type'; -import { AngularEnvOptions, getNodeModulesPaths, getWorkspace } from '@bitdev/angular.dev-services.common'; +import { + AngularEnvOptions, + componentIsApp, + getNodeModulesPaths, + getWorkspace +} from '@bitdev/angular.dev-services.common'; +import { NgccProcessor } from '@bitdev/angular.dev-services.ngcc'; import { ApplicationAspect, ApplicationMain } from '@teambit/application'; import { ArtifactDefinition, @@ -19,7 +24,6 @@ import PackageJsonFile from '@teambit/legacy/dist/consumer/component/package-jso import removeFilesAndEmptyDirsRecursively from '@teambit/legacy/dist/utils/fs/remove-files-and-empty-dirs-recursively'; import { Logger } from '@teambit/logger'; -import { NgccProcessor } from '@bitdev/angular.dev-services.ngcc'; import { Workspace } from '@teambit/workspace'; import chalk from 'chalk'; import { mkdirsSync, outputFileSync } from 'fs-extra'; @@ -353,7 +357,6 @@ export class NgPackagrCompiler implements Compiler { */ getPreviewComponentRootPath(component: Component): string { return this.workspace!.componentDir(component.id, { - ignoreScopeAndVersion: true, ignoreVersion: true }, { relative: true }); } diff --git a/angular/devkit/preview/mounter/mounter.ts b/angular/devkit/preview/mounter/mounter.ts index 610535de..47ade2a0 100644 --- a/angular/devkit/preview/mounter/mounter.ts +++ b/angular/devkit/preview/mounter/mounter.ts @@ -1,6 +1,6 @@ // required here to make sure that this is loaded before the compositions file import '@angular/compiler'; -import { Type } from '@angular/core'; +import type { Type } from '@angular/core'; import { ngBootstrap, NgBootstrapOptions } from '@bitdev/angular.dev-services.preview.runtime'; export interface MounterOptions extends Omit { diff --git a/angular/devkit/preview/preview/component.json b/angular/devkit/preview/preview/component.json index 4265ac2a..af8a640c 100644 --- a/angular/devkit/preview/preview/component.json +++ b/angular/devkit/preview/preview/component.json @@ -7,7 +7,10 @@ "extensions": { "teambit.dependencies/dependency-resolver": { "policy": { - "dependencies": {}, + "dependencies": { + "@types/dompurify": "3.0.2", + "dompurify": "3.0.5" + }, "peerDependencies": { "@angular/animations": ">= 8.0.0", "@angular/compiler": ">= 8.0.0", diff --git a/angular/devkit/preview/preview/docs.ts b/angular/devkit/preview/preview/docs.ts index dcfa3a8a..e01d1a1c 100644 --- a/angular/devkit/preview/preview/docs.ts +++ b/angular/devkit/preview/preview/docs.ts @@ -1,5 +1,5 @@ -import { RenderingContext } from '@teambit/preview'; -import { Type } from '@angular/core'; +import type { RenderingContext } from '@teambit/preview'; +import type { Type } from '@angular/core'; import { ReplaySubject } from 'rxjs'; window.onDocsLoad$ = window.onDocsLoad$ || new ReplaySubject(); diff --git a/angular/devkit/preview/preview/preview-app/src/app/docs/safe-html.pipe.ts b/angular/devkit/preview/preview/preview-app/src/app/docs/safe-html.pipe.ts index dcd5d7f5..28bb00c8 100644 --- a/angular/devkit/preview/preview/preview-app/src/app/docs/safe-html.pipe.ts +++ b/angular/devkit/preview/preview/preview-app/src/app/docs/safe-html.pipe.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ import { Pipe, PipeTransform } from '@angular/core'; import { DomSanitizer } from '@angular/platform-browser'; -import { sanitize } from 'dompurify'; +import dompurify from 'dompurify'; @Pipe({ name: 'safeHtml' @@ -10,7 +10,7 @@ export class SafeHtmlPipe implements PipeTransform { constructor(private sanitizer: DomSanitizer) {} public transform(value: any): any { - const sanitizedContent = sanitize(value); + const sanitizedContent = dompurify.sanitize(value); return this.sanitizer.bypassSecurityTrustHtml(sanitizedContent); } } diff --git a/angular/devkit/preview/preview/preview-app/tsconfig-esm.app.json b/angular/devkit/preview/preview/preview-app/tsconfig-esm.app.json new file mode 100644 index 00000000..6fa8ccf4 --- /dev/null +++ b/angular/devkit/preview/preview/preview-app/tsconfig-esm.app.json @@ -0,0 +1,52 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "compileOnSave": false, + "compilerOptions": { + "allowJs": true, + "allowSyntheticDefaultImports": true, + "baseUrl": "./", + "declaration": false, + "downlevelIteration": true, + "esModuleInterop": true, + "experimentalDecorators": true, + "forceConsistentCasingInFileNames": true, + "importHelpers": true, + "lib": [ + "ES2022", + "dom" + ], + "module": "ES2022", + "moduleResolution": "nodenext", + "noFallthroughCasesInSwitch": true, + "noImplicitOverride": true, + "noImplicitReturns": true, + "noPropertyAccessFromIndexSignature": true, + "outDir": "./dist/out-tsc/app", + "preserveSymlinks": false, + "skipLibCheck": true, + "sourceMap": true, + "strict": true, + "target": "ES2022", + "types": [ + "node" + ], + "useDefineForClassFields": false + }, + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true, + "enableIvy": true + }, + "files": [ + "./src/main.ts", + "./src/polyfills.ts" + ], + "include": [ + "./src/app/**/*.ts", + ], + "exclude": [ + "./src/app/**/*.spec.ts" + ] +} diff --git a/angular/devkit/preview/preview/preview-app/tsconfig.app.json b/angular/devkit/preview/preview/preview-app/tsconfig.app.json index 05fbb74a..c81463bc 100644 --- a/angular/devkit/preview/preview/preview-app/tsconfig.app.json +++ b/angular/devkit/preview/preview/preview-app/tsconfig.app.json @@ -2,6 +2,8 @@ { "compileOnSave": false, "compilerOptions": { + "allowJs": true, + "allowSyntheticDefaultImports": true, "baseUrl": "./", "outDir": "./dist/out-tsc/app", "forceConsistentCasingInFileNames": true, @@ -12,18 +14,22 @@ "declaration": false, "downlevelIteration": true, "experimentalDecorators": true, - "moduleResolution": "node", - "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, "importHelpers": true, - "allowJs": true, - "target": "es5", - "module": "esnext", - "preserveSymlinks": false, "lib": [ "es2018", "dom", "esnext.asynciterable" - ] + ], + "module": "esnext", + "moduleResolution": "node", + "noFallthroughCasesInSwitch": true, + "noImplicitReturns": true, + "outDir": "./dist/out-tsc/app", + "preserveSymlinks": false, + "sourceMap": true, + "strict": true, + "target": "es5" }, "angularCompilerOptions": { "enableI18nLegacyMessageIdFormat": false, diff --git a/angular/devkit/vite/component.json b/angular/devkit/vite/component.json new file mode 100644 index 00000000..25149525 --- /dev/null +++ b/angular/devkit/vite/component.json @@ -0,0 +1,48 @@ +{ + "componentId": { + "scope": "bitdev.angular", + "name": "dev-services/vite" + }, + "propagate": true, + "extensions": { + "teambit.dependencies/dependency-resolver": { + "policy": { + "dependencies": { + "@analogjs/vite-plugin-angular": "^0.2.0", + "@mdx-js/react": "^1.6.22", + "@originjs/vite-plugin-commonjs": "^1.0.3", + "@teambit/node.deps-detectors.detective-es6": "0.0.1", + "@types/memoizee": "0.4.5", + "@vitejs/plugin-react": "^4.0.0", + "find-root": "1.1.0", + "memoizee": "0.4.15", + "node-stdlib-browser": "^1.2.0", + "parse5-html-rewriting-stream": "7.0.0", + "remark-admonitions": "^1.2.1", + "remark-frontmatter": "^2.0.0", + "typescript": "-", + "unified": "^10.1.2", + "unist-util-remove": "^2.0.1", + "unist-util-visit": "^2.0.3", + "vite": "^4.4.9", + "vite-plugin-commonjs": "^0.8.2", + "vite-plugin-markdown": "2.1.0", + "vite-plugin-mdx": "^3.5.11", + "vite-plugin-node-polyfills": "^0.11.2", + "vite-plugin-require-transform": "^1.0.21", + "yaml": "^2.3.1" + }, + "devDependencies": { + "@types/find-root": "1.1.2", + "@types/jest": "26.0.20", + "@types/node": "12.20.4", + "@babel/runtime": "7.20.0" + }, + "peerDependencies": { + "typescript": ">= 3.5.3", + "vite": "4.4.8" + } + } + } + } +} diff --git a/angular/devkit/vite/dev-server/config.ts b/angular/devkit/vite/dev-server/config.ts new file mode 100644 index 00000000..4dc2f7d3 --- /dev/null +++ b/angular/devkit/vite/dev-server/config.ts @@ -0,0 +1,118 @@ +// @ts-nocheck +// Make sure bit recognizes the dependencies +import '@mdx-js/react'; +import 'node-stdlib-browser'; +import { relative } from 'path'; +import { defineConfig, InlineConfig } from 'vite'; +import { nodePolyfills } from 'vite-plugin-node-polyfills'; +import { ViteDevServerAspectsContext, ViteDevServerOptions } from '../ng-vite-dev-server'; +import { getHostAlias } from './utils'; +// import react from "@vitejs/plugin-react"; +// import mdx from "vite-plugin-mdx"; +// import { htmlPlugin } from "./html-plugin"; +// import mdxOptions from "./mdx-config"; + +/** + * Generate a Vite config for dev server. + * + * 1. alias/fallbacks/provide for Node APIs and host deps + * 2. module loaders like for MDX + * 3. root path, public path, entry files, and html + * 4. websocket protocol + */ +export async function configFactory(options: ViteDevServerOptions, aspectContext: ViteDevServerAspectsContext, port: number): Promise { + const { devServerContext: { publicPath, entry, id, rootPath, envRuntime, }, define, alias, plugins, transformers, } = options; + const { workspace, pubsub } = aspectContext; + const entries = entry; + + const root = options.root ?? workspace.path; + const publicDir = relative(workspace.path, publicPath); + const base = options.base ?? `/${rootPath}/`; + + // TODO: for WebpackBitReporterPlugin + // id; + // pubsub; + + // e.g. teambit.vite/examples/vue/vue@07106b6c1256c0efd94804266c07572a450cf675 + const envId = envRuntime.id; + // directory to place preview.root files + // const previewRootDir = `./node_modules/.cache/bit/teambit.ui`; + // directory to place preview entries files + // e.g. ./node_modules/.cache/bit/teambit.preview/preview/{envId}/compositions-1687107103571.js + // const entriesDir = `./node_modules/.cache/bit/teambit.preview/preview/${envId}`; + // directory to place vite cache + // TODO + // const cacheDir = `./node_modules/.cache/bit/teambit.vite/vite-dev-server/${envId}`; + const cacheDir = `./node_modules/.cache/bit/bitdev.angular/vite-dev-server/${envId}`; + + // set host deps as alias + const hostAlias = getHostAlias(options.devServerContext); + + // get full component list from workspace, and then + // - ignore them from watch + // - exclude them from optimizeDeps + const components = await workspace.list(); + const packageList = components.map(c => workspace.componentPackageName(c)); + + const config: InlineConfig = defineConfig({ + configFile: false, + envFile: false, + root, + base, + publicDir, + define, + resolve: { + mainFields: ['module'], + alias: [ + { + // this is required for the SCSS modules + find: /^~(.*)$/, + replacement: '$1' + }, + ...hostAlias, + ...alias || [] + ], + }, + // apply different cache dir for each env + cacheDir, + server: { + port, + watch: { + // 1. preview root + // 2. entry files + // 3. local packages + ignored: [ + ...packageList.map(p => `!**/node_modules/${p}/**`), + ] + }, + fs: { + strict: false + } + }, + optimizeDeps: { + entries, + // exclude: packageList, + }, + // TODO: make it replaceable and reusable + plugins: [ + nodePolyfills(), + // react(), + // mdx(mdxOptions), + // htmlPlugin(entries), + ...plugins || [], + ], + }); + + // apply transformers + if (transformers) { + transformers.forEach(transformer => { + transformer(config); + }); + } + + return config; +} + + + + diff --git a/angular/devkit/vite/dev-server/utils/host-alias.ts b/angular/devkit/vite/dev-server/utils/host-alias.ts new file mode 100644 index 00000000..c42e5253 --- /dev/null +++ b/angular/devkit/vite/dev-server/utils/host-alias.ts @@ -0,0 +1,57 @@ +// @ts-nocheck + +import findRoot from 'find-root'; +import type { Alias } from 'vite'; + +export type ViteContext = { + /** + * A path for the host root dir + * Host root dir is usually the env root dir + * This can be used in different bundle options which run require.resolve + * for example when configuring webpack aliases or webpack expose loader on the peers deps + */ + hostRootDir?: string; + /** + * Array of host dependencies, they are used later in case you use one of the following: + * + */ + hostDependencies?: string[]; + /** + * Make the hostDependencies externals. externals (from webpack docs): + * The externals configuration option provides a way of excluding dependencies from the output bundles. + * Instead, the created bundle relies on that dependency to be present in the consumer's (any end-user application) environment. + */ + externalizeHostDependencies?: boolean; + /** + * Make aliases for the hostDependencies. + * the path of each one will be resolved by [hostRootDir, process.cwd(), __dirname] + * this will usually replace the instance of import one of the host dependencies by the instance of the env provided it + */ + aliasHostDependencies?: boolean; +}; + +export function getHostAlias(context: ViteContext): Alias[] { + const alias: Alias[] = []; + const { hostDependencies: deps, aliasHostDependencies, hostRootDir } = context; + if (deps && aliasHostDependencies) { + deps.forEach(dep => { + let resolved: string; + try { + resolved = require.resolve(dep, { paths: [hostRootDir, process.cwd(), __dirname] }); + const folder = findRoot(resolved); + alias.push({ + find: dep, + replacement: folder + }) + } catch (e) { + if (resolved) { + alias.push({ + find: dep, + replacement: resolved + }) + } + } + }) + } + return alias; +} diff --git a/angular/devkit/vite/dev-server/utils/html-plugin.ts b/angular/devkit/vite/dev-server/utils/html-plugin.ts new file mode 100644 index 00000000..f0b3968b --- /dev/null +++ b/angular/devkit/vite/dev-server/utils/html-plugin.ts @@ -0,0 +1,62 @@ +import type { IncomingMessage, ServerResponse } from 'http'; +import type { Plugin } from 'vite'; + +const cleanUrl = (url: string) => url.replace(/#.*$/s, '').replace(/\?.*$/s, ''); + +const defaultHeadHtml = ` + + + + +` + +const defaultBodyHtml = ` +
+` + +const defaultGenScriptHtml = (entries: string[]): string => entries.map( + src => ``).join('\n'); + +const defaultGenHtml = (entries: string[]) => ` + + +${defaultHeadHtml} + +${defaultBodyHtml} +${defaultGenScriptHtml(entries)} + +`; + +export type HtmlPluginOptions = { + entries: string[]; + genHtml?: (entries: string[], context: { req: IncomingMessage, res: ServerResponse }) => string; +}; + +export const htmlPlugin = ({ entries, genHtml }: HtmlPluginOptions): Plugin => { + return { + name: 'html-plugin', + configureServer(server) { + return () => { + server.middlewares.use(async (req, res, next) => { + const url = req.url && cleanUrl(req.url) + if (url?.endsWith('.html')) { + res.statusCode = 200 + res.setHeader('Content-Type', 'text/html') + const preHtml = genHtml? genHtml(entries, { req, res }) : defaultGenHtml(entries); + const html = await server.transformIndexHtml( + url, + preHtml, + req.originalUrl + ) + res.end(html) + return + } + next() + }) + } + } + } +} diff --git a/angular/devkit/vite/dev-server/utils/index.ts b/angular/devkit/vite/dev-server/utils/index.ts new file mode 100644 index 00000000..3131ccc4 --- /dev/null +++ b/angular/devkit/vite/dev-server/utils/index.ts @@ -0,0 +1,3 @@ +export * from './html-plugin'; +export * from './host-alias'; +export * from './mdx-config'; diff --git a/angular/devkit/vite/dev-server/utils/mdx-config.ts b/angular/devkit/vite/dev-server/utils/mdx-config.ts new file mode 100644 index 00000000..010f7696 --- /dev/null +++ b/angular/devkit/vite/dev-server/utils/mdx-config.ts @@ -0,0 +1,107 @@ +import detectiveEs6 from '@teambit/node.deps-detectors.detective-es6'; +// @ts-ignore +import remarkNotes from 'remark-admonitions'; +import detectFrontmatter from 'remark-frontmatter'; +import type { Pluggable } from 'unified'; +import remove from 'unist-util-remove'; +import visit from 'unist-util-visit'; +import type { MdxOptions } from 'vite-plugin-mdx'; +import yaml from 'yaml'; + +type ImportSpecifier = { + /** + * relative/absolute or module name. e.g. the `y` in the example of `import x from 'y';` + */ + fromModule: string; + + /** + * is default import (e.g. `import x from 'y';`) + */ + isDefault?: boolean; + + /** + * the name used to identify the module, e.g. the `x` in the example of `import x from 'y';` + */ + identifier?: string; +}; + +const DEFAULT_RENDERER = ` +// @ts-nocheck +import React from 'react' +import { mdx } from '@mdx-js/react' + +/* @jsxRuntime classic */ +/* @jsx mdx */ +`; + +function wrapWithScopeContext(): Pluggable { + return (tree, file) => { + const imports: ImportSpecifier[] = file.data?.imports || []; + const ids = imports.reduce((identifiers: string[], importSpecifier: ImportSpecifier) => { + const newIds: string[] = []; + if (importSpecifier.identifier) newIds.push(importSpecifier.identifier); + return identifiers.concat(newIds); + }, []); + + const preNode = { + type: 'jsx', + value: ``, + }; + + const postNode = { + type: 'jsx', + value: ``, + }; + + tree.children.unshift({ + type: 'import', + value: `import { MDXScopeProvider } from '@teambit/mdx.ui.mdx-scope-context';`, + }); + + tree.children.unshift(preNode); + tree.children.push(postNode); + }; +} + +function extractMetadata(): Pluggable { + return function transformer(tree, file) { + visit(tree, 'yaml', (node: any) => { + try { + file.data.frontmatter = yaml.parse(node.value, { prettyErrors: true }); + } catch (err: any) { + throw new Error( + `failed extracting metadata/front-matter using Yaml lib, due to an error (please disregard the line/column): ${err.message}` + ); + } + }); + }; +} + +function extractImports(): Pluggable { + return function transformer(tree, file) { + visit(tree, 'import', (node: any) => { + const es6Import = detectiveEs6(node.value); + const imports: ImportSpecifier[] = Object.keys(es6Import).flatMap((dep) => { + if (!es6Import[dep].importSpecifiers) { + return { + fromModule: dep, + }; + } + return es6Import[dep].importSpecifiers.map((importSpecifier) => ({ + fromModule: dep, + identifier: importSpecifier.name, + isDefault: importSpecifier.isDefault, + })); + }); + (file.data.imports ||= []).push(...imports); + }); + + remove(tree, 'yaml'); + }; +} + +export const mdxOptions: MdxOptions = { + remarkPlugins: [remarkNotes, [detectFrontmatter, ['yaml']], extractMetadata, extractImports], + rehypePlugins: [wrapWithScopeContext], + renderer: DEFAULT_RENDERER, +} diff --git a/angular/devkit/vite/index.ts b/angular/devkit/vite/index.ts new file mode 100644 index 00000000..55424c8a --- /dev/null +++ b/angular/devkit/vite/index.ts @@ -0,0 +1 @@ +export * from './ng-vite-dev-server'; diff --git a/angular/devkit/vite/ng-vite-dev-server.ts b/angular/devkit/vite/ng-vite-dev-server.ts new file mode 100644 index 00000000..dbe6e55b --- /dev/null +++ b/angular/devkit/vite/ng-vite-dev-server.ts @@ -0,0 +1,164 @@ +import { default as ngVitePlugin } from '@analogjs/vite-plugin-angular'; +import { + AngularEnvOptions, + BrowserOptions, + DevServerOptions, + isAppDevContext +} from '@bitdev/angular.dev-services.common'; +import type { DevServer, DevServerContext } from '@teambit/bundler'; +import type { AsyncEnvHandler, EnvContext } from '@teambit/envs'; +import type { Logger } from '@teambit/logger'; +import type { PubsubMain } from '@teambit/pubsub'; +import type { Workspace } from '@teambit/workspace'; +import type { Server } from 'http'; +import { posix } from 'path'; +// @ts-ignore +import type { Alias, InlineConfig, Plugin, PluginOption } from 'vite'; +import { configFactory } from './dev-server/config'; +import { htmlPlugin } from './plugins/index-html.plugin'; + + +export type NgViteDevServerOptions = { + angularOptions: Partial; + + /** + * context of the dev server execution. + */ + devServerContext: DevServerContext; + + /** + * name of the dev server. + */ + name?: string; + + ngEnvOptions: AngularEnvOptions; + + sourceRoot?: string; + + // TODO: fix type once we can support preview with vite + transformers?: (ViteConfigTransformer | any)[]; + + // TODO: remove this once we can support preview with vite + [key: string]: any; +}; + + +export type ViteConfigTransformer = (config: InlineConfig) => void; + +export type ViteDevServerAspectsContext = { + logger: Logger; + workspace: Workspace; + pubsub: PubsubMain; +}; + +export type ViteDevServerOptions = { + /** + * name of the dev server. + */ + name?: string; + + /** + * context of the dev server execution. + */ + devServerContext: DevServerContext; + + /** + * optimize entries before passing them to Vite. + */ + optimizeEntries?: (entries: string[], context: ViteDevServerAspectsContext) => string[]; + + /** + * root path of the dev server. + */ + root?: string; + + /** + * base URL to use for all relative URLs in a document + */ + base?: string; + + /** + * variables to be injected to the dev server. + */ + define?: Record; + + /** + * alias to be injected to the dev server. + */ + alias?: Alias[]; + + /** + * list of plugins to be injected to the dev server. + */ + plugins?: PluginOption[]; + + /** + * list of transformers to modify Vite config in an advanced way. + */ + transformers?: ViteConfigTransformer[]; +}; + +export class NgViteDevServer { + id = 'ng-vite-dev-server'; + + constructor( + private options: ViteDevServerOptions, + private aspectContext: ViteDevServerAspectsContext + ) { + } + + async listen(port: number): Promise { + const config: InlineConfig = await configFactory(this.options, this.aspectContext, port); + const vite = await import('vite'); + const server = await vite.createServer(config); + await server.listen(port); + if (!server.httpServer) { + throw new Error('vite server failed to start'); + } + return server.httpServer; + } + + static from(options: NgViteDevServerOptions): AsyncEnvHandler { + return async(context: EnvContext): Promise => { + const rootPath = options.devServerContext.rootPath; + const name = options.name || 'vite-dev-server'; + const logger = context.createLogger(name); + const workspace: Workspace = context.getAspect('teambit.workspace/workspace'); + const pubsub = context.getAspect('teambit.harmony/pubsub'); + + let appRootPath: string; + let tsconfigPath: string; + if (isAppDevContext(options.devServerContext)) { // When you use `bit run ` + appRootPath = workspace?.componentDir(options.devServerContext.appComponent.id, { + ignoreVersion: true + }) || ''; + tsconfigPath = options?.angularOptions?.tsConfig ?? posix.join(appRootPath, 'tsconfig.app.json'); + } else { // When you use `bit start` + // appRootPath = getPreviewRootPath(workspace); + // tsconfigPath = writeTsconfig(options.devServerContext, appRootPath, tempFolder, application, pkg, devFilesMain, workspace); + throw new Error('vite does not support preview yet'); + } + + const opts = { + devServerContext: options.devServerContext, + root: rootPath, + base: options.angularOptions.baseHref ?? './', + plugins: [ + ngVitePlugin({ + tsconfig: tsconfigPath, + workspaceRoot: rootPath + }) as Plugin[], + htmlPlugin(options.angularOptions, rootPath, options.sourceRoot) + ] + }; + + const aspectContext: ViteDevServerAspectsContext = { + logger, + workspace, + pubsub + }; + return new NgViteDevServer(opts, aspectContext); + }; + } +} + diff --git a/angular/devkit/vite/plugins/index-file/augment-index-html.ts b/angular/devkit/vite/plugins/index-file/augment-index-html.ts new file mode 100644 index 00000000..c8f2dd48 --- /dev/null +++ b/angular/devkit/vite/plugins/index-file/augment-index-html.ts @@ -0,0 +1,266 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import { extname } from 'path'; +import { htmlRewritingStream } from './html-rewriting-stream'; +import { loadEsmModule } from '@bitdev/angular.dev-services.common'; +import { EntryPointsType } from './package-chunk-sort'; + +export type CrossOriginValue = 'none' | 'anonymous' | 'use-credentials'; + +export interface AugmentIndexHtmlOptions { + /* Input contents */ + html: string; + baseHref?: string; + deployUrl?: string; + /** crossorigin attribute setting of elements that provide CORS support */ + crossOrigin?: CrossOriginValue; + /** Used to sort the insertion of files in the HTML file */ + entrypoints: EntryPointsType[]; + /** Used to set the document default locale */ + lang?: string; + hints?: { url: string; mode: string; as?: string }[]; +} + +/* + * Helper function used by the IndexHtmlWebpackPlugin. + * Can also be directly used by builder, e.g. in order to generate an index.html + * after processing several configurations in order to build different sets of + * bundles for differential serving. + */ +export async function augmentIndexHtml( + params: AugmentIndexHtmlOptions, +): Promise<{ content: string; warnings: string[]; errors: string[] }> { + const { entrypoints, deployUrl = '', lang, baseHref, html } = params; + + const warnings: string[] = []; + const errors: string[] = []; + + let { crossOrigin = 'none' } = params; + + const stylesheets = new Set(); + const scripts = new Map(); + + // Sort files in the order we want to insert them by entrypoint + for (const [file, isModule] of entrypoints) { + const extension = extname(file); + if (scripts.has(file) || stylesheets.has(file)) { + continue; + } + + switch (extension) { + case '.js': + case '.jsx': + case '.ts': + case '.tsx': + // Also, non entrypoints need to be loaded as no module as they can contain problematic code. + scripts.set(file, isModule); + break; + case '.mjs': + case '.mts': + if (!isModule) { + // It would be very confusing to link an `*.mjs` file in a non-module script context, + // so we disallow it entirely. + throw new Error('`.mjs` & `.mts` files *must* set `isModule` to `true`.'); + } + scripts.set(file, true /* isModule */); + break; + case '.css': + case '.scss': + case '.sass': + case '.less': + stylesheets.add(file); + break; + } + } + + let scriptTags: string[] = []; + for (const [src, isModule] of scripts) { + const attrs = [`src="${deployUrl}${src}"`]; + + // This is also need for non-entry-points as they may contain problematic code. + if (isModule) { + attrs.push('type="module"'); + } else { + attrs.push('defer'); + } + + if (crossOrigin !== 'none') { + attrs.push(`crossorigin="${crossOrigin}"`); + } + + scriptTags.push(``); + } + + let linkTags: string[] = []; + for (const src of stylesheets) { + const attrs = [`rel="stylesheet"`, `href="${deployUrl}${src}"`]; + + if (crossOrigin !== 'none') { + attrs.push(`crossorigin="${crossOrigin}"`); + } + + linkTags.push(``); + } + + if (params.hints?.length) { + for (const hint of params.hints) { + const attrs = [`rel="${hint.mode}"`, `href="${deployUrl}${hint.url}"`]; + + if (hint.mode !== 'modulepreload' && crossOrigin !== 'none') { + // Value is considered anonymous by the browser when not present or empty + attrs.push(crossOrigin === 'anonymous' ? 'crossorigin' : `crossorigin="${crossOrigin}"`); + } + + if (hint.mode === 'preload' || hint.mode === 'prefetch') { + switch (extname(hint.url)) { + case '.js': + attrs.push('as="script"'); + break; + case '.css': + attrs.push('as="style"'); + break; + default: + if (hint.as) { + attrs.push(`as="${hint.as}"`); + } + break; + } + } + + linkTags.push(``); + } + } + + const dir = lang ? await getLanguageDirection(lang, warnings) : undefined; + const { rewriter, transformedContent } = await htmlRewritingStream(html); + const baseTagExists = html.includes(' { + switch (tag.tagName) { + case 'html': + // Adjust document locale if specified + if (isString(lang)) { + updateAttribute(tag, 'lang', lang); + } + + if (dir) { + updateAttribute(tag, 'dir', dir); + } + break; + case 'head': + // Base href should be added before any link, meta tags + if (!baseTagExists && isString(baseHref)) { + rewriter.emitStartTag(tag); + rewriter.emitRaw(``); + + return; + } + break; + case 'base': + // Adjust base href if specified + if (isString(baseHref)) { + updateAttribute(tag, 'href', baseHref); + } + break; + } + + rewriter.emitStartTag(tag); + }) + .on('endTag', (tag) => { + switch (tag.tagName) { + case 'head': + for (const linkTag of linkTags) { + rewriter.emitRaw(linkTag); + } + + linkTags = []; + break; + case 'body': + // Add script tags + for (const scriptTag of scriptTags) { + rewriter.emitRaw(scriptTag); + } + + scriptTags = []; + break; + } + + rewriter.emitEndTag(tag); + }); + + const content = await transformedContent(); + + return { + content: + linkTags.length || scriptTags.length + ? // In case no body/head tags are not present (dotnet partial templates) + linkTags.join('') + scriptTags.join('') + content + : content, + warnings, + errors, + }; +} + +function updateAttribute( + tag: { attrs: { name: string; value: string }[] }, + name: string, + value: string, +): void { + const index = tag.attrs.findIndex((a) => a.name === name); + const newValue = { name, value }; + + if (index === -1) { + tag.attrs.push(newValue); + } else { + tag.attrs[index] = newValue; + } +} + +function isString(value: unknown): value is string { + return typeof value === 'string'; +} + +async function getLanguageDirection( + locale: string, + warnings: string[], +): Promise { + const dir = await getLanguageDirectionFromLocales(locale); + + if (!dir) { + warnings.push( + `Locale data for '${locale}' cannot be found. 'dir' attribute will not be set for this locale.`, + ); + } + + return dir; +} + +async function getLanguageDirectionFromLocales(locale: string): Promise { + try { + const localeData = ( + await loadEsmModule( + `@angular/common/locales/${locale}`, + ) + ).default; + + const dir = localeData[localeData.length - 2]; + + return isString(dir) ? dir : undefined; + } catch { + // In some cases certain locales might map to files which are named only with language id. + // Example: `en-US` -> `en`. + const [languageId] = locale.split('-', 1); + if (languageId !== locale) { + return getLanguageDirectionFromLocales(languageId); + } + } + + return undefined; +} diff --git a/angular/devkit/vite/plugins/index-file/html-rewriting-stream.ts b/angular/devkit/vite/plugins/index-file/html-rewriting-stream.ts new file mode 100644 index 00000000..6b1ec1c3 --- /dev/null +++ b/angular/devkit/vite/plugins/index-file/html-rewriting-stream.ts @@ -0,0 +1,57 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import { Readable, Writable } from 'stream'; +import { loadEsmModule } from '@bitdev/angular.dev-services.common'; + +export async function htmlRewritingStream(content: string): Promise<{ + rewriter: import('parse5-html-rewriting-stream').RewritingStream; + transformedContent: () => Promise; +}> { + const { RewritingStream } = await loadEsmModule( + 'parse5-html-rewriting-stream', + ); + const chunks: Buffer[] = []; + const rewriter = new RewritingStream(); + + return { + rewriter, + transformedContent: () => { + return new Promise((resolve) => { + new Readable({ + encoding: 'utf8', + read(): void { + this.push(Buffer.from(content)); + this.push(null); + }, + }) + .pipe(rewriter) + .pipe( + new Writable({ + write( + chunk: string | Buffer, + encoding: string | undefined, + callback: Function, + ): void { + chunks.push( + typeof chunk === 'string' + ? Buffer.from(chunk, encoding as BufferEncoding) + : chunk, + ); + callback(); + }, + final(callback: (error?: Error) => void): void { + callback(); + resolve(Buffer.concat(chunks).toString()); + }, + }), + ); + }); + }, + }; +} diff --git a/angular/devkit/vite/plugins/index-file/index-html-generator.ts b/angular/devkit/vite/plugins/index-file/index-html-generator.ts new file mode 100644 index 00000000..0b4f7f78 --- /dev/null +++ b/angular/devkit/vite/plugins/index-file/index-html-generator.ts @@ -0,0 +1,101 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import * as fs from 'fs'; +import { join } from 'path'; +import { EntryPointsType } from './package-chunk-sort'; +import { stripBom } from './strip-bom'; +import { CrossOriginValue, augmentIndexHtml } from './augment-index-html'; + +type IndexHtmlGeneratorPlugin = ( + html: string, + options: IndexHtmlGeneratorProcessOptions, +) => Promise; + +export type HintMode = 'prefetch' | 'preload' | 'modulepreload' | 'preconnect' | 'dns-prefetch'; + +export interface IndexHtmlGeneratorProcessOptions { + lang: string | undefined; + baseHref: string | undefined; + hints?: { url: string; mode: HintMode; as?: string }[]; +} + +export interface IndexHtmlGeneratorOptions { + rootPath: string; + indexPath: string; + deployUrl?: string; + entrypoints: EntryPointsType[]; + crossOrigin?: CrossOriginValue; +} + +export interface IndexHtmlTransformResult { + content: string; + warnings: string[]; + errors: string[]; +} + +export class IndexHtmlGenerator { + private readonly plugins: IndexHtmlGeneratorPlugin[]; + + constructor(readonly options: IndexHtmlGeneratorOptions) { + this.plugins = [ + augmentIndexHtmlPlugin(this) + ]; + } + + async process(options: IndexHtmlGeneratorProcessOptions): Promise { + let content = stripBom(await this.readIndex(join(this.options.rootPath, this.options.indexPath))); + const warnings: string[] = []; + const errors: string[] = []; + + for (const plugin of this.plugins) { + const result = await plugin(content, options); + if (typeof result === 'string') { + content = result; + } else { + content = result.content; + + if (result.warnings.length) { + warnings.push(...result.warnings); + } + + if (result.errors.length) { + errors.push(...result.errors); + } + } + } + + return { + content, + warnings, + errors, + }; + } + + protected async readIndex(path: string): Promise { + return fs.promises.readFile(path, 'utf-8'); + } +} + +function augmentIndexHtmlPlugin(generator: IndexHtmlGenerator): IndexHtmlGeneratorPlugin { + const { deployUrl, crossOrigin, entrypoints } = generator.options; + + return async (html, options) => { + const { lang, baseHref, hints } = options; + + return augmentIndexHtml({ + html, + baseHref, + deployUrl, + crossOrigin, + lang, + entrypoints, + hints, + }); + }; +} diff --git a/angular/devkit/vite/plugins/index-file/package-chunk-sort.ts b/angular/devkit/vite/plugins/index-file/package-chunk-sort.ts new file mode 100644 index 00000000..4ac6a1ae --- /dev/null +++ b/angular/devkit/vite/plugins/index-file/package-chunk-sort.ts @@ -0,0 +1,89 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import path from 'path'; +// import { ScriptElement, StyleElement } from '@angular-devkit/build-angular/src/builders/browser/schema'; +import type { BrowserBuilderOptions } from '@angular-devkit/build-angular'; + +export type EntryPointsType = [name: string, isModule: boolean, inject: boolean]; + +type Flatten = T extends any[] ? T[number] : T; +export type ScriptElement = NonNullable>; +export type StyleElement = NonNullable>; + +export type NormalizedEntryPoint = Required>; + +export function normalizeExtraEntryPoints( + extraEntryPoints: (ScriptElement | StyleElement)[], + defaultBundleName: string, +): NormalizedEntryPoint[] { + return extraEntryPoints.map((entry) => { + if(!entry) { + throw new Error('Empty entry point found.'); + } + + if (typeof entry === 'string') { + return { input: entry || '', inject: true, bundleName: defaultBundleName }; + } + + const { inject = true, ...newEntry } = entry; + let bundleName; + if (entry.bundleName) { + bundleName = entry.bundleName; + } else if (!inject) { + // Lazy entry points use the file name as bundle name. + bundleName = path.parse(entry.input).name; + } else { + bundleName = defaultBundleName; + } + + return { ...newEntry, inject, bundleName }; + }); +} + +export function generateEntryPoints(options: { + main: string; + polyfills?: string[] | string; + styles: StyleElement[]; + scripts: ScriptElement[]; + isHMREnabled?: boolean; +}): EntryPointsType[] { + // Add all styles/scripts, except lazy-loaded ones. + const extraEntryPoints = ( + extraEntryPoints: (ScriptElement | StyleElement)[], + defaultBundleName: string, + ) => { + const entryPoints = normalizeExtraEntryPoints(extraEntryPoints, defaultBundleName) + .filter((entry) => entry.inject) + .map((entry) => entry.input); + + // remove duplicates + return [...new Set(entryPoints)].map((f) => [f, defaultBundleName === 'scripts', defaultBundleName === 'scripts']); + }; + + let polyfills: EntryPointsType[] = []; + if(options.polyfills) { + polyfills = Array.isArray(options.polyfills) ? options.polyfills.map((p: string) => [p, true, false]) : [[options.polyfills, true, false]]; + } + const entryPoints: EntryPointsType[] = [ + ...polyfills, + ...extraEntryPoints(options.styles, 'styles'), + ...extraEntryPoints(options.scripts, 'scripts'), + [options.main, true, false], + ]; + + const duplicates = entryPoints.filter( + ([name]) => entryPoints[0].indexOf(name) !== entryPoints[0].lastIndexOf(name), + ); + + if (duplicates.length > 0) { + throw new Error(`Multiple bundles have been named the same: '${duplicates.join(`', '`)}'.`); + } + + return entryPoints; +} diff --git a/angular/devkit/vite/plugins/index-file/strip-bom.ts b/angular/devkit/vite/plugins/index-file/strip-bom.ts new file mode 100644 index 00000000..7cc13e47 --- /dev/null +++ b/angular/devkit/vite/plugins/index-file/strip-bom.ts @@ -0,0 +1,15 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +// TODO: cleanup this file, it's copied as is from Angular CLI. + +// Strip BOM from file data. +// https://stackoverflow.com/questions/24356713 +export function stripBom(data: string) { + return data.replace(/^\uFEFF/, ''); +} diff --git a/angular/devkit/vite/plugins/index-html.plugin.ts b/angular/devkit/vite/plugins/index-html.plugin.ts new file mode 100644 index 00000000..7479e4a1 --- /dev/null +++ b/angular/devkit/vite/plugins/index-html.plugin.ts @@ -0,0 +1,73 @@ +import type { BrowserBuilderOptions } from '@angular-devkit/build-angular'; +import type { ServerResponse } from 'http'; +import { default as memoize } from 'memoizee'; +import { join } from 'path'; +import type { Connect, Plugin, ViteDevServer } from 'vite'; +import { IndexHtmlGenerator } from './index-file/index-html-generator'; +import { generateEntryPoints } from './index-file/package-chunk-sort'; + +export function getIndexInputFile(index: BrowserBuilderOptions['index']): string { + if (typeof index === 'string') { + return index; + } else { + return index.input; + } +} + +const cleanUrl = (url: string) => url.replace(/#.*$/s, '').replace(/\?.*$/s, ''); + +const genHtml = async function(options: Partial, rootPath: string, sourceRoot = 'src') { + const entrypoints = generateEntryPoints({ + main: options.main ?? `./${join(sourceRoot, `main.ts`)}`, + polyfills: options.polyfills ?? `./${join(sourceRoot, `polyfills.ts`)}`, + scripts: options.scripts ?? [], + styles: options.styles ?? [] + }); + + const indexHtmlGenerator = new IndexHtmlGenerator({ + rootPath, + indexPath: getIndexInputFile(options.index ?? `./${join(sourceRoot, `index.html`)}`), + entrypoints, + crossOrigin: options.crossOrigin + }); + + const { content, warnings, errors } = await indexHtmlGenerator.process({ + baseHref: options.baseHref ?? './', + // i18nLocale is used when Ivy is disabled + lang: undefined + }); + + if (warnings.length) { + warnings.forEach((warning) => console.warn(warning)); + } + + if (errors.length) { + throw new Error(`Index html generation failed: ${errors.join(', ')}`); + } + + return content; +}; + +const memoized = memoize(genHtml); + +export const htmlPlugin = (options: Partial, rootPath: string, sourceRoot = 'src'): Plugin => { + return { + name: 'html-plugin', + configureServer(server: ViteDevServer) { + return (): void => { + server.middlewares.use(async(req: Connect.IncomingMessage, res: ServerResponse, next: Connect.NextFunction) => { + const url = req.url && cleanUrl(req.url); + if (url?.endsWith('.html')) { + res.statusCode = 200; + res.setHeader('Content-Type', 'text/html'); + let html = await memoized(options, rootPath, sourceRoot); + html = await server.transformIndexHtml(options.index as string ?? `./${join(sourceRoot, `index.html`)}`, html, req.originalUrl); + res.end(html); + return; + } + next(); + }); + }; + } + }; +}; diff --git a/angular/devkit/webpack/ng-webpack-bundler.ts b/angular/devkit/webpack/ng-webpack-bundler.ts index e444b75a..1839fd2b 100644 --- a/angular/devkit/webpack/ng-webpack-bundler.ts +++ b/angular/devkit/webpack/ng-webpack-bundler.ts @@ -1,9 +1,13 @@ import { AngularEnvOptions, BrowserOptions, + BundlerSetup, DevServerOptions, getNodeModulesPaths, - getWorkspace + getPreviewRootPath, + getWorkspace, + isAppBuildContext, + writeTsconfig } from '@bitdev/angular.dev-services.common'; import { AppBuildContext, ApplicationAspect, ApplicationMain } from '@teambit/application'; import { BundlerContext } from '@teambit/bundler'; @@ -24,13 +28,7 @@ import { import { generateTransformers, runTransformers } from '@teambit/webpack.webpack-bundler'; import { join } from 'path'; import { Configuration, WebpackPluginInstance } from 'webpack'; -import { - getPreviewRootPath, - isAppBuildContext, - WebpackConfigFactoryOpts, - WebpackSetup, - writeTsconfig -} from './utils'; +import { WebpackConfigFactoryOpts } from './utils'; import { StatsLoggerPlugin } from './webpack-plugins/stats-logger'; export type NgWebpackBundlerOptions = { @@ -90,10 +88,11 @@ export class NgWebpackBundler { tempFolder = join(CACHE_ROOT, idName); } - let appRootPath: string; let tsconfigPath: string; + let appRootPath: string; + let tsconfigPath: string; let plugins: WebpackPluginInstance[] = []; if (isAppBuildContext(bundlerContext)) { // When you use `bit run ` - appRootPath = bundlerContext.capsule.path;// this.workspace?.componentDir(context.appComponent.id, {ignoreScopeAndVersion: true, ignoreVersion: true}) || ''; + appRootPath = bundlerContext.capsule.path;// this.workspace?.componentDir(context.appComponent.id, {ignoreVersion: true}) || ''; tsconfigPath = join(appRootPath, 'tsconfig.app.json'); plugins = [new StatsLoggerPlugin()]; } else { // When you use `bit build` @@ -121,7 +120,7 @@ export class NgWebpackBundler { nodeModulesPaths: getNodeModulesPaths(false, isolator, workspace), plugins, rootPath: appRootPath, - setup: WebpackSetup.Build, + setup: BundlerSetup.Build, sourceRoot: options.sourceRoot ?? 'src', tempFolder, tsConfigPath: tsconfigPath, diff --git a/angular/devkit/webpack/ng-webpack-dev-server.ts b/angular/devkit/webpack/ng-webpack-dev-server.ts index f6a35582..6a576409 100644 --- a/angular/devkit/webpack/ng-webpack-dev-server.ts +++ b/angular/devkit/webpack/ng-webpack-dev-server.ts @@ -1,9 +1,13 @@ import { AngularEnvOptions, BrowserOptions, + BundlerSetup, DevServerOptions, getNodeModulesPaths, - getWorkspace + getPreviewRootPath, + getWorkspace, + isAppDevContext, + writeTsconfig } from '@bitdev/angular.dev-services.common'; import { ApplicationAspect, ApplicationMain } from '@teambit/application'; import { DevServer, DevServerContext } from '@teambit/bundler'; @@ -25,16 +29,10 @@ import { import { generateTransformers, runTransformers } from '@teambit/webpack.webpack-bundler'; import { join, posix } from 'path'; import { Configuration } from 'webpack'; -import { - getPreviewRootPath, - isAppContext, - WebpackConfigFactoryOpts, - WebpackSetup, - writeTsconfig -} from './utils'; +import { WebpackConfigFactoryOpts } from './utils'; export type WebpackDevServerOptions = { - angularOptions?: Partial; + angularOptions: Partial; /** * context of the dev server execution. @@ -93,11 +91,11 @@ export class NgWebpackDevServer { tempFolder = join(CACHE_ROOT, idName); } - let appRootPath: string; let tsconfigPath: string; + let appRootPath: string; + let tsconfigPath: string; let isApp = false; - if (isAppContext(devServerContext)) { // When you use `bit run ` + if (isAppDevContext(devServerContext)) { // When you use `bit run ` appRootPath = workspace?.componentDir(devServerContext.appComponent.id, { - ignoreScopeAndVersion: true, ignoreVersion: true }) || ''; tsconfigPath = options?.angularOptions?.tsConfig ?? posix.join(appRootPath, 'tsconfig.app.json'); @@ -122,7 +120,7 @@ export class NgWebpackDevServer { publicRoot: devServerContext.rootPath, pubsub: webpackMain.pubsub, rootPath: appRootPath, - setup: WebpackSetup.Serve, + setup: BundlerSetup.Serve, sourceRoot: options.sourceRoot ?? 'src', tempFolder, tsConfigPath: tsconfigPath, diff --git a/angular/devkit/webpack/utils.ts b/angular/devkit/webpack/utils.ts index 352a89d5..040b8b44 100644 --- a/angular/devkit/webpack/utils.ts +++ b/angular/devkit/webpack/utils.ts @@ -1,39 +1,19 @@ -import { componentIsApp } from '@bitdev/angular.app-types.angular-app-type'; -import { AppBuildContext, AppContext, ApplicationMain } from '@teambit/application'; +import { BundlerSetup } from '@bitdev/angular.dev-services.common'; import { BundlerContext, DevServerContext } from '@teambit/bundler'; -import { Component, ComponentID } from '@teambit/component'; -import { DevFilesMain } from '@teambit/dev-files'; -import { pathNormalizeToLinux } from '@teambit/legacy/dist/utils'; import { Logger } from '@teambit/logger'; -import { PkgMain } from '@teambit/pkg'; -import { TesterAspect } from '@teambit/tester'; import { WebpackConfigWithDevServer } from '@teambit/webpack'; -import { Workspace } from '@teambit/workspace'; -import { existsSync, mkdirSync, writeFileSync } from 'fs-extra'; -import objectHash from 'object-hash'; -import { join, posix, resolve } from 'path'; -import { readConfigFile, sys } from 'typescript'; import { Configuration, WebpackPluginInstance } from 'webpack'; -// Make sure bit recognizes the dependencies -import "@bitdev/angular.dev-services.preview.preview"; export type WebpackConfig = Configuration; -export type WebpackPlugin = WebpackPluginInstance; - -export enum WebpackSetup { - Serve = 'serve', - Build = 'build', -} - export interface WebpackConfigFactoryOpts { tempFolder: string; context: DevServerContext | BundlerContext; tsConfigPath: string; rootPath: string; logger: Logger; - setup: WebpackSetup; + setup: BundlerSetup; webpackOptions: Partial; angularOptions: any; sourceRoot?: string; @@ -43,130 +23,3 @@ export interface WebpackConfigFactoryOpts { plugins: WebpackPluginInstance[]; useNgcc: boolean; } - -const writeHash = new Map(); -const timestamp = Date.now(); - -export function getPreviewRootPath(workspace?: Workspace): string { - const defaultPath = () => resolve(require.resolve('@bitdev/angular.dev-services.preview.preview'), '../../preview-app/'); - if(!workspace) return defaultPath(); - try { - const rootPath = workspace?.componentDir(ComponentID.fromString('bitdev.angular/dev-services/preview/preview'), { - ignoreScopeAndVersion: true, - ignoreVersion: true - }, { relative: false }) || ''; - return join(rootPath, 'preview-app'); - } catch (e) { - return defaultPath(); - } -} - -export function isBuildContext(context: DevServerContext | BundlerContext): context is BundlerContext { - return (context as BundlerContext).capsuleNetwork !== undefined; -} - -export function isAppContext(context: DevServerContext | AppContext): context is DevServerContext & AppContext { - return (context as any).appName !== undefined; -} - -export function isAppBuildContext( - context: BundlerContext | AppBuildContext -): context is BundlerContext & AppBuildContext { - return (context as any).appName !== undefined; -} - -/** - * Add the list of files to include into the typescript compilation as absolute paths - */ -export function generateTsConfig( - appPath: string, - includePaths: string[], - excludePaths: string[] = [], - tsPaths: { [key: string]: string[] } -): string { - const tsconfigPath = join(appPath, 'tsconfig.app.json'); - const tsconfigJSON = readConfigFile(tsconfigPath, sys.readFile).config; - const pAppPath = pathNormalizeToLinux(appPath); - - // tsconfigJSON.config.angularCompilerOptions.enableIvy = this.enableIvy; - tsconfigJSON.files = tsconfigJSON.files.map((file: string) => posix.join(pAppPath, file)); - tsconfigJSON.include = [ - ...tsconfigJSON.include.map((file: string) => posix.join(pAppPath, file)), - ...includePaths.map((path) => posix.join(path, '**/*.ts')) - ]; - tsconfigJSON.exclude = [ - ...tsconfigJSON.exclude.map((file: string) => posix.join(pAppPath, file)), - ...excludePaths, - ]; - tsconfigJSON.compilerOptions.paths = tsPaths; - - return JSON.stringify(tsconfigJSON, undefined, 2); -} - -/** - * write a link to load custom modules dynamically. - */ -export function writeTsconfig( - context: DevServerContext | BundlerContext, - rootSpace: string, - tempFolder: string, - application: ApplicationMain, - pkg: PkgMain, - devFilesMain: DevFilesMain, - workspace?: Workspace -): string { - const tsPaths: { [key: string]: string[] } = {}; - const includePaths = new Set(); - const excludePaths = new Set(); - const dirPath = join(tempFolder, context.id); - if (!existsSync(dirPath)) { - mkdirSync(dirPath, { recursive: true }); - } - - // get the list of files for existing component compositions to include into the compilation - context.components.forEach((component: Component) => { - let outputPath: string; - - const isApp = componentIsApp(component, application); - if (isApp) { - return; - } - if (isBuildContext(context)) { - const capsules = context.capsuleNetwork.graphCapsules; - const capsule = capsules.getCapsule(component.id); - if (!capsule) { - throw new Error(`No capsule found for ${component.id} in network graph`); - } - outputPath = pathNormalizeToLinux(capsule.path); - } else { - outputPath = pathNormalizeToLinux(workspace?.componentDir(component.id, { - ignoreScopeAndVersion: true, - ignoreVersion: true - }) || ''); - } - // map the package names to the workspace component paths for typescript in case a package references another local package - const pkgName = pkg.getPackageName(component); - tsPaths[pkgName] = [`${outputPath}/public-api.ts`]; - tsPaths[`${pkgName}/*`] = [`${outputPath}/*`]; - - includePaths.add(outputPath); - - // get the list of spec patterns - const devPatterns: string[] = devFilesMain.getDevPatterns(component, TesterAspect.id); - devPatterns.forEach(specPattern => { - excludePaths.add(posix.join(outputPath, specPattern)); - }); - }); - - const content = generateTsConfig(rootSpace, Array.from(includePaths), Array.from(excludePaths), tsPaths); - const hash = objectHash(content); - const targetPath = join(dirPath, `__tsconfig-${timestamp}.json`); - - // write only if the link has changed (prevents triggering fs watches) - if (writeHash.get(targetPath) !== hash) { - writeFileSync(targetPath, content); - writeHash.set(targetPath, hash); - } - - return pathNormalizeToLinux(targetPath); -} diff --git a/angular/devkit/webpack/webpack-plugins/stats-logger.ts b/angular/devkit/webpack/webpack-plugins/stats-logger.ts index f5c7f5de..e09e09da 100644 --- a/angular/devkit/webpack/webpack-plugins/stats-logger.ts +++ b/angular/devkit/webpack/webpack-plugins/stats-logger.ts @@ -11,11 +11,22 @@ export class StatsLoggerPlugin { }; try { // "Executed when the compilation has completed." - const { createWebpackLoggingCallback } = await loadEsmModule('@angular-devkit/build-angular/src/webpack/utils/stats') as any; - const loggingCallback = createWebpackLoggingCallback({} as any, logger as any) as any; - compiler.hooks.done.tap(PLUGIN_NAME, (stats: Stats) => { - loggingCallback(stats, { stats: { logging: 'info', colors: true } }); - }); + try { + const { createWebpackLoggingCallback } = await loadEsmModule('@angular-devkit/build-angular/src/webpack/utils/stats') as any; + const loggingCallback = createWebpackLoggingCallback({} as any, logger as any) as any; + compiler.hooks.done.tap(PLUGIN_NAME, (stats: Stats) => { + loggingCallback(stats, { stats: { logging: 'info', colors: true } }); + }); + } catch (e) { + // angular v16+ + try { + const { createWebpackLoggingCallback } = await loadEsmModule('@angular-devkit/build-angular/src/tools/webpack/utils/stats') as any; + const loggingCallback = createWebpackLoggingCallback({} as any, logger as any) as any; + compiler.hooks.done.tap(PLUGIN_NAME, (stats: Stats) => { + loggingCallback(stats, { stats: { logging: 'info', colors: true } }); + }); + } catch (e) {} + } } catch (e) { // if it fails, just continue (we don't need logging to break the build) } diff --git a/angular/envs/angular-env/component.json b/angular/envs/angular-env/component.json index e5e90aa3..750c88f4 100644 --- a/angular/envs/angular-env/component.json +++ b/angular/envs/angular-env/component.json @@ -8,55 +8,55 @@ "teambit.dependencies/dependency-resolver": { "policy": { "dependencies": { - "@angular-devkit/architect": "0.1600.0", - "@angular-devkit/build-angular": "~16.0.0", - "@angular-devkit/build-webpack": "0.1600.0", - "@angular-devkit/core": "~16.0.0", - "@angular-devkit/schematics": "~16.0.0", - "@angular-eslint/eslint-plugin": "~16.0.1", - "@angular-eslint/eslint-plugin-template": "~16.0.1", - "@angular-eslint/template-parser": "~16.0.1", - "@angular/animations": "~16.0.0", - "@angular/common": "~16.0.0", - "@angular/compiler": "~16.0.0", - "@angular/compiler-cli": "~16.0.0", - "@angular/core": "~16.0.0", - "@angular/elements": "~16.0.0", - "@angular/platform-browser": "~16.0.0", - "@angular/platform-browser-dynamic": "~16.0.0", - "@ngtools/webpack": "~16.0.0", + "@angular-devkit/architect": "0.1602.0", + "@angular-devkit/build-angular": "~16.2.0", + "@angular-devkit/build-webpack": "0.1602.0", + "@angular-devkit/core": "~16.2.0", + "@angular-devkit/schematics": "~16.2.0", + "@angular-eslint/eslint-plugin": "~16.1.1", + "@angular-eslint/eslint-plugin-template": "~16.1.1", + "@angular-eslint/template-parser": "~16.1.1", + "@angular/animations": "~16.2.0", + "@angular/common": "~16.2.0", + "@angular/compiler": "~16.2.0", + "@angular/compiler-cli": "~16.2.0", + "@angular/core": "~16.2.0", + "@angular/elements": "~16.2.0", + "@angular/platform-browser": "~16.2.0", + "@angular/platform-browser-dynamic": "~16.2.0", + "@ngtools/webpack": "~16.2.0", + "@types/eslint": "^8.40.0", "@types/jest": "^29.5.0", "@types/react-dev-utils": "~9.0.8", "@types/remark-prism": "~1.3.0", "@typescript-eslint/eslint-plugin": "^5.59.2", "@typescript-eslint/parser": "^5.59.2", "css-loader": "6.7.3", - "eslint": "^8.39.0", + "eslint": "^8.40.0", "events": "^3.2.0", "html-loader": "~2.1.2", "jest": "^29.5.0", + "jest-environment-jsdom": "^29.0.3", + "jest-environment-node": "^29.0.3", "jest-preset-angular": "~13.1.0", - "ng-packagr": "~16.0.0-rc.1", + "ng-packagr": "~16.2.0", "postcss": "8.4.23", "postcss-flexbugs-fixes": "5.0.2", "postcss-loader": "7.2.4", "postcss-preset-env": "7.8.2", "remark": "~13.0.0", - "remark-frontmatter": "~3.0.0", - "remark-html": "13.0.1", "remark-loader": "~4.0.0", - "remark-prism": "~1.3.6", "resolve-url-loader": "5.0.0", - "sass": "1.62.1", - "sass-loader": "13.2.2", + "sass": "1.65.1", + "sass-loader": "13.3.2", "style-loader": "^2.0.0", - "ts-node": "^10.8.1", - "tslib": "^2.5.0", - "typescript": "~5.0.2", + "ts-node": "^10.9.1", + "tslib": "^2.6.1", + "typescript": "5.1.6", "webpack": "5.81.0", "webpack-dev-middleware": "6.0.2", "webpack-dev-server": "4.13.3", - "zone.js": "0.13.1" + "zone.js": "~0.13.0" }, "peerDependencies": { "rxjs": "^6.5.5 || ^7.4.0" diff --git a/angular/envs/angular-env/env.jsonc b/angular/envs/angular-env/env.jsonc index aeafe6d7..c357a9ed 100644 --- a/angular/envs/angular-env/env.jsonc +++ b/angular/envs/angular-env/env.jsonc @@ -8,7 +8,7 @@ "runtime": [ { "name": "tslib", - "version": "^2.4.1", + "version": "^2.6.1", "supportedRange": "^2.3.0" } ], @@ -42,33 +42,33 @@ "peers": [ { "name": "@angular/common", - "version": "~16.0.0", - "supportedRange": "^16.0.0" + "version": "~16.2.0", + "supportedRange": "^16.2.0" }, { "name": "@angular/compiler", - "version": "~16.0.0", - "supportedRange": "^16.0.0", + "version": "~16.2.0", + "supportedRange": "^16.2.0", }, { "name": "@angular/compiler-cli", - "version": "~16.0.0", - "supportedRange": "^16.0.0", + "version": "~16.2.0", + "supportedRange": "^16.2.0", }, { "name": "@angular/core", - "version": "~16.0.0", - "supportedRange": "^16.0.0" + "version": "~16.2.0", + "supportedRange": "^16.2.0" }, { "name": "@angular/platform-browser", - "version": "~16.0.0", - "supportedRange": "^16.0.0" + "version": "~16.2.0", + "supportedRange": "^16.2.0" }, { "name": "@angular/platform-browser-dynamic", - "version": "~16.0.0", - "supportedRange": "^16.0.0" + "version": "~16.2.0", + "supportedRange": "^16.2.0" }, { "name": "jest", @@ -82,13 +82,13 @@ }, { "name": "tslib", - "version": "^2.4.1", + "version": "^2.6.1", "supportedRange": "^2.3.0" }, { "name": "typescript", - "version": "~5.0.2", - "supportedRange": ">=4.9.3 <5.1" + "version": "~5.1.6", + "supportedRange": ">=4.9.3 <5.2" }, { "name": "zone.js", diff --git a/angular/envs/angular-v12-env/webpack-config.factory.ts b/angular/envs/angular-v12-env/webpack-config.factory.ts index 969f55ab..0c7f0643 100644 --- a/angular/envs/angular-v12-env/webpack-config.factory.ts +++ b/angular/envs/angular-v12-env/webpack-config.factory.ts @@ -24,12 +24,12 @@ import { IndexHtmlWebpackPlugin } from '@angular-devkit/build-angular/src/webpack/plugins/index-html-webpack-plugin'; import { getSystemPath, logging, normalize, tags } from '@angular-devkit/core'; +import { BundlerSetup } from '@bitdev/angular.dev-services.common'; import { WebpackBuildConfigFactoryOpts, WebpackConfig, WebpackConfigFactoryOpts, - WebpackServeConfigFactoryOpts, - WebpackSetup + WebpackServeConfigFactoryOpts } from '@bitdev/angular.dev-services.webpack'; import { BundlerContext, DevServerContext } from '@teambit/bundler'; import { Logger } from '@teambit/logger'; @@ -100,7 +100,7 @@ async function getWebpackConfig( tsconfigPath: string, workspaceRoot: string, logger: Logger, - setup: WebpackSetup, + setup: BundlerSetup, webpackOptions: Partial = {}, angularOptions: Partial = {}, sourceRoot = 'src' @@ -120,13 +120,13 @@ async function getWebpackConfig( scripts: angularOptions.scripts, vendorChunk: angularOptions.vendorChunk ?? true, namedChunks: angularOptions.namedChunks ?? true, - optimization: angularOptions.optimization ?? setup === WebpackSetup.Build, - buildOptimizer: angularOptions.buildOptimizer ?? setup === WebpackSetup.Build, + optimization: angularOptions.optimization ?? setup === BundlerSetup.Build, + buildOptimizer: angularOptions.buildOptimizer ?? setup === BundlerSetup.Build, aot: angularOptions.aot ?? true, deleteOutputPath: angularOptions.deleteOutputPath ?? true, sourceMap: angularOptions.sourceMap ?? true, - outputHashing: angularOptions.outputHashing ?? (setup === WebpackSetup.Build ? OutputHashing.All : OutputHashing.None), - watch: setup === WebpackSetup.Serve, + outputHashing: angularOptions.outputHashing ?? (setup === BundlerSetup.Build ? OutputHashing.All : OutputHashing.None), + watch: setup === BundlerSetup.Serve, allowedCommonJsDependencies: ['dompurify', '@teambit/harmony', 'graphql', '@teambit/documenter.ng.content.copy-box', ...(angularOptions.allowedCommonJsDependencies || [])], }; const normalizedWorkspaceRoot = normalize(workspaceRoot); @@ -151,7 +151,7 @@ async function getWebpackConfig( getSystemPath(normalizedSourceRoot), normalizedOptions, (wco: BrowserWebpackConfigOptions) => [ - setup === WebpackSetup.Serve ? getDevServerConfig(wco) : {}, + setup === BundlerSetup.Serve ? getDevServerConfig(wco) : {}, getCommonConfig(wco), getBrowserConfig(wco), getStylesConfig(wco), @@ -171,7 +171,7 @@ async function getWebpackConfig( // Add bit generated files to the list of entries webpackConfig.entry.main.unshift(...entryFiles); - // if (setup === WebpackSetup.Serve && browserOptions.index) { + // if (setup === BundlerSetup.Serve && browserOptions.index) { const { scripts = [], styles = [] } = browserOptions; // const { options: compilerOptions } = readTsconfig(browserOptions.tsConfig, workspaceRoot); // const target = compilerOptions.target || ts.ScriptTarget.ES5; @@ -207,7 +207,7 @@ async function getWebpackConfig( delete webpackConfig?.resolve?.modules; webpackConfig.stats = 'errors-only'; - if (setup === WebpackSetup.Serve) { + if (setup === BundlerSetup.Serve) { return migrateConfiguration(webpackConfig) as any; } @@ -228,7 +228,7 @@ export async function webpackConfigFactory(opts: WebpackConfigFactoryOpts & Webp ) as WebpackConfigWithDevServer; let overwriteConfig: WebpackConfigWithDevServer; - if (opts.setup === WebpackSetup.Serve) { + if (opts.setup === BundlerSetup.Serve) { overwriteConfig = webpack5ServeConfigFactory( opts.devServerID, opts.workspaceDir, diff --git a/angular/envs/angular-v13-env/webpack-config.factory.ts b/angular/envs/angular-v13-env/webpack-config.factory.ts index 0f1b4014..4bee7859 100644 --- a/angular/envs/angular-v13-env/webpack-config.factory.ts +++ b/angular/envs/angular-v13-env/webpack-config.factory.ts @@ -21,12 +21,12 @@ import { IndexHtmlWebpackPlugin } from '@angular-devkit/build-angular/src/webpack/plugins/index-html-webpack-plugin'; import { getSystemPath, logging, normalize, tags } from '@angular-devkit/core'; +import { BundlerSetup } from '@bitdev/angular.dev-services.common'; import { WebpackBuildConfigFactoryOpts, WebpackConfig, WebpackConfigFactoryOpts, - WebpackServeConfigFactoryOpts, - WebpackSetup + WebpackServeConfigFactoryOpts } from '@bitdev/angular.dev-services.webpack'; import { BundlerContext, DevServerContext } from '@teambit/bundler'; import { Logger } from '@teambit/logger'; @@ -101,7 +101,7 @@ async function getWebpackConfig( tsconfigPath: string, workspaceRoot: string, logger: Logger, - setup: WebpackSetup, + setup: BundlerSetup, webpackOptions: Partial = {}, angularOptions: Partial = {}, sourceRoot = 'src' @@ -121,13 +121,13 @@ async function getWebpackConfig( scripts: angularOptions.scripts, vendorChunk: angularOptions.vendorChunk ?? true, namedChunks: angularOptions.namedChunks ?? true, - optimization: angularOptions.optimization ?? setup === WebpackSetup.Build, - buildOptimizer: angularOptions.buildOptimizer ?? setup === WebpackSetup.Build, + optimization: angularOptions.optimization ?? setup === BundlerSetup.Build, + buildOptimizer: angularOptions.buildOptimizer ?? setup === BundlerSetup.Build, aot: angularOptions.aot ?? true, deleteOutputPath: angularOptions.deleteOutputPath ?? true, sourceMap: angularOptions.sourceMap ?? true, - outputHashing: angularOptions.outputHashing ?? (setup === WebpackSetup.Build ? OutputHashing.All : OutputHashing.None), - watch: setup === WebpackSetup.Serve, + outputHashing: angularOptions.outputHashing ?? (setup === BundlerSetup.Build ? OutputHashing.All : OutputHashing.None), + watch: setup === BundlerSetup.Serve, allowedCommonJsDependencies: ['dompurify', '@teambit/harmony', 'graphql', '@teambit/documenter.ng.content.copy-box', ...(angularOptions.allowedCommonJsDependencies || [])], }; const normalizedWorkspaceRoot = normalize(workspaceRoot); @@ -167,7 +167,7 @@ async function getWebpackConfig( 'bit-angular-v13-env', // projectName normalizedOptions, (wco: BrowserWebpackConfigOptions) => [ - setup === WebpackSetup.Serve ? getDevServerConfig(wco) : {}, + setup === BundlerSetup.Serve ? getDevServerConfig(wco) : {}, getCommonConfig(wco), getStylesConfig(wco), ], @@ -215,7 +215,7 @@ async function getWebpackConfig( // uniqueName should not be an empty string webpackConfig.output.uniqueName = 'angular-v13-env'; - if (setup === WebpackSetup.Serve) { + if (setup === BundlerSetup.Serve) { webpackConfig = migrateConfiguration(webpackConfig); } @@ -236,7 +236,7 @@ export async function webpackConfigFactory(opts: WebpackConfigFactoryOpts & Webp ) as WebpackConfigWithDevServer; let overwriteConfig: WebpackConfigWithDevServer; - if (opts.setup === WebpackSetup.Serve) { + if (opts.setup === BundlerSetup.Serve) { overwriteConfig = webpack5ServeConfigFactory( opts.devServerID, opts.workspaceDir, diff --git a/angular/envs/angular-v14-env/webpack-config.factory.ts b/angular/envs/angular-v14-env/webpack-config.factory.ts index 34d7b4b6..3b6a2c3a 100644 --- a/angular/envs/angular-v14-env/webpack-config.factory.ts +++ b/angular/envs/angular-v14-env/webpack-config.factory.ts @@ -21,12 +21,12 @@ import { IndexHtmlWebpackPlugin } from '@angular-devkit/build-angular/src/webpack/plugins/index-html-webpack-plugin'; import { getSystemPath, logging, normalize, tags } from '@angular-devkit/core'; +import { BundlerSetup } from '@bitdev/angular.dev-services.common'; import { WebpackBuildConfigFactoryOpts, WebpackConfig, WebpackConfigFactoryOpts, - WebpackServeConfigFactoryOpts, - WebpackSetup + WebpackServeConfigFactoryOpts } from '@bitdev/angular.dev-services.webpack'; import { BundlerContext, DevServerContext } from '@teambit/bundler'; import { Logger } from '@teambit/logger'; @@ -101,7 +101,7 @@ async function getWebpackConfig( tsconfigPath: string, workspaceRoot: string, logger: Logger, - setup: WebpackSetup, + setup: BundlerSetup, webpackOptions: Partial = {}, angularOptions: Partial = {}, sourceRoot = 'src' @@ -121,13 +121,13 @@ async function getWebpackConfig( scripts: angularOptions.scripts, vendorChunk: angularOptions.vendorChunk ?? true, namedChunks: angularOptions.namedChunks ?? true, - optimization: angularOptions.optimization ?? setup === WebpackSetup.Build, - buildOptimizer: angularOptions.buildOptimizer ?? setup === WebpackSetup.Build, + optimization: angularOptions.optimization ?? setup === BundlerSetup.Build, + buildOptimizer: angularOptions.buildOptimizer ?? setup === BundlerSetup.Build, aot: angularOptions.aot ?? true, deleteOutputPath: angularOptions.deleteOutputPath ?? true, sourceMap: angularOptions.sourceMap ?? true, - outputHashing: angularOptions.outputHashing ?? (setup === WebpackSetup.Build ? OutputHashing.All : OutputHashing.None), - watch: setup === WebpackSetup.Serve, + outputHashing: angularOptions.outputHashing ?? (setup === BundlerSetup.Build ? OutputHashing.All : OutputHashing.None), + watch: setup === BundlerSetup.Serve, allowedCommonJsDependencies: ['dompurify', '@teambit/harmony', 'graphql', '@teambit/documenter.ng.content.copy-box', ...(angularOptions.allowedCommonJsDependencies || [])] }; const normalizedWorkspaceRoot = normalize(workspaceRoot); @@ -168,7 +168,7 @@ async function getWebpackConfig( 'bit-angular-v14-env', // projectName normalizedOptions, (wco: BrowserWebpackConfigOptions) => [ - setup === WebpackSetup.Serve ? getDevServerConfig(wco) : {}, + setup === BundlerSetup.Serve ? getDevServerConfig(wco) : {}, getCommonConfig(wco), getStylesConfig(wco) ], @@ -217,7 +217,7 @@ async function getWebpackConfig( // uniqueName should not be an empty string webpackConfig.output.uniqueName = 'angular-v14-env'; - if (setup === WebpackSetup.Serve) { + if (setup === BundlerSetup.Serve) { webpackConfig = migrateConfiguration(webpackConfig); } @@ -238,7 +238,7 @@ export async function webpackConfigFactory(opts: WebpackConfigFactoryOpts & Webp ) as WebpackConfigWithDevServer; let overwriteConfig: WebpackConfigWithDevServer; - if (opts.setup === WebpackSetup.Serve) { + if (opts.setup === BundlerSetup.Serve) { overwriteConfig = webpack5ServeConfigFactory( opts.devServerID, opts.workspaceDir, diff --git a/angular/envs/angular-v15-env/webpack-config.factory.ts b/angular/envs/angular-v15-env/webpack-config.factory.ts index 9247e30f..fd27b695 100644 --- a/angular/envs/angular-v15-env/webpack-config.factory.ts +++ b/angular/envs/angular-v15-env/webpack-config.factory.ts @@ -21,12 +21,12 @@ import { IndexHtmlWebpackPlugin } from '@angular-devkit/build-angular/src/webpack/plugins/index-html-webpack-plugin'; import { getSystemPath, logging, normalize, tags } from '@angular-devkit/core'; +import { BundlerSetup } from '@bitdev/angular.dev-services.common'; import { WebpackBuildConfigFactoryOpts, WebpackConfig, WebpackConfigFactoryOpts, - WebpackServeConfigFactoryOpts, - WebpackSetup + WebpackServeConfigFactoryOpts } from '@bitdev/angular.dev-services.webpack'; import { BundlerContext, DevServerContext } from '@teambit/bundler'; import { Logger } from '@teambit/logger'; @@ -101,7 +101,7 @@ async function getWebpackConfig( tsconfigPath: string, workspaceRoot: string, logger: Logger, - setup: WebpackSetup, + setup: BundlerSetup, webpackOptions: Partial = {}, angularOptions: Partial = {}, sourceRoot = 'src' @@ -121,13 +121,13 @@ async function getWebpackConfig( scripts: angularOptions.scripts, vendorChunk: angularOptions.vendorChunk ?? true, namedChunks: angularOptions.namedChunks ?? true, - optimization: angularOptions.optimization ?? setup === WebpackSetup.Build, - buildOptimizer: angularOptions.buildOptimizer ?? setup === WebpackSetup.Build, + optimization: angularOptions.optimization ?? setup === BundlerSetup.Build, + buildOptimizer: angularOptions.buildOptimizer ?? setup === BundlerSetup.Build, aot: angularOptions.aot ?? true, deleteOutputPath: angularOptions.deleteOutputPath ?? true, sourceMap: angularOptions.sourceMap ?? true, - outputHashing: angularOptions.outputHashing ?? (setup === WebpackSetup.Build ? OutputHashing.All : OutputHashing.None), - watch: setup === WebpackSetup.Serve, + outputHashing: angularOptions.outputHashing ?? (setup === BundlerSetup.Build ? OutputHashing.All : OutputHashing.None), + watch: setup === BundlerSetup.Serve, allowedCommonJsDependencies: ['dompurify', '@teambit/harmony', 'graphql', '@teambit/documenter.ng.content.copy-box', ...(angularOptions.allowedCommonJsDependencies || [])] }; const normalizedWorkspaceRoot = normalize(workspaceRoot); @@ -168,7 +168,7 @@ async function getWebpackConfig( 'bit-angular-v15-env', // projectName normalizedOptions, (wco: BrowserWebpackConfigOptions) => [ - setup === WebpackSetup.Serve ? getDevServerConfig(wco) : {}, + setup === BundlerSetup.Serve ? getDevServerConfig(wco) : {}, getCommonConfig(wco), getStylesConfig(wco) ], @@ -217,7 +217,7 @@ async function getWebpackConfig( // uniqueName should not be an empty string webpackConfig.output.uniqueName = 'angular-v15-env'; - if (setup === WebpackSetup.Serve) { + if (setup === BundlerSetup.Serve) { webpackConfig = migrateConfiguration(webpackConfig); } @@ -238,7 +238,7 @@ export async function webpackConfigFactory(opts: WebpackConfigFactoryOpts & Webp ) as WebpackConfigWithDevServer; let overwriteConfig: WebpackConfigWithDevServer; - if (opts.setup === WebpackSetup.Serve) { + if (opts.setup === BundlerSetup.Serve) { overwriteConfig = webpack5ServeConfigFactory( opts.devServerID, opts.workspaceDir, diff --git a/angular/envs/angular-v16-env/angular-v16-env.bit-env.ts b/angular/envs/angular-v16-env/angular-v16-env.bit-env.ts index c61aa190..1b187515 100644 --- a/angular/envs/angular-v16-env/angular-v16-env.bit-env.ts +++ b/angular/envs/angular-v16-env/angular-v16-env.bit-env.ts @@ -26,7 +26,8 @@ export class AngularV16Env extends AngularBaseEnv { webpackDevServerModulePath: require.resolve('webpack-dev-server'), // resolving to the webpack used by angular devkit to avoid multiple instances of webpack // otherwise, if we use a different version, it would break - webpackModulePath: require.resolve('webpack', { paths: [require.resolve('@angular-devkit/build-angular')] }) + webpackModulePath: require.resolve('webpack', { paths: [require.resolve('@angular-devkit/build-angular')] }), + devServer: 'webpack', }; } diff --git a/angular/envs/angular-v16-env/component.json b/angular/envs/angular-v16-env/component.json index b2683272..789b2ac3 100644 --- a/angular/envs/angular-v16-env/component.json +++ b/angular/envs/angular-v16-env/component.json @@ -8,24 +8,23 @@ "teambit.dependencies/dependency-resolver": { "policy": { "dependencies": { - "@angular-devkit/architect": "0.1600.3", - "@angular-devkit/build-angular": "~16.0.3", - "@angular-devkit/build-webpack": "0.1600.3", - "@angular-devkit/core": "~16.0.3", - "@angular-devkit/schematics": "~16.0.3", - "@angular-eslint/eslint-plugin": "~16.0.3", - "@angular-eslint/eslint-plugin-template": "~16.0.3", - "@angular-eslint/template-parser": "~16.0.3", - "@angular/animations": "~16.0.3", - "@angular/common": "~16.0.3", - "@angular/compiler": "~16.0.3", - "@angular/compiler-cli": "~16.0.3", - "@angular/core": "~16.0.3", - "@angular/elements": "~16.0.3", - "@angular/platform-browser": "~16.0.3", - "@angular/platform-browser-dynamic": "~16.0.3", - "@jest/globals": "^29.3.1", - "@ngtools/webpack": "~16.0.3", + "@angular-devkit/architect": "0.1602.0", + "@angular-devkit/build-angular": "~16.2.0", + "@angular-devkit/build-webpack": "0.1602.0", + "@angular-devkit/core": "~16.2.0", + "@angular-devkit/schematics": "~16.2.0", + "@angular-eslint/eslint-plugin": "~16.1.1", + "@angular-eslint/eslint-plugin-template": "~16.1.1", + "@angular-eslint/template-parser": "~16.1.1", + "@angular/animations": "~16.2.0", + "@angular/common": "~16.2.0", + "@angular/compiler": "~16.2.0", + "@angular/compiler-cli": "~16.2.0", + "@angular/core": "~16.2.0", + "@angular/elements": "~16.2.0", + "@angular/platform-browser": "~16.2.0", + "@angular/platform-browser-dynamic": "~16.2.0", + "@ngtools/webpack": "~16.2.0", "@types/eslint": "^8.40.0", "@types/jest": "^29.5.0", "@types/react-dev-utils": "~9.0.8", @@ -40,7 +39,7 @@ "jest-environment-jsdom": "^29.0.3", "jest-environment-node": "^29.0.3", "jest-preset-angular": "~13.1.0", - "ng-packagr": "~16.0.1", + "ng-packagr": "~16.2.0", "postcss": "8.4.23", "postcss-flexbugs-fixes": "5.0.2", "postcss-loader": "7.2.4", @@ -48,16 +47,16 @@ "remark": "~13.0.0", "remark-loader": "~4.0.0", "resolve-url-loader": "5.0.0", - "sass": "1.62.1", - "sass-loader": "13.2.2", + "sass": "1.65.1", + "sass-loader": "13.3.2", "style-loader": "^2.0.0", - "ts-node": "^10.8.1", - "tslib": "^2.5.0", - "typescript": "~5.0.2", + "ts-node": "^10.9.1", + "tslib": "^2.6.1", + "typescript": "5.1.6", "webpack": "5.81.0", "webpack-dev-middleware": "6.0.2", "webpack-dev-server": "4.13.3", - "zone.js": "0.13.1" + "zone.js": "~0.13.0" }, "peerDependencies": { "rxjs": "^6.5.5 || ^7.4.0" diff --git a/angular/envs/angular-v16-env/env.jsonc b/angular/envs/angular-v16-env/env.jsonc index de4d2cd5..c357a9ed 100644 --- a/angular/envs/angular-v16-env/env.jsonc +++ b/angular/envs/angular-v16-env/env.jsonc @@ -3,12 +3,12 @@ * these components would be resolved once for all components in * in the dependency graph of your component. **/ - { +{ "policy": { "runtime": [ { "name": "tslib", - "version": "^2.4.1", + "version": "^2.6.1", "supportedRange": "^2.3.0" } ], @@ -42,33 +42,33 @@ "peers": [ { "name": "@angular/common", - "version": "~16.0.0", - "supportedRange": "^16.0.0" + "version": "~16.2.0", + "supportedRange": "^16.2.0" }, { "name": "@angular/compiler", - "version": "~16.0.0", - "supportedRange": "^16.0.0", + "version": "~16.2.0", + "supportedRange": "^16.2.0", }, { "name": "@angular/compiler-cli", - "version": "~16.0.0", - "supportedRange": "^16.0.0", + "version": "~16.2.0", + "supportedRange": "^16.2.0", }, { "name": "@angular/core", - "version": "~16.0.0", - "supportedRange": "^16.0.0" + "version": "~16.2.0", + "supportedRange": "^16.2.0" }, { "name": "@angular/platform-browser", - "version": "~16.0.0", - "supportedRange": "^16.0.0" + "version": "~16.2.0", + "supportedRange": "^16.2.0" }, { "name": "@angular/platform-browser-dynamic", - "version": "~16.0.0", - "supportedRange": "^16.0.0" + "version": "~16.2.0", + "supportedRange": "^16.2.0" }, { "name": "jest", @@ -82,13 +82,13 @@ }, { "name": "tslib", - "version": "^2.4.1", + "version": "^2.6.1", "supportedRange": "^2.3.0" }, { "name": "typescript", - "version": "~5.0.2", - "supportedRange": ">=4.9.3 <5.1" + "version": "~5.1.6", + "supportedRange": ">=4.9.3 <5.2" }, { "name": "zone.js", diff --git a/angular/envs/angular-v16-env/webpack-config.factory.ts b/angular/envs/angular-v16-env/webpack-config.factory.ts index 87eb9dca..1e8a6f73 100644 --- a/angular/envs/angular-v16-env/webpack-config.factory.ts +++ b/angular/envs/angular-v16-env/webpack-config.factory.ts @@ -1,6 +1,14 @@ /* eslint-disable no-param-reassign */ import type { BrowserBuilderOptions, DevServerBuilderOptions } from '@angular-devkit/build-angular'; import { OutputHashing } from '@angular-devkit/build-angular'; +import { + getCommonConfig, + getDevServerConfig, + getStylesConfig +} from '@angular-devkit/build-angular/src/tools/webpack/configs'; +import { + IndexHtmlWebpackPlugin +} from '@angular-devkit/build-angular/src/tools/webpack/plugins/index-html-webpack-plugin'; import { normalizeBrowserSchema, normalizeOptimization @@ -12,21 +20,13 @@ import { generateWebpackConfig, getIndexOutputFile } from '@angular-devkit/build-angular/src/utils/webpack-browser-config'; -import { - getCommonConfig, - getDevServerConfig, - getStylesConfig -} from '@angular-devkit/build-angular/src/webpack/configs'; -import { - IndexHtmlWebpackPlugin -} from '@angular-devkit/build-angular/src/webpack/plugins/index-html-webpack-plugin'; import { getSystemPath, logging, normalize, tags } from '@angular-devkit/core'; +import { BundlerSetup } from '@bitdev/angular.dev-services.common'; import { WebpackBuildConfigFactoryOpts, WebpackConfig, WebpackConfigFactoryOpts, - WebpackServeConfigFactoryOpts, - WebpackSetup + WebpackServeConfigFactoryOpts } from '@bitdev/angular.dev-services.webpack'; import { BundlerContext, DevServerContext } from '@teambit/bundler'; import { Logger } from '@teambit/logger'; @@ -37,7 +37,7 @@ import { WebpackConfigWithDevServer } from '@teambit/webpack'; import path, { join } from 'path'; -import { Configuration } from 'webpack'; +import type { Configuration } from 'webpack'; import { webpack5BuildConfigFactory } from './webpack/webpack5.build.config'; import { webpack5ServeConfigFactory } from './webpack/webpack5.serve.config'; @@ -101,7 +101,7 @@ async function getWebpackConfig( tsconfigPath: string, workspaceRoot: string, logger: Logger, - setup: WebpackSetup, + setup: BundlerSetup, webpackOptions: Partial = {}, angularOptions: Partial = {}, sourceRoot = 'src' @@ -121,13 +121,13 @@ async function getWebpackConfig( scripts: angularOptions.scripts, vendorChunk: angularOptions.vendorChunk ?? true, namedChunks: angularOptions.namedChunks ?? true, - optimization: angularOptions.optimization ?? setup === WebpackSetup.Build, - buildOptimizer: angularOptions.buildOptimizer ?? setup === WebpackSetup.Build, + optimization: angularOptions.optimization ?? setup === BundlerSetup.Build, + buildOptimizer: angularOptions.buildOptimizer ?? setup === BundlerSetup.Build, aot: angularOptions.aot ?? true, deleteOutputPath: angularOptions.deleteOutputPath ?? true, sourceMap: angularOptions.sourceMap ?? true, - outputHashing: angularOptions.outputHashing ?? (setup === WebpackSetup.Build ? OutputHashing.All : OutputHashing.None), - watch: setup === WebpackSetup.Serve, + outputHashing: angularOptions.outputHashing ?? (setup === BundlerSetup.Build ? OutputHashing.All : OutputHashing.None), + watch: setup === BundlerSetup.Serve, allowedCommonJsDependencies: ['dompurify', '@teambit/harmony', 'graphql', '@teambit/documenter.ng.content.copy-box', ...(angularOptions.allowedCommonJsDependencies || [])] }; const normalizedWorkspaceRoot = normalize(workspaceRoot); @@ -167,7 +167,7 @@ async function getWebpackConfig( 'bit-angular-v16-env', // projectName normalizedOptions, (wco: BrowserWebpackConfigOptions) => [ - setup === WebpackSetup.Serve ? getDevServerConfig(wco) : {}, + setup === BundlerSetup.Serve ? getDevServerConfig(wco) : {}, getCommonConfig(wco), getStylesConfig(wco) ], @@ -216,7 +216,7 @@ async function getWebpackConfig( // uniqueName should not be an empty string webpackConfig.output.uniqueName = 'angular-v16-env'; - if (setup === WebpackSetup.Serve) { + if (setup === BundlerSetup.Serve) { webpackConfig = migrateConfiguration(webpackConfig); } @@ -237,7 +237,7 @@ export async function webpackConfigFactory(opts: WebpackConfigFactoryOpts & Webp ) as WebpackConfigWithDevServer; let overwriteConfig: WebpackConfigWithDevServer; - if (opts.setup === WebpackSetup.Serve) { + if (opts.setup === BundlerSetup.Serve) { overwriteConfig = webpack5ServeConfigFactory( opts.devServerID, opts.workspaceDir, diff --git a/angular/envs/base-env/angular-base-env.bit-env.ts b/angular/envs/base-env/angular-base-env.bit-env.ts index ebd7c323..1514eb04 100644 --- a/angular/envs/base-env/angular-base-env.bit-env.ts +++ b/angular/envs/base-env/angular-base-env.bit-env.ts @@ -1,13 +1,32 @@ -import { AngularAppType, NG_APP_NAME } from '@bitdev/angular.app-types.angular-app-type'; -import { AngularEnvOptions, BrowserOptions, DevServerOptions } from '@bitdev/angular.dev-services.common'; -import { AngularPreview, BundlerProvider, DevServerProvider } from '@bitdev/angular.dev-services.preview.preview'; +import { AngularAppType } from '@bitdev/angular.app-types.angular-app-type'; +import { + AngularEnvOptions, + BrowserOptions, + DevServerOptions, + isAppDevContext, + NG_APP_NAME +} from '@bitdev/angular.dev-services.common'; +import { + NgMultiCompiler, + NgMultiCompilerTask +} from '@bitdev/angular.dev-services.compiler.multi-compiler'; +import { + AngularPreview, + BundlerProvider, + DevServerProvider +} from '@bitdev/angular.dev-services.preview.preview'; +import { NgViteDevServer, ViteConfigTransformer } from '@bitdev/angular.dev-services.vite'; +import { NgWebpackBundler, NgWebpackDevServer } from '@bitdev/angular.dev-services.webpack'; +import { + NgAppTemplate, + NgEnvTemplate, + NgModuleTemplate +} from '@bitdev/angular.templates.generators'; import { AngularStarter, DesignSystemStarter, MaterialDesignSystemStarter } from '@bitdev/angular.templates.starters'; -import { NgAppTemplate, NgEnvTemplate, NgModuleTemplate } from '@bitdev/angular.templates.generators'; -import { NgWebpackBundler, NgWebpackDevServer } from '@bitdev/angular.dev-services.webpack'; import { AppTypeList } from '@teambit/application'; import { Pipeline } from '@teambit/builder'; import { Bundler, BundlerContext, DevServer, DevServerContext } from '@teambit/bundler'; @@ -19,7 +38,6 @@ import { AsyncEnvHandler, EnvHandler } from '@teambit/envs'; import { Formatter } from '@teambit/formatter'; import { StarterList, TemplateList } from '@teambit/generator'; import { Linter } from '@teambit/linter'; -import { NgMultiCompiler, NgMultiCompilerTask } from '@bitdev/angular.dev-services.compiler.multi-compiler'; import { PackageGenerator } from '@teambit/pkg'; import { Preview } from '@teambit/preview'; import { SchemaExtractor } from '@teambit/schema'; @@ -52,7 +70,7 @@ export abstract class AngularBaseEnv implements AngularEnvInterface { [key: string]: any; public getNgEnvOptions(): AngularEnvOptions { - return { ...this.ngEnvOptions}; + return { ...this.ngEnvOptions }; } /** @@ -60,10 +78,9 @@ export abstract class AngularBaseEnv implements AngularEnvInterface { */ public setNgEnvOptions(...ngEnvOptions: Partial[]): void { this.ngEnvOptions = merge(this.ngEnvOptions || {}, ...ngEnvOptions); - // TODO check if we need to run ngcc - // if (this.ngEnvOptions.useNgcc) { - // this.depResolver.registerPostInstallSubscribers([this.postInstall.bind(this)]); - // } + if (this.ngEnvOptions.devServer === 'vite' && this.angularVersion < 16) { + throw new Error(`Vite dev server is only supported for Angular 16+`); + } } /** @@ -132,17 +149,27 @@ export abstract class AngularBaseEnv implements AngularEnvInterface { getDevServer( devServerContext: DevServerContext, ngEnvOptions: AngularEnvOptions, - transformers: WebpackConfigTransformer[] = [], + transformers: (WebpackConfigTransformer | ViteConfigTransformer)[] = [], angularOptions: Partial = {}, webpackOptions: Partial = {}, sourceRoot?: string ): AsyncEnvHandler { + if (this.ngEnvOptions.devServer === 'vite' && isAppDevContext(devServerContext)) { + return NgViteDevServer.from({ + angularOptions, + devServerContext, + ngEnvOptions, + sourceRoot, + transformers, + webpackOptions: webpackOptions as any + }); + } return NgWebpackDevServer.from({ angularOptions, devServerContext, ngEnvOptions, sourceRoot, - transformers, + transformers: transformers as WebpackConfigTransformer[], webpackOptions: webpackOptions as any }); } @@ -150,7 +177,7 @@ export abstract class AngularBaseEnv implements AngularEnvInterface { getBundler( bundlerContext: BundlerContext, ngEnvOptions: AngularEnvOptions, - transformers: WebpackConfigTransformer[] = [], + transformers: (WebpackConfigTransformer | ViteConfigTransformer)[] = [], angularOptions: Partial = {}, webpackOptions: Partial = {}, sourceRoot?: string @@ -160,14 +187,20 @@ export abstract class AngularBaseEnv implements AngularEnvInterface { bundlerContext, ngEnvOptions, sourceRoot, - transformers, + transformers: transformers as WebpackConfigTransformer[], webpackOptions: webpackOptions as any }); } preview(): EnvHandler { const ngEnvOptions = this.getNgEnvOptions(); - const devServerProvider: DevServerProvider = (devServerContext: DevServerContext) => this.getDevServer(devServerContext, ngEnvOptions); + const devServerProvider: DevServerProvider = ( + devServerContext: DevServerContext, + transformers?: (WebpackConfigTransformer | ViteConfigTransformer)[], + angularOptions?: Partial, + webpackOptions?: Partial, + sourceRoot?: string + ) => this.getDevServer(devServerContext, ngEnvOptions, transformers, angularOptions, webpackOptions, sourceRoot); const bundlerProvider: BundlerProvider = (bundlerContext: BundlerContext) => this.getBundler(bundlerContext, ngEnvOptions); return AngularPreview.from({ devServerProvider, @@ -184,7 +217,7 @@ export abstract class AngularBaseEnv implements AngularEnvInterface { */ schemaExtractor(): EnvHandler { return TypeScriptExtractor.from({ - tsconfig: require.resolve('./config/tsconfig.json'), + tsconfig: require.resolve('./config/tsconfig.json') }); } @@ -203,9 +236,9 @@ export abstract class AngularBaseEnv implements AngularEnvInterface { generators(): EnvHandler { const envName = this.constructor.name; return TemplateList.from([ - NgModuleTemplate.from({envName, angularVersion: this.angularVersion}), - NgEnvTemplate.from({envName, angularVersion: this.angularVersion}), - NgAppTemplate.from({envName, angularVersion: this.angularVersion}) + NgModuleTemplate.from({ envName, angularVersion: this.angularVersion }), + NgEnvTemplate.from({ envName, angularVersion: this.angularVersion }), + NgAppTemplate.from({ envName, angularVersion: this.angularVersion }) ]); } @@ -215,9 +248,9 @@ export abstract class AngularBaseEnv implements AngularEnvInterface { */ starters(): EnvHandler { return StarterList.from([ - AngularStarter.from({envName: this.constructor.name, angularVersion: this.angularVersion}), - DesignSystemStarter.from({envName: this.constructor.name}), - MaterialDesignSystemStarter.from({envName: this.constructor.name}), + AngularStarter.from({ envName: this.constructor.name, angularVersion: this.angularVersion }), + DesignSystemStarter.from({ envName: this.constructor.name }), + MaterialDesignSystemStarter.from({ envName: this.constructor.name }) ]); } @@ -262,6 +295,6 @@ export abstract class AngularBaseEnv implements AngularEnvInterface { } apps(): EnvHandler { - return AppTypeList.from([AngularAppType.from({angularEnv: this, name: NG_APP_NAME})]); + return AppTypeList.from([AngularAppType.from({ angularEnv: this, name: NG_APP_NAME })]); } } diff --git a/angular/envs/base-env/component.json b/angular/envs/base-env/component.json index 191ea87e..f648c425 100644 --- a/angular/envs/base-env/component.json +++ b/angular/envs/base-env/component.json @@ -19,11 +19,9 @@ "@babel/preset-env": "7.22.15", "@babel/preset-typescript": "7.22.15", "@babel/runtime": "^7.12.18", - "@types/dompurify": "^2.2.2", "@types/enhanced-resolve": "~3.0.7", "@types/node": "^12.11.1", "@types/trusted-types": "~2.0.1", - "dompurify": "^2.2.9", "enhanced-resolve": "5.8.3", "eslint": ">= 7.0.0", "events": "3.3.0", diff --git a/angular/examples/my-angular-env/env.jsonc b/angular/examples/my-angular-env/env.jsonc index aeafe6d7..c357a9ed 100644 --- a/angular/examples/my-angular-env/env.jsonc +++ b/angular/examples/my-angular-env/env.jsonc @@ -8,7 +8,7 @@ "runtime": [ { "name": "tslib", - "version": "^2.4.1", + "version": "^2.6.1", "supportedRange": "^2.3.0" } ], @@ -42,33 +42,33 @@ "peers": [ { "name": "@angular/common", - "version": "~16.0.0", - "supportedRange": "^16.0.0" + "version": "~16.2.0", + "supportedRange": "^16.2.0" }, { "name": "@angular/compiler", - "version": "~16.0.0", - "supportedRange": "^16.0.0", + "version": "~16.2.0", + "supportedRange": "^16.2.0", }, { "name": "@angular/compiler-cli", - "version": "~16.0.0", - "supportedRange": "^16.0.0", + "version": "~16.2.0", + "supportedRange": "^16.2.0", }, { "name": "@angular/core", - "version": "~16.0.0", - "supportedRange": "^16.0.0" + "version": "~16.2.0", + "supportedRange": "^16.2.0" }, { "name": "@angular/platform-browser", - "version": "~16.0.0", - "supportedRange": "^16.0.0" + "version": "~16.2.0", + "supportedRange": "^16.2.0" }, { "name": "@angular/platform-browser-dynamic", - "version": "~16.0.0", - "supportedRange": "^16.0.0" + "version": "~16.2.0", + "supportedRange": "^16.2.0" }, { "name": "jest", @@ -82,13 +82,13 @@ }, { "name": "tslib", - "version": "^2.4.1", + "version": "^2.6.1", "supportedRange": "^2.3.0" }, { "name": "typescript", - "version": "~5.0.2", - "supportedRange": ">=4.9.3 <5.1" + "version": "~5.1.6", + "supportedRange": ">=4.9.3 <5.2" }, { "name": "zone.js", diff --git a/angular/examples/my-angular-env/my-angular-env.bit-env.ts b/angular/examples/my-angular-env/my-angular-env.bit-env.ts index b8ae516f..1fd8ecd5 100644 --- a/angular/examples/my-angular-env/my-angular-env.bit-env.ts +++ b/angular/examples/my-angular-env/my-angular-env.bit-env.ts @@ -24,7 +24,7 @@ import hostDependencies from './preview/host-dependencies'; export class MyAngularEnv extends AngularEnv { // Name of the environment, used for friendly mentions across bit - name = 'my-my-angular-env'; + name = 'my-angular-env'; getTesterConfig() { return { diff --git a/angular/examples/my-angular-v16-env/env.jsonc b/angular/examples/my-angular-v16-env/env.jsonc index aeafe6d7..c357a9ed 100644 --- a/angular/examples/my-angular-v16-env/env.jsonc +++ b/angular/examples/my-angular-v16-env/env.jsonc @@ -8,7 +8,7 @@ "runtime": [ { "name": "tslib", - "version": "^2.4.1", + "version": "^2.6.1", "supportedRange": "^2.3.0" } ], @@ -42,33 +42,33 @@ "peers": [ { "name": "@angular/common", - "version": "~16.0.0", - "supportedRange": "^16.0.0" + "version": "~16.2.0", + "supportedRange": "^16.2.0" }, { "name": "@angular/compiler", - "version": "~16.0.0", - "supportedRange": "^16.0.0", + "version": "~16.2.0", + "supportedRange": "^16.2.0", }, { "name": "@angular/compiler-cli", - "version": "~16.0.0", - "supportedRange": "^16.0.0", + "version": "~16.2.0", + "supportedRange": "^16.2.0", }, { "name": "@angular/core", - "version": "~16.0.0", - "supportedRange": "^16.0.0" + "version": "~16.2.0", + "supportedRange": "^16.2.0" }, { "name": "@angular/platform-browser", - "version": "~16.0.0", - "supportedRange": "^16.0.0" + "version": "~16.2.0", + "supportedRange": "^16.2.0" }, { "name": "@angular/platform-browser-dynamic", - "version": "~16.0.0", - "supportedRange": "^16.0.0" + "version": "~16.2.0", + "supportedRange": "^16.2.0" }, { "name": "jest", @@ -82,13 +82,13 @@ }, { "name": "tslib", - "version": "^2.4.1", + "version": "^2.6.1", "supportedRange": "^2.3.0" }, { "name": "typescript", - "version": "~5.0.2", - "supportedRange": ">=4.9.3 <5.1" + "version": "~5.1.6", + "supportedRange": ">=4.9.3 <5.2" }, { "name": "zone.js", diff --git a/integration/demo-app/demo-app.ng-app.ts b/integration/demo-app/demo-app.ng-app.ts index 715b4fd9..43a61f43 100644 --- a/integration/demo-app/demo-app.ng-app.ts +++ b/integration/demo-app/demo-app.ng-app.ts @@ -1,4 +1,4 @@ -import type { AngularAppOptions } from '@bitdev/angular.dev-services.apps'; +import type { AngularAppOptions } from '@bitdev/angular.app-types.angular-app-type'; import type { BrowserOptions, DevServerOptions } from '@bitdev/angular.dev-services.common'; const angularOptions: BrowserOptions & DevServerOptions = { diff --git a/tsconfig.json b/tsconfig.json index 7b821aaa..ceb7788b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -27,7 +27,7 @@ // "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */ /* Module Resolution Options */ - "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + "moduleResolution": "nodenext", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */