Skip to content

Commit

Permalink
feat: angular 19 support (#185)
Browse files Browse the repository at this point in the history
BREAKING CHANGE: angular 19 support
  • Loading branch information
droshev authored Jan 29, 2025
1 parent ba29331 commit 1fdc892
Show file tree
Hide file tree
Showing 19 changed files with 1,979 additions and 2,611 deletions.
3 changes: 2 additions & 1 deletion apps/documentation/.eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
"prefix": "ui-angular",
"style": "kebab-case"
}
]
],
"@angular-eslint/prefer-standalone": "off"
},
"extends": [
"plugin:@nx/angular",
Expand Down
3 changes: 2 additions & 1 deletion apps/playground/.eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
"prefix": "ui-angular",
"style": "kebab-case"
}
]
],
"@angular-eslint/prefer-standalone": "off"
}
},
{
Expand Down
5 changes: 3 additions & 2 deletions apps/playground/src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import { Component } from '@angular/core';
import { FormControl, FormGroup, Validators } from "@angular/forms";

@Component({
selector: 'ui-angular-root',
templateUrl: './app.component.html'
selector: 'ui-angular-root',
templateUrl: './app.component.html',
standalone: false
})
export class AppComponent {
radioControl = new FormControl('Option 2');
Expand Down
3 changes: 2 additions & 1 deletion apps/playground/src/app/main.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import {Component, ViewChild} from "@angular/core";
import {ButtonComponent} from "@ui5/webcomponents-ngx/main/button";

@Component({
templateUrl: './main.component.html',
templateUrl: './main.component.html',
standalone: false
})
export class MainComponent {
@ViewChild(ButtonComponent) button!: ButtonComponent;
Expand Down
5 changes: 3 additions & 2 deletions apps/playground/src/app/modules/child/child.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import {Component, inject} from '@angular/core';
import {I18nService} from "@ui5/webcomponents-ngx/i18n";

@Component({
template: `
template: `
<ui5-button routerLink="/">{{ 'SOMETHING' | ui5I18n : 'text from the child' }}</ui5-button>
<ui5-button (click)="changeLanguage()">{{ 'CHANGE_LANGUAGE' | ui5I18n }}</ui5-button>
`
`,
standalone: false
})
export class ChildComponent {
i18nService = inject(I18nService);
Expand Down
8 changes: 4 additions & 4 deletions jest.config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { getJestProjects } from '@nx/jest';
import { getJestProjectsAsync } from '@nx/jest';

export default {
projects: getJestProjects(),
};
export default async () => ({
projects: await getJestProjectsAsync(),
});
3 changes: 2 additions & 1 deletion libs/fundamental-styles/.eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
{
"files": ["*.ts"],
"rules": {
"@nx/enforce-module-boundaries": [2]
"@nx/enforce-module-boundaries": [2],
"@angular-eslint/prefer-standalone": "off"
},
"extends": [
"plugin:@nx/angular",
Expand Down
4 changes: 2 additions & 2 deletions libs/fundamental-styles/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
"directory": "libs/fundamental-styles"
},
"peerDependencies": {
"@angular/common": "^18.0.0",
"@angular/core": "^18.0.0"
"@angular/common": "^19.0.0",
"@angular/core": "^19.0.0"
},
"dependencies": {
"tslib": "^2.3.0"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,21 @@ import {Change} from "@schematics/angular/utility/change";
import {addImportToModule} from "@schematics/angular/utility/ast-utils";
import {getModuleDeclaration} from "../utils/getModuleDeclaration";
import {getProjectMainFile} from "../utils/project-main-file";
import {
addModuleImportToStandaloneBootstrap,
findBootstrapApplicationCall
} from "@schematics/angular/private/standalone";
import * as ts from "typescript";
import {findImportProvidersFromCall} from "../utils/find-import-providers-from-call";
import { findBootstrapApplicationCall } from "@schematics/angular/utility/standalone/util";
// import {
// addModuleImportToStandaloneBootstrap,
// findBootstrapApplicationCall
// } from "@schematics/angular/private/standalone";
// import * as ts from "typescript";
// import {findImportProvidersFromCall} from "../utils/find-import-providers-from-call";
import { getSourceFile } from "../utils/getSourceFile";

export function addThemingModule(host: Tree, project: ProjectDefinition, context: SchematicContext, options: Schema): { changes: Change[]; file: string } {
try {
return addModuleToNonStandaloneApp(host, project, context, options);
} catch (e) {
if ((e as {message?: string}).message?.includes('Bootstrap call not found')) {
return addModuleToStandaloneApp(host, project, context, options);
return addModuleToStandaloneApp(host, project);
} else {
throw e;
}
Expand Down Expand Up @@ -66,34 +67,33 @@ function addModuleToNonStandaloneApp(host: Tree, project: ProjectDefinition, con
return {changes, file: appModulePath};
}

function addModuleToStandaloneApp(host: Tree, project: ProjectDefinition, context: SchematicContext, options: Schema): { changes: Change[]; file: string } {
function addModuleToStandaloneApp(host: Tree, project: ProjectDefinition): { changes: Change[]; file: string } {
const mainFile = getProjectMainFile(project);
const mainFileSource = getSourceFile(host, mainFile);
const bootstrapCall = findBootstrapApplicationCall(mainFileSource);
const bootstrapCall = findBootstrapApplicationCall(host, mainFile);
if (!bootstrapCall) {
throw new SchematicsException('Could not find bootstrap call in main.ts');
}
const themingModuleWithDefaultTheme = `Ui5ThemingModule.forRoot({ defaultTheme: '${options.defaultTheme}' })`;
const imports = findImportProvidersFromCall(bootstrapCall);
if (imports) {
const themingModuleImport = imports.arguments.find(arg => ts.isCallExpression(arg) && ts.isPropertyAccessExpression(arg.expression) && ts.isIdentifier(arg.expression.expression) && arg.expression.expression.text === 'Ui5ThemingModule');
if (themingModuleImport) {
const oldContent = host.readText(mainFile);
const newContent = oldContent.split(themingModuleImport.getFullText()).join(themingModuleWithDefaultTheme);
host.overwrite(mainFile, newContent);
context.logger.info('Found previous Ui5ThemingModule. Replaced with new one.');
return {changes: [], file: mainFile};
}
}
addModuleImportToStandaloneBootstrap(
host,
mainFile,
themingModuleWithDefaultTheme,
'@ui5/theming-ngx'
);
const oldContent = host.readText(mainFile);
const newContent = oldContent.replace(themingModuleWithDefaultTheme, 'Ui5ThemingModule'); // This is fixing the incorrect import
host.overwrite(mainFile, newContent);
// const themingModuleWithDefaultTheme = `Ui5ThemingModule.forRoot({ defaultTheme: '${options.defaultTheme}' })`;
// const imports = findImportProvidersFromCall(bootstrapCall);
// if (imports) {
// const themingModuleImport = imports.arguments.find(arg => ts.isCallExpression(arg) && ts.isPropertyAccessExpression(arg.expression) && ts.isIdentifier(arg.expression.expression) && arg.expression.expression.text === 'Ui5ThemingModule');
// if (themingModuleImport) {
// const oldContent = host.readText(mainFile);
// const newContent = oldContent.split(themingModuleImport.getFullText()).join(themingModuleWithDefaultTheme);
// host.overwrite(mainFile, newContent);
// context.logger.info('Found previous Ui5ThemingModule. Replaced with new one.');
// return {changes: [], file: mainFile};
// }
// }
// addModuleImportToStandaloneBootstrap(
// host,
// mainFile,
// themingModuleWithDefaultTheme,
// '@ui5/theming-ngx'
// );
// const oldContent = host.readText(mainFile);
// const newContent = oldContent.replace(themingModuleWithDefaultTheme, 'Ui5ThemingModule'); // This is fixing the incorrect import
// host.overwrite(mainFile, newContent);

return {changes: [], file: mainFile};
}
Expand Down
3 changes: 2 additions & 1 deletion libs/ui5-angular-theming/.eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
"prefix": "ui-angular",
"style": "kebab-case"
}
]
],
"@angular-eslint/prefer-standalone": "off"
},
"extends": [
"plugin:@nx/angular",
Expand Down
4 changes: 2 additions & 2 deletions libs/ui5-angular-theming/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
"directory": "libs/ui5-angular-theming"
},
"peerDependencies": {
"@angular/common": "^18.0.0",
"@angular/core": "^18.0.0"
"@angular/common": "^19.0.0",
"@angular/core": "^19.0.0"
},
"dependencies": {
"tslib": "^2.3.0"
Expand Down
4 changes: 2 additions & 2 deletions libs/ui5-angular/.eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@
"@angular-eslint/no-input-rename": ["off"],
"@angular-eslint/no-output-rename": ["off"],
"@typescript-eslint/no-explicit-any": ["off"],
"@angular-eslint/no-host-metadata-property": ["off"],
"@angular-eslint/no-inputs-metadata-property": ["off"],
"@angular-eslint/no-outputs-metadata-property": ["off"],
"@angular-eslint/no-output-native": ["off"],
"@typescript-eslint/no-empty-interface": ["off"],
"@typescript-eslint/no-non-null-assertion": "warn"
"@typescript-eslint/no-non-null-assertion": "warn",
"@angular-eslint/prefer-standalone": "off"
}
},
{
Expand Down
6 changes: 3 additions & 3 deletions libs/ui5-angular/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@
"directory": "libs/ui5-angular"
},
"peerDependencies": {
"@angular/common": "^18.0.0",
"@angular/core": "^18.0.0",
"@angular/forms": "^18.0.0",
"@angular/common": "^19.0.0",
"@angular/core": "^19.0.0",
"@angular/forms": "^19.0.0",
"@ui5/webcomponents": "2.6.2",
"@ui5/webcomponents-base": "2.6.2",
"@ui5/webcomponents-fiori": "2.6.2",
Expand Down
2 changes: 2 additions & 0 deletions libs/ui5-angular/schematics/add-theming/add-theming-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,5 @@ function addModuleToStandaloneApp(host: Tree, project: ProjectDefinition): { cha

return {changes: [], file: mainFile};
}


4 changes: 2 additions & 2 deletions libs/webcomponents-nx/src/executors/prepare/executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ export default async function (
options: PrepareOptions,
context: ExecutorContext
): Promise<{ success: boolean }> {
const projectConfig: ProjectConfiguration = context.workspace.projects[context.projectName as string];
const projectConfig: ProjectConfiguration = context.projectsConfigurations.projects[context.projectName as string];
if (options.schematics) {
await copySchematics(options, projectConfig, context.projectName as string);
}
const outputPath =
interpolate(context.workspace.projects[context.projectName].targets.build.outputs[0], {
interpolate(context.projectsConfigurations.projects[context.projectName].targets.build.outputs[0], {
projectRoot: projectConfig.root,
projectName: projectConfig.name,
project: projectConfig,
Expand Down
50 changes: 30 additions & 20 deletions libs/webcomponents-nx/src/executors/sync/executor.ts
Original file line number Diff line number Diff line change
@@ -1,58 +1,68 @@
import { ExecutorContext } from "nx/src/config/misc-interfaces";
import { flushChanges, FsTree, printChanges } from "nx/src/generators/tree";
import { workspaceRoot } from "nx/src/utils/app-root";
import { formatFiles, readProjectConfiguration } from "@nx/devkit";
import { fsCommit, NxFsImplementation } from "@ui5/webcomponents-transformer-fs-commit";
import { FileSystemInterface, transformer, TransformerConfig } from "@ui5/webcomponents-transformer";
import {
ExecutorContext,
readProjectConfiguration,
logger,
workspaceRoot,
formatFiles,
} from '@nx/devkit';
import { FsTree, flushChanges, printChanges } from 'nx/src/generators/tree';
import { fsCommit, NxFsImplementation } from '@ui5/webcomponents-transformer-fs-commit';
import { FileSystemInterface, transformer, TransformerConfig } from '@ui5/webcomponents-transformer';

type TransformerNxConfig<T> = Omit<TransformerConfig<T>, 'persist'>;

export type Ui5NxTransformerConfig<T = unknown> =
TransformerNxConfig<T>
| ((fs: FileSystemInterface) => TransformerNxConfig<T>);
TransformerNxConfig<T> |
((fs: FileSystemInterface) => TransformerNxConfig<T>);

const executeTransformation = async (options: {
transformerConfig: Ui5NxTransformerConfig;
basePath: string,
fsAdapter: FileSystemInterface
basePath: string;
fsAdapter: FileSystemInterface;
}) => {
await transformer({
persist: fsCommit(options.fsAdapter, options.basePath),
...(typeof options.transformerConfig === 'function' ? options.transformerConfig(options.fsAdapter) : options.transformerConfig)
...(typeof options.transformerConfig === 'function' ? options.transformerConfig(options.fsAdapter) : options.transformerConfig),
});
};

/**
* The executor which is calling the wrapper in Nx environment
* The executor which is calling the wrapper in Nx environment.
* @param schema
* @param executorContext
*/
export default async function (
schema: { configFile?: string, configFiles?: string[] },
schema: { configFile?: string; configFiles?: string[] },
executorContext: ExecutorContext
): Promise<{ success: boolean }> {
if (!schema.configFile && !schema.configFiles) {
throw new Error('No config file specified');
throw new Error('No config file specified.');
}

const host = new FsTree(workspaceRoot, executorContext.isVerbose);
const configFiles = schema.configFile ? [schema.configFile] : schema.configFiles;
const projectConfiguration = readProjectConfiguration(host, executorContext.projectName);
const outputs = executorContext.target.outputs;
const fsAdapter = new NxFsImplementation(host);

outputs.forEach(output => {
host.delete(output.replace('{projectRoot}', projectConfiguration.root).replace('{workspaceRoot}', workspaceRoot))
// Process the output paths
(executorContext.target.outputs || []).forEach((output) => {
host.delete(output.replace('{projectRoot}', projectConfiguration.root).replace('{workspaceRoot}', workspaceRoot));
});

// Execute transformation
for (const configFile of configFiles) {
await executeTransformation({
transformerConfig: await import(configFile).then((module) => module.default),
basePath: projectConfiguration.sourceRoot,
fsAdapter
fsAdapter,
});
}

const changes = host.listChanges();
printChanges(changes);
flushChanges(workspaceRoot, changes);
flushChanges(host.root, changes); // Pass the base path (host.root)

await formatFiles(host);

return {success: true};
return { success: true };
}
3 changes: 2 additions & 1 deletion nx.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,5 +67,6 @@
"defaultProject": "documentation",
"nxCloudAccessToken": "YTA5ZjBmYWItOTIxOC00ODdkLTkzMGEtZGFmZDk3NTUyODhhfHJlYWQ=",
"useInferencePlugins": false,
"defaultBase": "main"
"defaultBase": "main",
"useLegacyCache": true
}
Loading

0 comments on commit 1fdc892

Please sign in to comment.