Skip to content

Commit

Permalink
Merge pull request #1174 from IgniteUI/bpenkov/standalone-components
Browse files Browse the repository at this point in the history
Update to standalone components
  • Loading branch information
Lipata authored Dec 11, 2023
2 parents 4609525 + c2ba933 commit 0b3316a
Show file tree
Hide file tree
Showing 1,018 changed files with 72,510 additions and 814 deletions.
13 changes: 12 additions & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,15 @@ root = true

# Tab indentation (no size specified)
[*.js]
indent_style = tab
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true

[*.ts]
quote_type = single

[*.md]
max_line_length = off
trim_trailing_whitespace = false
2 changes: 1 addition & 1 deletion packages/cli/templates/angular/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class AngularFramework implements Framework {
this.id = "angular";
this.name = "Angular";
this.projectLibraries = [];
this.projectLibraries.push(require("@igniteui/angular-templates").default as ProjectLibrary);
this.projectLibraries.push(...require("@igniteui/angular-templates").default as ProjectLibrary[]);
this.projectLibraries.push(require("./ig-ts") as ProjectLibrary);
}
}
Expand Down
3 changes: 3 additions & 0 deletions packages/core/types/TemplateDependency.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,7 @@ export interface TemplateDependency {

/** Add an identifier into `ngModule` exports metadata */
export?: string | string[];

/** Describes a dependency for a standalone component's meta */
standalone?: boolean;
}
66 changes: 64 additions & 2 deletions packages/core/typescript/TypeScriptFileUpdate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export class TypeScriptFileUpdate {
private requestedImports: Array<{ from: string, imports: string[], edit: boolean }>;
private ngMetaEdits: {
declarations: string[],
imports: Array<{ name: string, root: boolean }>,
imports: Array<{ name: string, root: boolean, standalone?: boolean }>,
providers: string[],
exports: string[]
};
Expand All @@ -42,9 +42,14 @@ export class TypeScriptFileUpdate {
if (this.requestedImports.filter(x => x.edit).length) {
transforms.push(this.importsTransformer);
}
if (Object.keys(this.ngMetaEdits).filter(x => this.ngMetaEdits[x].length).length) {

// should we support both standalone and module-based components in the same app?
if (this.ngMetaEdits.imports.some(x => x.standalone)) {
transforms.push(this.componentMetaTransformer);
} else if (Object.keys(this.ngMetaEdits).filter(x => this.ngMetaEdits[x].length).length) {
transforms.push(this.ngModuleTransformer);
}

if (transforms.length) {
this.targetSource = ts.transform(this.targetSource, transforms).transformed[0];
}
Expand Down Expand Up @@ -137,6 +142,26 @@ export class TypeScriptFileUpdate {
this.ngMetaEdits.exports.push(...exportsArr);
}

/**
* Updates a standalone component's imports metadata.
*/
public addStandaloneImport(dep: TemplateDependency, variables?: { [key: string]: string }) {
const copy = {
import: this.asArray(dep.import, variables),
provide: this.asArray(dep.provide, variables)
};
if (dep.from) {
// request import
const identifiers = [...copy.import, ...copy.provide];
this.requestImport(identifiers, Util.applyConfigTransformation(dep.from, variables));
}

const imports = copy.import
.map(x => ({ name: x, root: dep.root, standalone: true }))
.filter(x => !this.ngMetaEdits.imports.find(i => i.name === x.name));
this.ngMetaEdits.imports.push(...imports);
}

//#region File state

/** Initializes existing imports info, [re]sets import and `NgModule` edits */
Expand Down Expand Up @@ -496,6 +521,43 @@ export class TypeScriptFileUpdate {
return ts.visitNode(rootNode, visitor);
}

// TODO: extend to allow the modification of multiple metadata properties
/** Transformation to apply `this.ngMetaEdits` to a standalone `Component` metadata imports */
protected componentMetaTransformer: ts.TransformerFactory<ts.Node> =
<T extends ts.Node>(context: ts.TransformationContext) => (rootNode: T) => {
const visitComponent: ts.Visitor = (node: ts.Node): ts.Node => {
let importsExpr = null;
const prop = "imports";
if (node.kind === ts.SyntaxKind.ObjectLiteralExpression &&
node.parent &&
node.parent.kind === ts.SyntaxKind.CallExpression) {
const obj = (node as ts.ObjectLiteralExpression);
const objProperties = ts.visitNodes(obj.properties, visitor);
const newProps = [];
const importDeps = this.ngMetaEdits.imports;
importsExpr = ts.factory.createArrayLiteralExpression(
importDeps.map(x => TsUtils.createIdentifier(x.name))
);
newProps.push(ts.factory.createPropertyAssignment(prop, importsExpr));
return context.factory.updateObjectLiteralExpression(obj, [
...objProperties,
...newProps
]);
} else {
node = ts.visitEachChild(node, visitComponent, context);
}

return node;
};
const visitCondition: (node: ts.Node) => boolean = (node: ts.Node) => {
return node.kind === ts.SyntaxKind.CallExpression &&
node.parent && node.parent.kind === ts.SyntaxKind.Decorator &&
(node as ts.CallExpression).expression.getText() === "Component";
};
const visitor = this.createVisitor(visitComponent, visitCondition, context);
return ts.visitNode(rootNode, visitor);
}

//#endregion ts.TransformerFactory

//#region Formatting
Expand Down
51 changes: 47 additions & 4 deletions packages/igx-templates/IgniteUIForAngularTemplate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export class IgniteUIForAngularTemplate implements Template {
return Promise.resolve(false);
}

return Object.assign({}, options["extraConfig"], this.getBaseVariables(name));
return Object.assign({}, options["extraConfig"], this.getBaseVariables(name));
}

//TODO: rename name to fullName for clarity + in all other places fileName to fullName
Expand All @@ -62,7 +62,47 @@ export class IgniteUIForAngularTemplate implements Template {
// tslint:disable-next-line:no-submodule-imports
require("@igniteui/cli-core/typescript").TypeScriptFileUpdate;

if (!(options && options.skipRoute) && App.container.get<IFileSystem>(FS_TOKEN).fileExists("src/app/app-routing.module.ts")) {
// standalone components
const mainModulePath = path.join(projectPath, `src/app/${modulePath}`);
if (!this.fileExists(mainModulePath)) {
const appRoutesPath = "src/app/app.routes.ts";
const folderName = this.folderName(name);
const fileName = this.fileName(name);
if (!(options && options.skipRoute) && this.fileExists(appRoutesPath)) {
const rountesConfig = new TsUpdate(path.join(projectPath, appRoutesPath));
rountesConfig.addRoute(
path.join(projectPath, `src/app/${folderName}/${fileName}.component.ts`),
this.fileName(name), //path
Util.nameFromPath(name) //text
);
}

const componentFile = new TsUpdate(path.join(projectPath, `src/app/${folderName}/${fileName}.component.ts`));
for (const dep of this.dependencies) {
if (dep.from && dep.from.startsWith(".")) {
// relative file dependency
const copy = Object.assign({}, dep);
copy.from = Util.relativePath(
path.join(projectPath, `src/app/${folderName}/${fileName}.component.ts`),
path.join(projectPath, copy.from!),
true,
true);
// can use addNgModuleMeta here instead?
componentFile.addStandaloneImport(copy,
Util.applyDelimiters(this.getBaseVariables(name), this.delimiters.content));
continue;
}

componentFile.addStandaloneImport(dep,
Util.applyDelimiters(this.getBaseVariables(name), this.delimiters.content));
}

componentFile.finalize();
return;
}

// ngModule based components
if (!(options && options.skipRoute) && this.fileExists("src/app/app-routing.module.ts")) {
//1) import the component class name,
//2) and populate the Routes array with the path and component
//for example: { path: 'combo', component: ComboComponent }
Expand All @@ -76,7 +116,6 @@ export class IgniteUIForAngularTemplate implements Template {

//3) add an import of the component class from its file location.
//4) populate the declarations portion of the @NgModule with the component class name.
const mainModulePath = path.join(projectPath, `src/app/${modulePath}`);
const mainModule = new TsUpdate(mainModulePath);
this.addClassDeclaration(mainModule, projectPath, name, modulePath);

Expand All @@ -85,7 +124,7 @@ export class IgniteUIForAngularTemplate implements Template {
if (dep.from && dep.from.startsWith(".")) {
// relative file dependency
const copy = Object.assign({}, dep);
copy.from = Util.relativePath(mainModulePath, path.join(projectPath, copy.from), true, true);
copy.from = Util.relativePath(mainModulePath, path.join(projectPath, copy.from!), true, true);
mainModule.addNgModuleMeta(copy,
Util.applyDelimiters(this.getBaseVariables(name), this.delimiters.content));
} else {
Expand All @@ -101,6 +140,10 @@ export class IgniteUIForAngularTemplate implements Template {
}
public setExtraConfiguration(extraConfigKeys: {}) { }

public fileExists(filePath: string): boolean {
return App.container.get<IFileSystem>(FS_TOKEN).fileExists(filePath);
}

protected addClassDeclaration(mainModule: TypeScriptFileUpdate, projPath: string, name: string, modulePath: string) {
mainModule.addDeclaration(
path.join(projPath, `src/app/${this.folderName(name)}/${this.fileName(name)}.component.ts`),
Expand Down
1 change: 1 addition & 0 deletions packages/igx-templates/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const IGNITEUI_ANGULAR_PACKAGE = "igniteui-angular@~17.0.0";
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<p>igx-accordion component sample.</p>
<p>You can read more about configuring the igx-tabs component in the
<a href="https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/accordion/README.md" target="_blank">README</a> or the
<a href="https://www.infragistics.com/products/ignite-ui-angular/angular/components/accordion" target="_blank">official documentation</a>.</p>
<igx-switch [(ngModel)]="singleBranchExpand">Single Branch Expand</igx-switch>
<article class="sample-wrapper">
<igx-accordion #accordion [singleBranchExpand]="singleBranchExpand">
<igx-expansion-panel>
<igx-expansion-panel-header class="expansion-header">
<igx-expansion-panel-title>What has changed about subscription and pricing model?
</igx-expansion-panel-title>
</igx-expansion-panel-header>
<igx-expansion-panel-body>
We have moved to a subscription-based pricing model for all our developer tools. This makes it easier
for you to manage your license subscriptions and allows us to provide a better level of service for you.

We updated our pricing and packages to provide you with flexible options and the best value. This
includes <a href="https://www.infragistics.com/products/ignite-ui">Ignite UI </a>(formerly Ignite UI for
JavaScript) which includes all of our JavaScript framework
components for web development, including: Angular, ASP.NET (Core and MVC), Blazor, JQuery, React and
Web Components), as well as Infragistics Professional, Infragistics Ultimate, our Ultimate UI products.

We also offer multi-year subscriptions options with a built-in discount, so you can see the value up
front. With these updates we are confident that we are providing the best platforms and the best price.
</igx-expansion-panel-body>
</igx-expansion-panel>
<igx-expansion-panel>
<igx-expansion-panel-header class="expansion-header">
<igx-expansion-panel-title>Who will the updated changes impact?</igx-expansion-panel-title>
</igx-expansion-panel-header>
<igx-expansion-panel-body>
The license updates will impact all new and current customers using Ignite UI, Infragistics Professional
and Infragistics Ultimate. Specifically, we have also made updates to our product and packaging for
Ignite UI for JavaScript, Ignite UI for Angular, Ignite UI for React and Ignite UI for Web components.
For more information, please refer to this blog:

<a
href="https://www.infragistics.com/community/blogs/b/jason_beres/posts/announcement-changes-to-ignite-ui-product-packaging">Announcement:
Changes to Ignite UI Product & Packaging</a>

The pricing has been updated for all products and packages. So, all new or additional licenses will be
sold based on our new pricing and packages. All existing license agreements will be honored and renewed
based upon the current agreement.
</igx-expansion-panel-body>
</igx-expansion-panel>
<igx-expansion-panel>
<igx-expansion-panel-header class="expansion-header">
<igx-expansion-panel-title>What is the difference between your old model and your current subscription
model
for Ignite UI?</igx-expansion-panel-title>
</igx-expansion-panel-header>
<igx-expansion-panel-body>
For Ignite UI customers, we are moving away from NPM for licensed packages. The current NPM packages
will be replaced with packages that include a “Trial Version” watermark. Licensed packages for Ignite UI
will be available from our cloud hosted ProGet server.

For more information, please refer to this article:

<a
href="https://www.infragistics.com/products/ignite-ui-angular/angular/components/general/ignite-ui-licensing">Moving
from Trial to Licensed Ignite UI NPM Packages</a>
</igx-expansion-panel-body>
</igx-expansion-panel>
<igx-expansion-panel>
<igx-expansion-panel-header class="expansion-header">
<igx-expansion-panel-title>What happens if I don’t renew my subscription?</igx-expansion-panel-title>
</igx-expansion-panel-header>
<igx-expansion-panel-body>
Any unlicensed or trial versions of Ignite UI for Angular, React and Web Components will now include
this watermark.
</igx-expansion-panel-body>
</igx-expansion-panel>
<igx-expansion-panel>
<igx-expansion-panel-header class="expansion-header">
<igx-expansion-panel-title>If I don’t renew my subscription will I still have access to previous
versions of
Infragistics products?</igx-expansion-panel-title>
</igx-expansion-panel-header>
<igx-expansion-panel-body>
Any version of Infragistics software which you have downloaded can continue to be used perpetually.
Access to download any new or previous versions through our customer portal and package feeds will
require maintaining an active subscription by continuing to renew it.
</igx-expansion-panel-body>
</igx-expansion-panel>
</igx-accordion>
</article>
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
$ep-sample-border: 1px solid rgba(174, 174, 174, 0.25);

.sample-wrapper {
overflow-y: auto;
max-height: 450px;
width: 600px;
margin: 8px;
}

igx-expansion-panel-body,
igx-switch {
padding: 16px;
}

igx-expansion-panel {
border: $ep-sample-border;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { <%=ClassName%>Component } from './<%=filePrefix%>.component';
import { IgxAccordionModule, IgxSwitchModule } from '<%=igxPackage%>';

describe('<%=ClassName%>Component', () => {
let component: <%=ClassName%>Component;
let fixture: ComponentFixture<<%=ClassName%>Component>;

beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [<%=ClassName%>Component],
imports: [ NoopAnimationsModule, IgxAccordionModule, IgxSwitchModule ]
})
.compileComponents();
}));

beforeEach(() => {
fixture = TestBed.createComponent(<%=ClassName%>Component);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Component } from '@angular/core';

@Component({
selector: 'app-<%=filePrefix%>',
templateUrl: './<%=filePrefix%>.component.html',
styleUrls: ['./<%=filePrefix%>.component.scss']
})
export class <%=ClassName%>Component {
public singleBranchExpand = false;
}
19 changes: 19 additions & 0 deletions packages/igx-templates/igx-ts-legacy/accordion/default/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { IgniteUIForAngularTemplate } from "../../../IgniteUIForAngularTemplate";

class IgxAccordionTemplate extends IgniteUIForAngularTemplate {
constructor() {
super(__dirname);
this.components = ["Accordion"];
this.controlGroup = "Layouts";
this.listInComponentTemplates = true;
this.id = "accordion";
this.projectType = "igx-ts";
this.name = "Accordion";
this.description = "Basic IgxAccordion sample";
this.dependencies = [{
import: ["IgxAccordionModule", "IgxSwitchModule"],
from: "<%=igxPackage%>"
}];
}
}
module.exports = new IgxAccordionTemplate();
15 changes: 15 additions & 0 deletions packages/igx-templates/igx-ts-legacy/accordion/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

import { BaseComponent } from "@igniteui/cli-core";

class IgxAccordionComponent extends BaseComponent {
/**
*
*/
constructor() {
super(__dirname);
this.name = "Accordion";
this.group = "Layouts";
this.description = `allows users to navigate among multiple collapsible panels displayed in a single container`;
}
}
module.exports = new IgxAccordionComponent();
Loading

0 comments on commit 0b3316a

Please sign in to comment.