mirror of
https://github.com/tiennm99/serena.git
synced 2026-10-14 10:19:02 +00:00
feat: Add Angular, HTML, and SCSS / Sass language servers (#1439)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Pavlo Basanets <Pavlo_Basanets@epam.com>
This commit is contained in:
53 files changed
+3280
-22
No files matched your search
@@ -0,0 +1,5 @@
|
||||
node_modules
|
||||
.angular
|
||||
dist
|
||||
out-tsc
|
||||
.angular-install.lock
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||
"version": 1,
|
||||
"newProjectRoot": "projects",
|
||||
"projects": {
|
||||
"serena-test-app": {
|
||||
"projectType": "application",
|
||||
"root": "",
|
||||
"sourceRoot": "src",
|
||||
"prefix": "app",
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:application",
|
||||
"options": {
|
||||
"outputPath": "dist/serena-test-app",
|
||||
"index": "src/index.html",
|
||||
"browser": "src/main.ts",
|
||||
"tsConfig": "tsconfig.app.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
{
|
||||
"name": "serena-angular-test-repo",
|
||||
"version": "0.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "serena-angular-test-repo",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@angular/common": "^21.0.0",
|
||||
"@angular/compiler": "^21.0.0",
|
||||
"@angular/core": "^21.0.0",
|
||||
"@angular/platform-browser": "^21.0.0",
|
||||
"rxjs": "~7.8.0",
|
||||
"tslib": "^2.6.0",
|
||||
"zone.js": "~0.15.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "~5.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular/common": {
|
||||
"version": "21.2.10",
|
||||
"resolved": "https://registry.npmjs.org/@angular/common/-/common-21.2.10.tgz",
|
||||
"integrity": "sha512-WLyi/CRLtgALg2mmaqIuKuPnE4i+8PGt/uuz26pVqx+ASh28/TWr5KSCAMomgxEc8kt4OE7lopoQsTihrQCfEw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/core": "21.2.10",
|
||||
"rxjs": "^6.5.3 || ^7.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular/compiler": {
|
||||
"version": "21.2.10",
|
||||
"resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-21.2.10.tgz",
|
||||
"integrity": "sha512-IrgdFuzzD7NTK3WQaSfowjAPxPbnTqsgR92NsOs5ZaWu3RgLl21dHThNc0BK1KwVwppLUSWmD4qePbcLW71VzQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular/core": {
|
||||
"version": "21.2.10",
|
||||
"resolved": "https://registry.npmjs.org/@angular/core/-/core-21.2.10.tgz",
|
||||
"integrity": "sha512-uxH+mbPiCE7rInWKYOPe9Ytas97+mFM6FhFORoN234yBK3b8he+iDuxX6dsbhEFCxhRmfS6hLxe7BdLY6U6kIA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/compiler": "21.2.10",
|
||||
"rxjs": "^6.5.3 || ^7.4.0",
|
||||
"zone.js": "~0.15.0 || ~0.16.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@angular/compiler": {
|
||||
"optional": true
|
||||
},
|
||||
"zone.js": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@angular/platform-browser": {
|
||||
"version": "21.2.10",
|
||||
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-21.2.10.tgz",
|
||||
"integrity": "sha512-5WMoHGU8BOV3eO9h3vGMIUDPf+3SHis7+X2dHKMtKfFBUtiO8m/lq2x3PzkkKj1782i7KYt92EqPHuADd/eWOw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/animations": "21.2.10",
|
||||
"@angular/common": "21.2.10",
|
||||
"@angular/core": "21.2.10"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@angular/animations": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/rxjs": {
|
||||
"version": "7.8.2",
|
||||
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz",
|
||||
"integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"tslib": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.9.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/zone.js": {
|
||||
"version": "0.15.1",
|
||||
"resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.15.1.tgz",
|
||||
"integrity": "sha512-XE96n56IQpJM7NAoXswY3XRLcWFW83xe0BiAOeMD7K5k5xecOeul3Qcpx6GqEeeHNkW5DWL5zOyTbEfB4eti8w==",
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "serena-angular-test-repo",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"description": "Minimal Angular workspace fixture used by Serena language-server tests.",
|
||||
"dependencies": {
|
||||
"@angular/common": "^21.0.0",
|
||||
"@angular/compiler": "^21.0.0",
|
||||
"@angular/core": "^21.0.0",
|
||||
"@angular/platform-browser": "^21.0.0",
|
||||
"rxjs": "~7.8.0",
|
||||
"tslib": "^2.6.0",
|
||||
"zone.js": "~0.15.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "~5.9.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<section>
|
||||
<h1>{{ title() | exclaim }}</h1>
|
||||
<p>{{ greeting() }}</p>
|
||||
|
||||
<input
|
||||
type="text"
|
||||
[value]="userName()"
|
||||
(input)="setName($any($event.target).value)"
|
||||
placeholder="Enter your name"
|
||||
/>
|
||||
|
||||
@if (items().length > 0) {
|
||||
<ul>
|
||||
@for (item of items(); track item) {
|
||||
<app-item-card [label]="item"></app-item-card>
|
||||
}
|
||||
</ul>
|
||||
} @else {
|
||||
<p>No items.</p>
|
||||
}
|
||||
</section>
|
||||
@@ -0,0 +1,26 @@
|
||||
import { Component, signal } from '@angular/core';
|
||||
import { GreetingService } from './greeting.service';
|
||||
import { ItemCardComponent } from './item-card.component';
|
||||
import { ExclaimPipe } from './exclaim.pipe';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
standalone: true,
|
||||
imports: [ItemCardComponent, ExclaimPipe],
|
||||
templateUrl: './app.component.html',
|
||||
})
|
||||
export class AppComponent {
|
||||
readonly title = signal('Serena Angular Test');
|
||||
readonly userName = signal('');
|
||||
readonly items = signal<string[]>(['alpha', 'beta', 'gamma']);
|
||||
|
||||
constructor(private readonly greetings: GreetingService) {}
|
||||
|
||||
greeting(): string {
|
||||
return this.greetings.greet(this.userName() || undefined);
|
||||
}
|
||||
|
||||
setName(name: string): void {
|
||||
this.userName.set(name);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
<!-- Template path: ngserver flags the reference to ``undefinedSignal()`` since
|
||||
it has no matching field on DiagnosticsSampleComponent. The standalone .html
|
||||
diagnostic exercises the Angular template compiler, not plain-HTML validation. -->
|
||||
<section>
|
||||
<h1>{{ undefinedSignal() }}</h1>
|
||||
</section>
|
||||
@@ -0,0 +1,15 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
// Two diagnostic sources in one fixture:
|
||||
// * the type error on ``count`` is reported by tsserver via the Angular plugin
|
||||
// when diagnosing this .ts file directly.
|
||||
// * the ``undefinedSignal()`` reference in diagnostics_sample.html is reported
|
||||
// by ngserver when diagnosing the template attached via ``templateUrl``.
|
||||
@Component({
|
||||
selector: 'app-diagnostics-sample',
|
||||
standalone: true,
|
||||
templateUrl: './diagnostics_sample.html',
|
||||
})
|
||||
export class DiagnosticsSampleComponent {
|
||||
readonly count: number = 'not-a-number';
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
|
||||
@Pipe({
|
||||
name: 'exclaim',
|
||||
standalone: true,
|
||||
})
|
||||
export class ExclaimPipe implements PipeTransform {
|
||||
transform(value: string, count: number = 1): string {
|
||||
return value + '!'.repeat(count);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export interface Greeter {
|
||||
greet(name?: string): string;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Greeter } from './greeter.interface';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class GreetingService implements Greeter {
|
||||
private readonly defaultName = 'World';
|
||||
|
||||
greet(name?: string): string {
|
||||
return `Hello, ${name ?? this.defaultName}!`;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-item-card',
|
||||
standalone: true,
|
||||
template: `
|
||||
<li class="item-card" [attr.data-label]="label">
|
||||
<strong>{{ label }}</strong>
|
||||
</li>
|
||||
`,
|
||||
})
|
||||
export class ItemCardComponent {
|
||||
@Input() label: string = '';
|
||||
|
||||
formatLabel(): string {
|
||||
return this.label.trim();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Serena Angular Test</title>
|
||||
</head>
|
||||
<body>
|
||||
<app-root></app-root>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,4 @@
|
||||
import { bootstrapApplication } from '@angular/platform-browser';
|
||||
import { AppComponent } from './app/app.component';
|
||||
|
||||
bootstrapApplication(AppComponent).catch((err) => console.error(err));
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./out-tsc/app",
|
||||
"types": []
|
||||
},
|
||||
"files": ["src/main.ts"],
|
||||
"include": ["src/**/*.d.ts", "src/**/*.ts", "src/**/*.html"]
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"compileOnSave": false,
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist/out-tsc",
|
||||
"strict": true,
|
||||
"noImplicitOverride": true,
|
||||
"noPropertyAccessFromIndexSignature": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"skipLibCheck": true,
|
||||
"isolatedModules": true,
|
||||
"esModuleInterop": true,
|
||||
"experimentalDecorators": true,
|
||||
"moduleResolution": "bundler",
|
||||
"importHelpers": true,
|
||||
"target": "ES2022",
|
||||
"module": "ES2022"
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"enableI18nLegacyMessageIdFormat": false,
|
||||
"strictInjectionParameters": true,
|
||||
"strictInputAccessModifiers": true,
|
||||
"strictTemplates": true
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user