Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
[![npm version](https://img.shields.io/npm/v/@framework-doctor/cli.svg)](https://www.npmjs.com/package/@framework-doctor/cli)
[![npm downloads](https://img.shields.io/npm/dm/@framework-doctor/cli.svg)](https://www.npmjs.com/package/@framework-doctor/cli)

Framework Doctor auto-detects your framework and runs the right health check. Supports **Svelte**, **React**, and **Vue**; Angular coming soon.
Framework Doctor auto-detects your framework and runs the right health check. Supports **Svelte**, **React**, **Vue**, and **Angular**.

## Quick start

Expand All @@ -19,6 +19,7 @@ Or run a specific doctor directly:
npx -y @framework-doctor/react . # React
npx -y @framework-doctor/svelte . # Svelte
npx -y @framework-doctor/vue . # Vue
npx -y @framework-doctor/angular . # Angular
```

## Try it
Expand Down Expand Up @@ -66,6 +67,15 @@ See [examples/README.md](examples/README.md) for more demo projects and commands
- `npx -y @framework-doctor/svelte . --diff main` - scan only files changed against `main`.
- `npx -y @framework-doctor/svelte . --project web` - select a specific workspace package.

**Angular (direct):**

- `npx -y @framework-doctor/angular .` - run a full scan
- `npx -y @framework-doctor/angular ./path/to/project` - scan a specific project directory
- `npx -y @framework-doctor/angular . --verbose` - include file and line details
- `npx -y @framework-doctor/angular . --score` - print only the numeric score (CI-friendly)
- `npx -y @framework-doctor/angular . --diff main` - scan only files changed against `main`
- `npx -y @framework-doctor/angular . --project my-app` - select a specific workspace project

## Options

Svelte doctor:
Expand All @@ -90,6 +100,8 @@ Options:

React doctor options: `--no-lint`, `--no-dead-code`, `--verbose`, `--score`, `--no-analytics`, `--project`, `--diff`, `--offline`. See [packages/react-doctor/README.md](packages/react-doctor/README.md).

Angular doctor options: `--no-lint`, `--no-dead-code`, `--verbose`, `--score`, `--no-analytics`, `--project`, `--diff`, `--offline`. See [packages/angular-doctor/README.md](packages/angular-doctor/README.md).

## Security checks

Svelte Doctor includes a security scan that flags:
Expand All @@ -104,7 +116,7 @@ To ignore a rule: `"svelte-doctor/no-at-html"`, `"svelte-doctor/no-new-function"

## Analytics

Both doctors optionally send anonymous usage data when you opt in. Data is stored in your Supabase (see [supabase/README.md](supabase/README.md)). If your function enforces `TELEMETRY_KEY`, set `FRAMEWORK_DOCTOR_TELEMETRY_KEY` in the client environment. To disable: `--no-analytics`, `"analytics": false` in config, or `DO_NOT_TRACK=1`.
The doctors optionally send anonymous usage data when you opt in. Data is stored in your Supabase (see [supabase/README.md](supabase/README.md)). If your function enforces `TELEMETRY_KEY`, set `FRAMEWORK_DOCTOR_TELEMETRY_KEY` in the client environment. To disable: `--no-analytics`, `"analytics": false` in config, or `DO_NOT_TRACK=1`.

## Configuration

Expand Down
8 changes: 4 additions & 4 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@ runs:
INPUT_VERBOSE: ${{ inputs.verbose }}
INPUT_PROJECT: ${{ inputs.project }}
run: |
FLAGS=""
if [ "$INPUT_VERBOSE" = "true" ]; then FLAGS="$FLAGS --verbose"; fi
if [ -n "$INPUT_PROJECT" ]; then FLAGS="$FLAGS --project $INPUT_PROJECT"; fi
npx -y @framework-doctor/svelte "$INPUT_DIRECTORY" $FLAGS
ARGS=("$INPUT_DIRECTORY")
if [ "$INPUT_VERBOSE" = "true" ]; then ARGS+=(--verbose); fi
if [ -n "$INPUT_PROJECT" ]; then ARGS+=(--project "$INPUT_PROJECT"); fi
npx -y @framework-doctor/svelte "${ARGS[@]}"

- id: score
shell: bash
Expand Down
48 changes: 48 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,51 @@ npx -y @framework-doctor/cli examples/svelte/demo-app
- **Score** — A 0–100 health score for the project

Add `--verbose` to see file and line details for each finding.

## Vue: demo-app

A minimal Vue 3 + Vite app with intentional issues. See [vue/demo-app/README.md](vue/demo-app/README.md) for details.

### Run from the repo

```bash
pnpm build
pnpm exec framework-doctor examples/vue/demo-app
# Or: pnpm exec vue-doctor examples/vue/demo-app
```

### Run with npx

```bash
npx -y @framework-doctor/cli examples/vue/demo-app
```

### What to expect

- **Errors** — Security (v-html, eval, new Function, implied eval), accessibility
- **Warnings** — Dead code, lint issues
- **Score** — 0–100 health score

## Angular: demo-app

A minimal Angular app with intentional issues. See [angular/demo-app/README.md](angular/demo-app/README.md) for details.

### Run from the repo

```bash
pnpm build
pnpm exec framework-doctor examples/angular/demo-app
# Or: pnpm exec angular-doctor examples/angular/demo-app
```

### Run with npx

```bash
npx -y @framework-doctor/cli examples/angular/demo-app
```

### What to expect

- **Errors** — Security (eval, innerHTML, bypassSecurityTrust*)
- **Warnings** — Dead code, lint issues
- **Score** — 0–100 health score
17 changes: 17 additions & 0 deletions examples/angular/demo-app/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Editor configuration, see https://editorconfig.org
root = true

[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true

[*.ts]
quote_type = single
ij_typescript_use_double_quotes = false

[*.md]
max_line_length = off
trim_trailing_whitespace = false
42 changes: 42 additions & 0 deletions examples/angular/demo-app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files.

# Compiled output
/dist
/tmp
/out-tsc
/bazel-out

# Node
/node_modules
npm-debug.log
yarn-error.log

# IDEs and editors
.idea/
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace

# Visual Studio Code
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*

# Miscellaneous
/.angular/cache
.sass-cache/
/connect.lock
/coverage
/libpeerconnection.log
testem.log
/typings

# System files
.DS_Store
Thumbs.db
29 changes: 29 additions & 0 deletions examples/angular/demo-app/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Framework Doctor Angular Demo

A minimal Angular app with **intentional issues** for testing [Framework Doctor](https://github.com/pitis/framework-doctor).

## Run the doctor

From the framework-doctor repo root (after `pnpm install` and `pnpm build`):

```bash
pnpm exec framework-doctor examples/angular/demo-app
# or directly:
pnpm exec angular-doctor examples/angular/demo-app
```

## Intentional issues

- **Security** — `eval()`, `bypassSecurityTrustHtml()` in `src/lib/security-test.ts`
- **Dead code** — Unused exports (if present)
- **Lint** — Various ESLint / Angular ESLint findings

## Develop

This project was generated with [Angular CLI](https://github.com/angular/angular-cli). To run the dev server:

```bash
ng serve
```

Open `http://localhost:4200/`. For code scaffolding, build, and tests see the [Angular CLI Overview](https://angular.dev/tools/cli).
57 changes: 57 additions & 0 deletions examples/angular/demo-app/angular.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"demo-app": {
"projectType": "application",
"schematics": {
"@schematics/angular:component": {
"style": "css",
"standalone": true
}
},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:application",
"options": {
"outputPath": "dist/demo-app",
"index": "src/index.html",
"browser": "src/main.ts",
"polyfills": ["zone.js"],
"tsConfig": "tsconfig.app.json",
"assets": ["src/favicon.ico", "src/assets"],
"styles": ["src/styles.css"],
"scripts": []
},
"configurations": {
"production": {
"budgets": [
{ "type": "initial", "maximumWarning": "500kb", "maximumError": "1mb" },
{ "type": "anyComponentStyle", "maximumWarning": "2kb", "maximumError": "4kb" }
],
"outputHashing": "all"
},
"development": {
"optimization": false,
"extractLicenses": false,
"sourceMap": true
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": { "buildTarget": "demo-app:build:production" },
"development": { "buildTarget": "demo-app:build:development" }
},
"defaultConfiguration": "development"
}
}
}
}
}
33 changes: 33 additions & 0 deletions examples/angular/demo-app/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"name": "framework-doctor-angular-demo",
"version": "1.0.0",
"private": true,
"description": "Demo Angular app for Framework Doctor - includes intentional issues",
"type": "module",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"watch": "ng build --watch"
},
"dependencies": {
"@angular/animations": "^19.0.0",
"@angular/common": "^19.0.0",
"@angular/compiler": "^19.0.0",
"@angular/core": "^19.0.0",
"@angular/forms": "^19.0.0",
"@angular/platform-browser": "^19.0.0",
"@angular/platform-browser-dynamic": "^19.0.0",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "~0.15.0"
},
"devDependencies": {
"@angular-devkit/build-angular": "^19.0.0",
"@angular/cli": "^19.0.0",
"@angular/compiler-cli": "^19.0.0",
"@types/node": "^20.0.0",
"typescript": "~5.6.0"
},
"packageManager": "pnpm@10.30.0"
}
19 changes: 19 additions & 0 deletions examples/angular/demo-app/src/app/app.component.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
.container {
max-width: 800px;
margin: 0 auto;
padding: 2rem;
font-family: system-ui, sans-serif;
}

.example {
margin: 1rem 0;
padding: 1rem;
border: 1px solid #e0e0e0;
border-radius: 8px;
}

hr {
margin: 2rem 0;
border: none;
border-top: 1px solid #e0e0e0;
}
17 changes: 17 additions & 0 deletions examples/angular/demo-app/src/app/app.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<div class="container">
<h1>Framework Doctor Angular Demo</h1>
<p>A minimal Angular app with intentional issues for angular-doctor testing.</p>

<div class="example">
<button type="button" (click)="toggleDarkMode()">
{{ darkMode ? '☀️ Light' : '🌙 Dark' }}
</button>
</div>

<hr />
<h2>angular-doctor test section (intentional issues)</h2>
<div class="example">
<app-doctor-test label="Test" />
<div [innerHTML]="userRenderedContent"></div>
</div>
</div>
18 changes: 18 additions & 0 deletions examples/angular/demo-app/src/app/app.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Component } from '@angular/core';
import { DoctorTestComponent } from './doctor-test.component';

@Component({
selector: 'app-root',
standalone: true,
imports: [DoctorTestComponent],
templateUrl: './app.component.html',
styleUrl: './app.component.css',
})
export default class AppComponent {
darkMode = false;
userRenderedContent = '<img src="invalid:" onerror="alert(1)">';

toggleDarkMode(): void {
this.darkMode = !this.darkMode;
}
}
5 changes: 5 additions & 0 deletions examples/angular/demo-app/src/app/app.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';

export const appConfig: ApplicationConfig = {
providers: [provideZoneChangeDetection({ eventCoalescing: true })],
};
10 changes: 10 additions & 0 deletions examples/angular/demo-app/src/app/doctor-test.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Component, input } from '@angular/core';

@Component({
selector: 'app-doctor-test',
standalone: true,
template: `<button type="button">{{ label() }}</button>`,
})
export class DoctorTestComponent {
label = input.required<string>();
}
1 change: 1 addition & 0 deletions examples/angular/demo-app/src/favicon.ico
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
placeholder
13 changes: 13 additions & 0 deletions examples/angular/demo-app/src/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Framework Doctor Angular Demo</title>
<base href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" type="image/x-icon" href="favicon.ico" />
</head>
<body>
<app-root></app-root>
</body>
</html>
8 changes: 8 additions & 0 deletions examples/angular/demo-app/src/lib/orphan-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* INTENTIONAL: Unused file for angular-doctor testing.
* This file is not imported anywhere - knip will report it as unused.
*/

export const ORPHAN_CONSTANT = 42;

export const orphanHelper = (value: number): number => value * 2;
Loading