Skip to content

Commit

Permalink
feat: cli healthcheck tool (#552)
Browse files Browse the repository at this point in the history
* feat: cli healthcheck

* fix: ensuring prompt works
  • Loading branch information
ashley-hunter authored Jan 23, 2025
1 parent 1aba2fa commit 61f701b
Show file tree
Hide file tree
Showing 24 changed files with 672 additions and 27 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { RouteMeta } from '@analogjs/router';
import { Component } from '@angular/core';
import { CodeComponent } from '@spartan-ng/app/app/shared/code/code.component';
import { MainSectionDirective } from '@spartan-ng/app/app/shared/layout/main-section.directive';
import { PageBottomNavLinkComponent } from '@spartan-ng/app/app/shared/layout/page-bottom-nav/page-bottom-nav-link.component';
import { PageBottomNavComponent } from '@spartan-ng/app/app/shared/layout/page-bottom-nav/page-bottom-nav.component';
Expand Down Expand Up @@ -30,7 +29,6 @@ export const routeMeta: RouteMeta = {
PageBottomNavLinkComponent,
PageNavComponent,
SectionSubHeadingComponent,
CodeComponent,
HlmAspectRatioDirective,
],
template: `
Expand Down Expand Up @@ -70,7 +68,7 @@ export const routeMeta: RouteMeta = {
https://www.figma.com/community/file/1203061493325953101
</a>
<spartan-page-bottom-nav>
<spartan-page-bottom-nav-link href="/components" label="Components" />
<spartan-page-bottom-nav-link href="/health-checks" label="Health Checks" />
<spartan-page-bottom-nav-link direction="previous" href="typography" label="Typography" />
</spartan-page-bottom-nav>
</section>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import type { RouteMeta } from '@analogjs/router';
import { Component } from '@angular/core';
import { MainSectionDirective } from '@spartan-ng/app/app/shared/layout/main-section.directive';
import { PageBottomNavLinkComponent } from '@spartan-ng/app/app/shared/layout/page-bottom-nav/page-bottom-nav-link.component';
import { PageBottomNavComponent } from '@spartan-ng/app/app/shared/layout/page-bottom-nav/page-bottom-nav.component';
import { PageNavComponent } from '@spartan-ng/app/app/shared/layout/page-nav/page-nav.component';
import { SectionIntroComponent } from '@spartan-ng/app/app/shared/layout/section-intro.component';
import { metaWith } from '@spartan-ng/app/app/shared/meta/meta.util';
import { hlmP } from '@spartan-ng/ui-typography-helm';
import { TabsCliComponent } from '../../../../shared/layout/tabs-cli.component';

export const routeMeta: RouteMeta = {
data: { breadcrumb: 'Figma' },
meta: metaWith('spartan - Health Checks', 'Ensure your project is up to date with the latest best practices.'),
title: 'spartan - Health Checks',
};

@Component({
selector: 'spartan-health-checks',
standalone: true,
imports: [
MainSectionDirective,
SectionIntroComponent,
PageBottomNavComponent,
PageBottomNavLinkComponent,
PageNavComponent,
TabsCliComponent,
],
template: `
<section spartanMainSection>
<spartan-section-intro
name="Health Checks"
lead="Ensure your project is up to date with the latest best practices."
/>
<section>
<p class="${hlmP}">
Spartan CLI comes with a health check tool that will scan your project and provide you with a report of any
issues that need to be addressed, and guidance on how to resolve them.
</p>
<p class="${hlmP}">
Additionally, while Spartan is still in its alpha stage we are working hard to make sure our components and
tooling is built with the latest best practices and is structure in a way that will serve us well into the
future. As a result we have been making some changes that may result in breaking changes to your project.
</p>
<p class="${hlmP}">
To help minimize the impact of such changes, in most cases the health check tool will be automatically be able
to fix the issues for you. To run the health check tool simply run the following command:
</p>
<spartan-cli-tabs
class="mb-6 mt-4"
nxCode="npx nx g @spartan-ng/cli:healthcheck"
ngCode="ng g @spartan-ng/cli:healthcheck"
/>
</section>
<spartan-page-bottom-nav>
<spartan-page-bottom-nav-link href="/components" label="Components" />
<spartan-page-bottom-nav-link direction="previous" href="figma" label="Figma" />
</spartan-page-bottom-nav>
</section>
<spartan-page-nav />
`,
})
export default class HealthChecksPageComponent {}
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
import type { RouteMeta } from '@analogjs/router';
import { Component } from '@angular/core';
import { NgIcon, provideIcons } from '@ng-icons/core';
import { provideIcons } from '@ng-icons/core';
import { lucideTriangleAlert } from '@ng-icons/lucide';
import { HlmIconDirective } from '@spartan-ng/ui-icon-helm';
import { hlmCode, hlmH4, hlmP } from '@spartan-ng/ui-typography-helm';
import { CodeComponent } from '../../../shared/code/code.component';
import { ComingSoonComponent } from '../../../shared/layout/coming-soon.component';
import { MainSectionDirective } from '../../../shared/layout/main-section.directive';
import { PageBottomNavLinkComponent } from '../../../shared/layout/page-bottom-nav/page-bottom-nav-link.component';
import { PageBottomNavComponent } from '../../../shared/layout/page-bottom-nav/page-bottom-nav.component';
import { PageNavComponent } from '../../../shared/layout/page-nav/page-nav.component';
import { SectionIntroComponent } from '../../../shared/layout/section-intro.component';
import { SectionSubHeadingComponent } from '../../../shared/layout/section-sub-heading.component';
import { TabsCliComponent } from '../../../shared/layout/tabs-cli.component';
import { TabsComponent } from '../../../shared/layout/tabs.component';
import { metaWith } from '../../../shared/meta/meta.util';

export const routeMeta: RouteMeta = {
Expand All @@ -31,12 +28,8 @@ export const routeMeta: RouteMeta = {
PageBottomNavComponent,
PageBottomNavLinkComponent,
PageNavComponent,
ComingSoonComponent,
SectionSubHeadingComponent,
CodeComponent,
NgIcon,
HlmIconDirective,
TabsComponent,
TabsCliComponent,
],
providers: [provideIcons({ lucideTriangleAlert })],
Expand Down
2 changes: 0 additions & 2 deletions apps/app/src/app/pages/(home).page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { lucideLayers, lucidePuzzle, lucideStar } from '@ng-icons/lucide';
import { HlmBadgeDirective } from '@spartan-ng/ui-badge-helm';
import { HlmButtonDirective } from '@spartan-ng/ui-button-helm';
import {
HlmCardContentDirective,
HlmCardDescriptionDirective,
HlmCardDirective,
HlmCardHeaderDirective,
Expand Down Expand Up @@ -39,7 +38,6 @@ const lead = 'leading-normal text-muted-foreground sm:text-xl sm:leading-8';
HlmCardHeaderDirective,
HlmCardTitleDirective,
HlmCardDescriptionDirective,
HlmCardContentDirective,
NgIcon,
HlmIconDirective,
ThreeHundredComponent,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ export class SideNavContentComponent {
{ label: 'Dark Mode', url: '/dark-mode' },
{ label: 'Typography', url: '/typography' },
{ label: 'Figma', url: '/figma' },
{ label: 'Health Checks', url: '/health-checks' },
],
},
{
Expand Down
10 changes: 10 additions & 0 deletions libs/cli/generators.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@
"factory": "./src/generators/migrate-core/generator",
"schema": "./src/generators/migrate-core/schema.json",
"description": "Migrate core library to brain core entrypoing"
},
"healthcheck": {
"factory": "./src/generators/healthcheck/generator",
"schema": "./src/generators/healthcheck/schema.json",
"description": "Run a healthcheck on the project to identify any potential issues or outdated code."
}
},
"schematics": {
Expand Down Expand Up @@ -61,6 +66,11 @@
"factory": "./src/generators/migrate-scroll-area/compat",
"schema": "./src/generators/migrate-core/schema.json",
"description": "Migrate core library to brain core entrypoing"
},
"healthcheck": {
"factory": "./src/generators/healthcheck/compat",
"schema": "./src/generators/healthcheck/schema.json",
"description": "Run a healthcheck on the project to identify any potential issues or outdated code."
}
}
}
1 change: 1 addition & 0 deletions libs/cli/jest.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export default {
displayName: 'cli',
preset: '../../jest.preset.cjs',
testEnvironment: 'node',
transform: {
'^.+\\.[tj]s$': ['ts-jest', { tsconfig: '<rootDir>/tsconfig.spec.json' }],
},
Expand Down
1 change: 1 addition & 0 deletions libs/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"enquirer": "2.3.6",
"jsonc-eslint-parser": "^2.1.0",
"nx": ">=20.0.0",
"picocolors": "^1.1.1",
"semver": "7.5.4",
"typescript": ">=5.0.0"
},
Expand Down
4 changes: 4 additions & 0 deletions libs/cli/src/generators/healthcheck/compat.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { convertNxGenerator } from '@nx/devkit';
import { healthcheckGenerator } from './generator';

export default convertNxGenerator(healthcheckGenerator);
80 changes: 80 additions & 0 deletions libs/cli/src/generators/healthcheck/generator.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { readJson, Tree, writeJson } from '@nx/devkit';
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
import { healthcheckGenerator } from './generator';

describe('healthcheck generator', () => {
let tree: Tree;

beforeEach(async () => {
tree = createTreeWithEmptyWorkspace();

writeJson(tree, 'package.json', {
dependencies: {
'@spartan-ng/brain': '0.0.1-alpha.300',
'@spartan-ng/ui-checkbox-brain': '0.0.1-alpha.300',
},
devDependencies: {
'@spartan-ng/cli': '0.0.1-alpha.300',
},
});

// add a file with legacy imports
tree.write(
'libs/my-lib/src/index.ts',
`
import { BrnCheckbox } from '@spartan-ng/ui-checkbox-brain';
import { hlm } from '@spartan-ng/ui-core';
`,
);

// add a file with a helm icon
tree.write(
'libs/my-lib/src/app.component.html',
`
<hlm-icon />
<hlm-scroll-area />
`,
);

await healthcheckGenerator(tree, { skipFormat: true, autoFix: true });
});

it('should update to latest dependencies', () => {
const packageJson = readJson(tree, 'package.json');

expect(packageJson.dependencies['@spartan-ng/brain']).not.toEqual('0.0.1-alpha.300');
expect(packageJson.devDependencies['@spartan-ng/cli']).not.toEqual('0.0.1-alpha.300');
});

it('should update brain imports', () => {
const contents = tree.read('libs/my-lib/src/index.ts', 'utf-8');

expect(contents).not.toContain('@spartan-ng/ui-checkbox-brain');
expect(contents).toContain('@spartan-ng/brain/checkbox');

// check if package.json was updated
const packageJson = readJson(tree, 'package.json');
expect(packageJson.dependencies['@spartan-ng/ui-checkbox-brain']).toBeUndefined();
});

it('should update core imports', () => {
const contents = tree.read('libs/my-lib/src/index.ts', 'utf-8');

expect(contents).not.toContain('@spartan-ng/ui-core');
expect(contents).toContain('@spartan-ng/brain/core');
});

it('should update helm icons', () => {
const contents = tree.read('libs/my-lib/src/app.component.html', 'utf-8');

expect(contents).not.toContain('<hlm-icon');
expect(contents).toContain('<ng-icon hlm');
});

it('should update helm scroll areas', () => {
const contents = tree.read('libs/my-lib/src/app.component.html', 'utf-8');

expect(contents).not.toContain('<hlm-scroll-area');
expect(contents).toContain('<ng-scrollbar hlm');
});
});
52 changes: 52 additions & 0 deletions libs/cli/src/generators/healthcheck/generator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { formatFiles, logger, Tree } from '@nx/devkit';
import { Healthcheck, HealthcheckReport, HealthcheckStatus, isHealthcheckFixable } from './healthchecks';
import { brainImportsHealthcheck } from './healthchecks/brain-imports';
import { coreImportsHealthcheck } from './healthchecks/core-imports';
import { helmIconHealthcheck } from './healthchecks/hlm-icon';
import { scrollAreaHealthcheck } from './healthchecks/hlm-scroll-area';
import { versionHealthcheck } from './healthchecks/version';
import { HealthcheckGeneratorSchema } from './schema';
import { promptUser } from './utils/prompt';
import { printReport } from './utils/reporter';
import { runHealthcheck } from './utils/runner';

export async function healthcheckGenerator(tree: Tree, options: HealthcheckGeneratorSchema) {
logger.info('Running healthchecks...');

const healthchecks: Healthcheck[] = [
versionHealthcheck,
brainImportsHealthcheck,
coreImportsHealthcheck,
helmIconHealthcheck,
scrollAreaHealthcheck,
];

// store all the failed healthchecks that can be fixed
const failedReports: HealthcheckReport[] = [];

for (const healthcheck of healthchecks) {
const report = await runHealthcheck(tree, healthcheck);
printReport(report);

if (report.status === HealthcheckStatus.Failure) {
failedReports.push(report);
}
}

// if there are some failed healthchecks that can be fixed, ask the user if they want to fix them
for (const report of failedReports) {
if (report.fixable && isHealthcheckFixable(report.healthcheck)) {
const fix = options.autoFix || (await promptUser(report.healthcheck.prompt));

if (fix) {
await report.healthcheck.fix(tree);
}
}
}

if (!options.skipFormat) {
await formatFiles(tree);
}
}

export default healthcheckGenerator;
Loading

0 comments on commit 61f701b

Please sign in to comment.