Skip to content

Commit 5fac732

Browse files
committed
Merge branch 'dmdimitrov/query-builder-improvements' of https://github.com/IgniteUI/igniteui-angular into dmdimitrov/query-builder-improvements
2 parents d7dfe81 + abe8c42 commit 5fac732

File tree

51 files changed

+370
-826
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+370
-826
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ All notable changes for each version of this project will be documented in this
1414
- Added support for showing/hiding the indicator controls (dots). Can be configured via the `indicators` property. Defaults to `true`.
1515

1616
- `IFilteringExpression`
17-
- **Breaking Change** There is a new `conditionName` property which is required. This would generally be equal to the old `condition.name`.
17+
- A new optional property called `conditionName` has been introduced. This would generally be equal to the existing `condition.name`.
1818

1919

2020
#### Scrollbar: New CSS variables

projects/igniteui-angular/migrations/common/ServerHost.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export class ServerHost implements ts.server.ServerHost {
1515
/** Cached because Angular schematics encapsulation's customRequire doesn't provide `resolve` */
1616
private nativeRequire = createRequire(__filename);
1717

18-
constructor(private host: Tree) {
18+
constructor(public host: Tree) {
1919
this.args = ts.sys.args;
2020
this.newLine = ts.sys.newLine;
2121
this.useCaseSensitiveFileNames = ts.sys.useCaseSensitiveFileNames;

projects/igniteui-angular/migrations/common/UpdateChanges.spec.ts

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import { EmptyTree } from '@angular-devkit/schematics';
21
import { UnitTestTree } from '@angular-devkit/schematics/testing';
32
import * as fs from 'fs';
43
import * as path from 'path';
5-
import { ClassChanges, BindingChanges, SelectorChanges, ThemeChanges, ImportsChanges, ElementType, ThemeType } from './schema';
4+
import { ClassChanges, BindingChanges, SelectorChanges, ThemeChanges, ImportsChanges, ElementType, ThemeType, MemberChanges } from './schema';
65
import { UpdateChanges, InputPropertyType, BoundPropertyObject } from './UpdateChanges';
76
import * as tsUtils from './tsUtils';
7+
import { setupTestTree } from './setup.spec';
88

99
describe('UpdateChanges', () => {
1010
let appTree: UnitTestTree;
@@ -31,14 +31,19 @@ describe('UpdateChanges', () => {
3131
}
3232

3333
beforeEach(() => {
34-
appTree = new UnitTestTree(new EmptyTree());
35-
appTree.create('/angular.json', JSON.stringify({
34+
appTree = setupTestTree({
3635
projects: {
3736
testProj: {
37+
root: '',
3838
sourceRoot: '/'
3939
}
40+
},
41+
schematics: {
42+
'@schematics/angular:component': {
43+
prefix: 'app'
44+
}
4045
}
41-
}));
46+
});
4247
});
4348

4449
it('should replace/remove components', done => {
@@ -915,31 +920,45 @@ export class AppModule { }`);
915920
describe('Language Service migrations', () => {
916921

917922
it('Should be able to replace property of an event', () => {
918-
pending('set up tests for migrations through lang service');
923+
const selectorsJson: MemberChanges = {
924+
changes: [
925+
{ member: 'onGridKeydown', replaceWith: 'gridKeydown', definedIn: ['IgxGridComponent'] }
926+
]
927+
};
928+
const jsonPath = path.join(__dirname, 'changes', 'members.json');
929+
930+
// leave callThrough on spies for other files the LS test might want to load:
931+
spyOn(fs, 'existsSync').and.callThrough()
932+
.withArgs(jsonPath).and.returnValue(true);
933+
spyOn(fs, 'readFileSync').and.callThrough()
934+
.withArgs(jsonPath, jasmine.any(String)).and.returnValue(JSON.stringify(selectorsJson));
935+
919936
const fileContent =
920937
`import { Component } from '@angular/core';
921-
import { IGridCreatedEventArgs } from 'igniteui-angular';
938+
import { IgxGridComponent, IGridKeydownEventArgs } from 'igniteui-angular';
922939
@Component({
923940
selector: 'app-custom-grid',
924941
template: ''
925942
})
926943
export class CustomGridComponent {
927-
public childGridCreated(event: IGridCreatedEventArgs) {
928-
event.grid.onGridKeydown.subscribe(() => {});
944+
public childGridCreated(event: IGridKeydownEventArgs) {
945+
const grid = event.owner as IgxGridComponent;
946+
grid.onGridKeydown.subscribe(() => {});
929947
}
930948
}
931949
`;
932950
appTree.create('test.component.ts', fileContent);
933951
const expectedFileContent =
934952
`import { Component } from '@angular/core';
935-
import { IGridCreatedEventArgs } from 'igniteui-angular';
953+
import { IgxGridComponent, IGridKeydownEventArgs } from 'igniteui-angular';
936954
@Component({
937955
selector: 'app-custom-grid',
938956
template: ''
939957
})
940958
export class CustomGridComponent {
941-
public childGridCreated(event: IGridCreatedEventArgs) {
942-
event.grid.gridKeydown.subscribe(() => {});
959+
public childGridCreated(event: IGridKeydownEventArgs) {
960+
const grid = event.owner as IgxGridComponent;
961+
grid.gridKeydown.subscribe(() => {});
943962
}
944963
}
945964
`;

projects/igniteui-angular/migrations/common/UpdateChanges.ts

Lines changed: 42 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,14 @@ import {
1010
} from './schema';
1111
import {
1212
getLanguageService, getRenamePositions, getIdentifierPositions,
13-
createProjectService, isMemberIgniteUI, NG_LANG_SERVICE_PACKAGE_NAME, NG_CORE_PACKAGE_NAME, findMatches
13+
isMemberIgniteUI, NG_LANG_SERVICE_PACKAGE_NAME, NG_CORE_PACKAGE_NAME, findMatches
1414
} from './tsUtils';
1515
import {
1616
getProjectPaths, getWorkspace, getProjects, escapeRegExp, replaceMatch,
1717
getPackageManager, canResolvePackage, tryInstallPackage, tryUninstallPackage, getPackageVersion
1818
} from './util';
1919
import { ServerHost } from './ServerHost';
20+
import { serviceContainer } from './project-service-container';
2021

2122
const TSCONFIG_PATH = 'tsconfig.json';
2223

@@ -38,7 +39,6 @@ interface AppliedChange {
3839
/* eslint-disable arrow-parens */
3940
export class UpdateChanges {
4041
protected tsconfigPath = TSCONFIG_PATH;
41-
protected _projectService: tss.server.ProjectService;
4242

4343
public _shouldInvokeLS = true;
4444
public get shouldInvokeLS(): boolean {
@@ -54,40 +54,18 @@ export class UpdateChanges {
5454
}
5555

5656
public get projectService(): tss.server.ProjectService {
57-
if (!this._projectService) {
58-
this._projectService = createProjectService(this.serverHost);
59-
// Force Angular service to compile project on initial load w/ configure project
60-
// otherwise if the first compilation occurs on an HTML file the project won't have proper refs
61-
// and no actual angular metadata will be resolved for the rest of the migration
62-
63-
// TODO: this patter/issue might be obsolete; if so, should be safe to return _projectService directly
64-
const mainRelPath = this.getWorkspaceProjectEntryPath();
65-
if (!mainRelPath) {
66-
return null;
67-
}
68-
69-
// patch TSConfig so it includes angularOptions.strictTemplates
70-
// ivy ls requires this in order to function properly on templates
71-
this.patchTsConfig();
72-
const mainAbsPath = path.resolve(this._projectService.currentDirectory, mainRelPath);
73-
const scriptInfo = this._projectService.getOrCreateScriptInfoForNormalizedPath(tss.server.toNormalizedPath(mainAbsPath), false);
74-
this._projectService.openClientFile(scriptInfo.fileName);
75-
76-
77-
try {
78-
const project = this._projectService.findProject(scriptInfo.containingProjects[0].getProjectName());
79-
project.getLanguageService().getSemanticDiagnostics(mainAbsPath);
80-
} catch (err) {
81-
this.context.logger.warn(
82-
"An error occurred during TypeScript project service setup. Some migrations relying on language services might not be applied."
83-
);
84-
}
57+
const projectService = serviceContainer.projectService;
58+
if (!serviceContainer.configured) {
59+
this.configureForAngularLS(projectService);
60+
serviceContainer.configured = true;
8561
}
8662

87-
return this._projectService;
63+
return projectService;
8864
}
8965

90-
protected serverHost: ServerHost;
66+
protected get serverHost(): ServerHost {
67+
return serviceContainer.serverHost;
68+
}
9169
protected workspace: WorkspaceSchema;
9270
protected sourcePaths: string[];
9371
protected classChanges: ClassChanges;
@@ -176,7 +154,8 @@ export class UpdateChanges {
176154
this.themeChanges = this.loadConfig('theme-changes.json');
177155
this.importsChanges = this.loadConfig('imports.json');
178156
this.membersChanges = this.loadConfig('members.json');
179-
this.serverHost = new ServerHost(this.host);
157+
// update LS server host with the schematics tree:
158+
this.serverHost.host = this.host;
180159
}
181160

182161
/** Apply configured changes to the Host Tree */
@@ -835,6 +814,36 @@ export class UpdateChanges {
835814
return project;
836815
}
837816

817+
/**
818+
* Force Angular service to compile project on initial load w/ configured project
819+
* otherwise if the first compilation occurs on an HTML file the project won't have proper refs
820+
* and no actual angular metadata will be resolved for the rest of the migration
821+
*/
822+
private configureForAngularLS(projectService: ts.server.ProjectService): void {
823+
// TODO: this pattern/issue might be obsolete
824+
const mainRelPath = this.getWorkspaceProjectEntryPath();
825+
if (!mainRelPath) {
826+
return;
827+
}
828+
829+
// patch TSConfig so it includes angularOptions.strictTemplates
830+
// ivy ls requires this in order to function properly on templates
831+
this.patchTsConfig();
832+
const mainAbsPath = path.resolve(projectService.currentDirectory, mainRelPath);
833+
const scriptInfo = projectService.getOrCreateScriptInfoForNormalizedPath(tss.server.toNormalizedPath(mainAbsPath), false);
834+
projectService.openClientFile(scriptInfo.fileName);
835+
836+
837+
try {
838+
const project = projectService.findProject(scriptInfo.containingProjects[0].getProjectName());
839+
project.getLanguageService().getSemanticDiagnostics(mainAbsPath);
840+
} catch (err) {
841+
this.context.logger.warn(
842+
"An error occurred during TypeScript project service setup. Some migrations relying on language services might not be applied."
843+
);
844+
}
845+
}
846+
838847
private getWorkspaceProjectEntryPath(): string | null {
839848
const projectKeys = Object.keys(this.workspace.projects);
840849
if (!projectKeys.length) {
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import * as tss from 'typescript/lib/tsserverlibrary';
2+
import { createProjectService } from './tsUtils';
3+
import { ServerHost } from './ServerHost';
4+
5+
export class ProjectServiceContainer {
6+
private _serverHost: ServerHost;
7+
private _projectService: tss.server.ProjectService;
8+
9+
/** Indicates additional config adjustments after init have been made */
10+
public configured = false;
11+
12+
public get serverHost(): ServerHost {
13+
if (!this._serverHost) {
14+
this._serverHost = new ServerHost(null);
15+
}
16+
return this._serverHost;
17+
}
18+
19+
public get projectService(): tss.server.ProjectService {
20+
if (!this._projectService) {
21+
this._projectService = createProjectService(this.serverHost);
22+
}
23+
24+
return this._projectService;
25+
}
26+
}
27+
28+
export const serviceContainer = new ProjectServiceContainer();
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import { readFileSync } from 'fs';
2+
import { join } from 'path';
3+
4+
import { EmptyTree } from '@angular-devkit/schematics';
5+
import { UnitTestTree } from '@angular-devkit/schematics/testing';
6+
import * as tss from 'typescript/lib/tsserverlibrary';
7+
import { serviceContainer } from './project-service-container';
8+
9+
const configJson = {
10+
version: 1,
11+
projects: {
12+
testProj: {
13+
projectType: 'application',
14+
root: '',
15+
sourceRoot: 'testSrc',
16+
architect: {
17+
build: {
18+
builder: '@angular-devkit/build-angular:application',
19+
options: {
20+
browser: 'testSrc/appPrefix/component/test.component.ts'
21+
}
22+
}
23+
}
24+
}
25+
},
26+
schematics: {
27+
'@schematics/angular:component': {
28+
prefix: 'appPrefix'
29+
}
30+
}
31+
};
32+
33+
const tsConfig = readFileSync('tsconfig.json');
34+
35+
/**
36+
* Internal extension to ensure newly created .ts files are added to the project service
37+
* or their source cache is updated/invalidated if the name is reused in multiple tests
38+
*/
39+
class IgxUnitTestTree extends UnitTestTree {
40+
public override create(path: string, content: string | Buffer): void {
41+
super.create(path, content);
42+
if (!path.endsWith('.ts') && !path.endsWith('.html')) return;
43+
44+
const configured = serviceContainer.configured && serviceContainer.projectService.configuredProjects.size;
45+
if (configured) {
46+
// rush host update
47+
serviceContainer.serverHost.host = this;
48+
49+
const entryPath = tss.server.toNormalizedPath(join(process.cwd(), path))
50+
const scriptInfo = serviceContainer.projectService?.getOrCreateScriptInfoForNormalizedPath(entryPath, false);
51+
if (!scriptInfo) {
52+
return;
53+
}
54+
// integrate incoming virtual file in project/program for LS to work:
55+
const project = serviceContainer.projectService.configuredProjects.values().next().value as tss.server.ConfiguredProject;
56+
if (!project.containsScriptInfo(scriptInfo)) {
57+
// add the file to the configured project to prevent tss from creating an inferred project for floating files
58+
scriptInfo.attachToProject(project);
59+
} else {
60+
// if using same file, force-reload from host for new content
61+
scriptInfo.reloadFromFile(tss.server.asNormalizedPath(entryPath));
62+
}
63+
} else {
64+
// strip leading slash as it messes with the resolve and assign as new main entry
65+
path = path.startsWith('/') ? path.substring(1) : path;
66+
const config = JSON.parse(this.readContent('angular.json'));
67+
config.projects.testProj.architect.build.options.browser = path;
68+
this.overwrite('angular.json', JSON.stringify(config));
69+
}
70+
}
71+
}
72+
73+
/**
74+
* Create the test tree and init the `angular.json` file and `tsconfig.json`
75+
*/
76+
export function setupTestTree(ngConfigOverride: object | null = null) {
77+
const tree = new IgxUnitTestTree(new EmptyTree());
78+
tree.create('angular.json', JSON.stringify(ngConfigOverride ?? configJson));
79+
// mirror tsconfig in test tree, otherwise LS server host handling may be off:
80+
tree.create('tsconfig.json', tsConfig);
81+
return tree as UnitTestTree;
82+
}

projects/igniteui-angular/migrations/update-10_1_0/index.spec.ts

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,13 @@
11
import * as path from 'path';
2-
import { EmptyTree } from '@angular-devkit/schematics';
32
import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing';
3+
import { setupTestTree } from '../common/setup.spec';
44

55
describe('Update 10.1.0', () => {
66
let appTree: UnitTestTree;
77
const schematicRunner = new SchematicTestRunner('ig-migrate', path.join(__dirname, '../migration-collection.json'));
8-
const configJson = {
9-
projects: {
10-
testProj: {
11-
root: '/',
12-
sourceRoot: '/testSrc'
13-
}
14-
},
15-
schematics: {
16-
'@schematics/angular:component': {
17-
prefix: 'appPrefix'
18-
}
19-
}
20-
};
218

229
beforeEach(() => {
23-
appTree = new UnitTestTree(new EmptyTree());
24-
appTree.create('/angular.json', JSON.stringify(configJson));
10+
appTree = setupTestTree();
2511
});
2612

2713
it('should upgrade the igx-action-icon to igx-navbar-action', async () => {

0 commit comments

Comments
 (0)