Proyecto App Linux

This commit is contained in:
2026-05-03 23:43:09 +02:00
commit 7482733843
1248 changed files with 189750 additions and 0 deletions
+7
View File
@@ -0,0 +1,7 @@
# @iptvnator/pipes
This library was generated with [Nx](https://nx.dev).
## Running unit tests
Run `nx test @iptvnator/pipes` to execute the unit tests.
+34
View File
@@ -0,0 +1,34 @@
import nx from '@nx/eslint-plugin';
import baseConfig from '../../../eslint.config.mjs';
export default [
...baseConfig,
...nx.configs['flat/angular'],
...nx.configs['flat/angular-template'],
{
files: ['**/*.ts'],
rules: {
'@angular-eslint/directive-selector': [
'error',
{
type: 'attribute',
prefix: 'lib',
style: 'camelCase',
},
],
'@angular-eslint/component-selector': [
'error',
{
type: 'element',
prefix: 'lib',
style: 'kebab-case',
},
],
},
},
{
files: ['**/*.html'],
// Override or add rules here
rules: {},
},
];
+21
View File
@@ -0,0 +1,21 @@
export default {
displayName: '@iptvnator/pipes',
preset: '../../../jest.preset.js',
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
coverageDirectory: '../../../coverage/libs/ui/pipes',
transform: {
'^.+\\.(ts|mjs|js|html)$': [
'jest-preset-angular',
{
tsconfig: '<rootDir>/tsconfig.spec.json',
stringifyContentPathRegex: '\\.(html|svg)$',
},
],
},
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
snapshotSerializers: [
'jest-preset-angular/build/serializers/no-ng-attributes',
'jest-preset-angular/build/serializers/ng-snapshot',
'jest-preset-angular/build/serializers/html-comment',
],
};
+28
View File
@@ -0,0 +1,28 @@
{
"name": "@iptvnator/pipes",
"$schema": "../../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "libs/ui/pipes/src",
"prefix": "lib",
"projectType": "library",
"tags": ["scope:shared", "domain:shared-ui", "type:util"],
"targets": {
"test": {
"executor": "nx:run-commands",
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
"options": {
"command": [
"node",
"./tools/testing/run-web-esm-lib-tests.mjs",
"libs/ui/pipes/src"
],
"env": {
"NODE_OPTIONS": "--experimental-vm-modules"
},
"forwardAllArgs": true
}
},
"lint": {
"executor": "@nx/eslint:lint"
}
}
}
+4
View File
@@ -0,0 +1,4 @@
export * from './lib/filter.pipe';
export * from './lib/date-format.util';
export * from './lib/safe.pipe';
export * from './lib/sort.pipe';
@@ -0,0 +1,24 @@
import { formatWithIntl, normalizeDateLocale } from './date-format.util';
describe('date-format.util', () => {
it('normalizes app locale aliases to Angular/Intl locale ids', () => {
expect(normalizeDateLocale('ary')).toBe('ar-MA');
expect(normalizeDateLocale('by')).toBe('be');
expect(normalizeDateLocale('zhtw')).toBe('zh-Hant');
expect(normalizeDateLocale('de')).toBe('de');
expect(normalizeDateLocale()).toBe('en');
});
it('formats dates with cached Intl formatters', () => {
expect(
formatWithIntl('2026-04-05T12:30:00.000Z', {
locale: 'de',
timeZone: 'UTC',
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
})
).toContain('12:30');
});
});
+52
View File
@@ -0,0 +1,52 @@
const DEFAULT_DATE_LOCALE = 'en';
const DATE_LOCALE_ALIASES: Record<string, string> = {
ary: 'ar-MA',
by: 'be',
zhtw: 'zh-Hant',
};
const formatterCache = new Map<string, Intl.DateTimeFormat>();
export function normalizeDateLocale(locale?: string | null): string {
const normalizedLocale = String(locale ?? '').trim();
if (!normalizedLocale) {
return DEFAULT_DATE_LOCALE;
}
return (
DATE_LOCALE_ALIASES[normalizedLocale.toLowerCase()] ?? normalizedLocale
);
}
export function formatWithIntl(
value: Date | number | string,
options: Intl.DateTimeFormatOptions & {
locale?: string | null;
timeZone?: string;
} = {}
): string {
const date =
value instanceof Date
? value
: typeof value === 'number'
? new Date(value)
: new Date(value);
if (Number.isNaN(date.getTime())) {
return '';
}
const { locale, ...formatOptions } = options;
const resolvedLocale = normalizeDateLocale(locale);
const cacheKey = JSON.stringify([resolvedLocale, formatOptions]);
let formatter = formatterCache.get(cacheKey);
if (!formatter) {
formatter = new Intl.DateTimeFormat(resolvedLocale, formatOptions);
formatterCache.set(cacheKey, formatter);
}
return formatter.format(date);
}
+17
View File
@@ -0,0 +1,17 @@
import { Injectable, Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'filterBy',
standalone: true,
})
@Injectable()
export class FilterPipe implements PipeTransform {
transform(array: any[], filter: string, property: string): any {
if (!array || !filter) {
return array;
}
return array.filter((item) =>
item[property].toLowerCase().includes(filter.toLowerCase())
);
}
}
+11
View File
@@ -0,0 +1,11 @@
import { Pipe, PipeTransform } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';
@Pipe({ name: 'safe', standalone: true })
export class SafePipe implements PipeTransform {
constructor(private sanitizer: DomSanitizer) {}
transform(url: string) {
return this.sanitizer.bypassSecurityTrustResourceUrl(url);
}
}
+27
View File
@@ -0,0 +1,27 @@
import { Injectable, Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'sortBy',
standalone: true,
})
@Injectable()
export class SortPipe implements PipeTransform {
transform(array: any[], sortType: string): any {
if (!array || !sortType) {
return array;
}
return array.sort((a, b) => {
if (sortType === 'date') {
return parseInt(b.added) - parseInt(a.added);
} else if (sortType === 'rating') {
const ratingA = isNaN(parseFloat(a.rating)) ? 0 : parseFloat(a.rating);
const ratingB = isNaN(parseFloat(b.rating)) ? 0 : parseFloat(b.rating);
return ratingB - ratingA;
} else if (sortType === 'alpha') {
return a.name.toLowerCase().localeCompare(b.name.toLowerCase());
} else {
return 0;
}
});
}
}
+6
View File
@@ -0,0 +1,6 @@
import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone';
setupZoneTestEnv({
errorOnUnknownElements: true,
errorOnUnknownProperties: true,
});
+30
View File
@@ -0,0 +1,30 @@
{
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
"target": "es2022",
"moduleResolution": "bundler",
"strict": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"module": "preserve"
},
"angularCompilerOptions": {
"enableI18nLegacyMessageIdFormat": false,
"strictInjectionParameters": true,
"strictInputAccessModifiers": true,
"typeCheckHostBindings": true,
"strictTemplates": true
},
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.lib.json"
},
{
"path": "./tsconfig.spec.json"
}
]
}
+17
View File
@@ -0,0 +1,17 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../../dist/out-tsc",
"declaration": true,
"declarationMap": true,
"inlineSources": true,
"types": []
},
"exclude": [
"src/**/*.spec.ts",
"src/test-setup.ts",
"jest.config.ts",
"src/**/*.test.ts"
],
"include": ["src/**/*.ts"]
}
+17
View File
@@ -0,0 +1,17 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../../dist/out-tsc",
"module": "commonjs",
"target": "es2016",
"types": ["jest", "node"],
"moduleResolution": "node10"
},
"files": ["src/test-setup.ts"],
"include": [
"jest.config.ts",
"src/**/*.test.ts",
"src/**/*.spec.ts",
"src/**/*.d.ts"
]
}