Keep Chart.js and the whole of lodash out of apps' initial bundle
Each chart registers what it needs when it is built instead of on import, and lodash is imported one function at a time. In TaskStream the initial bundle drops from 2.13 MB to 1.90 MB. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -184,11 +184,21 @@ Detalles a tener en cuenta:
|
|||||||
- **`Dataset.cubicInterpolationMode` pasa a opcional.** Solo tiene sentido en
|
- **`Dataset.cubicInterpolationMode` pasa a opcional.** Solo tiene sentido en
|
||||||
`line`; forzarlo en `bar`/`pie`/`doughnut` no aportaba nada y esos tipos ni
|
`line`; forzarlo en `bar`/`pie`/`doughnut` no aportaba nada y esos tipos ni
|
||||||
siquiera usan `Dataset` (los dos últimos usan `ChartSlice`).
|
siquiera usan `Dataset` (los dos últimos usan `ChartSlice`).
|
||||||
|
- **Cada gráfico registra lo suyo en `buildChart()`, no al importar.** El
|
||||||
|
paquete es un único `fesm`, así que un `Chart.register(...)` a nivel de
|
||||||
|
módulo se ejecutaba en cualquier app que importase `@pi/ui` y metía Chart.js
|
||||||
|
entero en su bundle inicial, pintase gráficos o no (en TaskStream, unos
|
||||||
|
230 kB). Registrar dos veces lo mismo no hace nada. Si se añade un tipo de
|
||||||
|
gráfico, su registro va ahí también.
|
||||||
- **El `type="bar"` del `pi-chart` original nunca funcionó**: el input
|
- **El `type="bar"` del `pi-chart` original nunca funcionó**: el input
|
||||||
existía pero `getLineChartConfigs()` construía siempre un chart `line`
|
existía pero `getLineChartConfigs()` construía siempre un chart `line`
|
||||||
ignorando su valor. Se descubrió al dividir los componentes; `pi-bar-chart`
|
ignorando su valor. Se descubrió al dividir los componentes; `pi-bar-chart`
|
||||||
es la primera versión que realmente renderiza barras.
|
es la primera versión que realmente renderiza barras.
|
||||||
|
|
||||||
|
**`lodash` se importa por función** (`lodash/isEqual`, `lodash/some`,
|
||||||
|
`lodash/capitalize`), nunca `import _ from 'lodash'`: lodash es CommonJS y el
|
||||||
|
import completo mete la librería entera en el bundle de quien use un select.
|
||||||
|
|
||||||
**El loader de servidor de traducciones ya no está en el barrel.**
|
**El loader de servidor de traducciones ya no está en el barrel.**
|
||||||
`util/translate/loaders/translate-server.loader` usa `fs`, `path` y `process`;
|
`util/translate/loaders/translate-server.loader` usa `fs`, `path` y `process`;
|
||||||
exportarlo desde `@pi/ui` rompía el bundle de navegador de cualquier app que
|
exportarlo desde `@pi/ui` rompía el bundle de navegador de cualquier app que
|
||||||
|
|||||||
@@ -12,8 +12,6 @@ import { barDatasetConfig } from '../chart.configs';
|
|||||||
import { PiBaseCartesianChartComponent } from '../base-cartesian-chart.component';
|
import { PiBaseCartesianChartComponent } from '../base-cartesian-chart.component';
|
||||||
import { PiChartLegendComponent } from '../chart-legend/chart-legend.component';
|
import { PiChartLegendComponent } from '../chart-legend/chart-legend.component';
|
||||||
|
|
||||||
Chart.register(CategoryScale, LinearScale, BarController, BarElement, Tooltip, Legend);
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'pi-bar-chart',
|
selector: 'pi-bar-chart',
|
||||||
templateUrl: './bar-chart.component.html',
|
templateUrl: './bar-chart.component.html',
|
||||||
@@ -30,6 +28,9 @@ export class PiBarChartComponent extends PiBaseCartesianChartComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected buildChart(): Chart {
|
protected buildChart(): Chart {
|
||||||
|
// Aquí y no al importar: así una app que nunca pinta este gráfico no
|
||||||
|
// arrastra Chart.js. Registrar dos veces no hace nada.
|
||||||
|
Chart.register(CategoryScale, LinearScale, BarController, BarElement, Tooltip, Legend);
|
||||||
return new Chart(this.chartRef().nativeElement, {
|
return new Chart(this.chartRef().nativeElement, {
|
||||||
type: 'bar',
|
type: 'bar',
|
||||||
data: {
|
data: {
|
||||||
|
|||||||
@@ -15,9 +15,10 @@ import { Device } from '../../util/device-configs';
|
|||||||
* `pi-pie-chart`, `pi-doughnut-chart`): el `<canvas>`, el ciclo de vida que
|
* `pi-pie-chart`, `pi-doughnut-chart`): el `<canvas>`, el ciclo de vida que
|
||||||
* construye el `Chart` una vez la vista está lista, y el `TranslateService`
|
* construye el `Chart` una vez la vista está lista, y el `TranslateService`
|
||||||
* que todos usan para las etiquetas. Cada tipo registra solo los
|
* que todos usan para las etiquetas. Cada tipo registra solo los
|
||||||
* controladores/elementos de Chart.js que necesita — no hay un registro
|
* controladores/elementos de Chart.js que necesita, y lo hace en
|
||||||
* global compartido, así que un consumidor que solo importe `pi-pie-chart`
|
* `buildChart`, no al importar el módulo: el paquete es un único `fesm`, así
|
||||||
* no arrastra el código de líneas/barras.
|
* que un `Chart.register` a nivel de módulo metería Chart.js entero en el
|
||||||
|
* bundle inicial de cualquier app que use `@pi/ui`, pinte gráficos o no.
|
||||||
*/
|
*/
|
||||||
@Component({ template: '' })
|
@Component({ template: '' })
|
||||||
export abstract class PiBaseChartComponent implements AfterViewInit {
|
export abstract class PiBaseChartComponent implements AfterViewInit {
|
||||||
|
|||||||
@@ -8,8 +8,6 @@ import {
|
|||||||
} from 'chart.js';
|
} from 'chart.js';
|
||||||
import { PiBaseArcChartComponent } from '../base-arc-chart.component';
|
import { PiBaseArcChartComponent } from '../base-arc-chart.component';
|
||||||
|
|
||||||
Chart.register(ArcElement, DoughnutController, Tooltip, Legend);
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'pi-doughnut-chart',
|
selector: 'pi-doughnut-chart',
|
||||||
templateUrl: './doughnut-chart.component.html',
|
templateUrl: './doughnut-chart.component.html',
|
||||||
@@ -18,6 +16,9 @@ Chart.register(ArcElement, DoughnutController, Tooltip, Legend);
|
|||||||
})
|
})
|
||||||
export class PiDoughnutChartComponent extends PiBaseArcChartComponent {
|
export class PiDoughnutChartComponent extends PiBaseArcChartComponent {
|
||||||
protected buildChart(): Chart {
|
protected buildChart(): Chart {
|
||||||
|
// Aquí y no al importar: así una app que nunca pinta este gráfico no
|
||||||
|
// arrastra Chart.js. Registrar dos veces no hace nada.
|
||||||
|
Chart.register(ArcElement, DoughnutController, Tooltip, Legend);
|
||||||
return new Chart(this.chartRef().nativeElement, {
|
return new Chart(this.chartRef().nativeElement, {
|
||||||
type: 'doughnut',
|
type: 'doughnut',
|
||||||
data: this.getArcData<'doughnut'>(),
|
data: this.getArcData<'doughnut'>(),
|
||||||
|
|||||||
@@ -8,8 +8,6 @@ import { ArcElement, Chart, DoughnutController } from 'chart.js';
|
|||||||
import { DatasetColor } from '../../../util/models';
|
import { DatasetColor } from '../../../util/models';
|
||||||
import { PiBaseChartComponent } from '../base-chart.component';
|
import { PiBaseChartComponent } from '../base-chart.component';
|
||||||
|
|
||||||
Chart.register(ArcElement, DoughnutController);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Un único valor sobre `max`, no una serie ni porciones — por eso extiende
|
* Un único valor sobre `max`, no una serie ni porciones — por eso extiende
|
||||||
* directamente `PiBaseChartComponent` (la base raíz) en vez de
|
* directamente `PiBaseChartComponent` (la base raíz) en vez de
|
||||||
@@ -36,6 +34,9 @@ export class PiGaugeChartComponent extends PiBaseChartComponent {
|
|||||||
);
|
);
|
||||||
|
|
||||||
protected buildChart(): Chart {
|
protected buildChart(): Chart {
|
||||||
|
// Aquí y no al importar: así una app que nunca pinta este gráfico no
|
||||||
|
// arrastra Chart.js. Registrar dos veces no hace nada.
|
||||||
|
Chart.register(ArcElement, DoughnutController);
|
||||||
const clamped = this.clampedValue();
|
const clamped = this.clampedValue();
|
||||||
return new Chart(this.chartRef().nativeElement, {
|
return new Chart(this.chartRef().nativeElement, {
|
||||||
type: 'doughnut',
|
type: 'doughnut',
|
||||||
|
|||||||
@@ -14,17 +14,6 @@ import { hexToRgba, lineDatasetConfig } from '../chart.configs';
|
|||||||
import { PiBaseCartesianChartComponent } from '../base-cartesian-chart.component';
|
import { PiBaseCartesianChartComponent } from '../base-cartesian-chart.component';
|
||||||
import { PiChartLegendComponent } from '../chart-legend/chart-legend.component';
|
import { PiChartLegendComponent } from '../chart-legend/chart-legend.component';
|
||||||
|
|
||||||
Chart.register(
|
|
||||||
CategoryScale,
|
|
||||||
LinearScale,
|
|
||||||
LineController,
|
|
||||||
LineElement,
|
|
||||||
PointElement,
|
|
||||||
Filler,
|
|
||||||
Tooltip,
|
|
||||||
Legend,
|
|
||||||
);
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'pi-line-chart',
|
selector: 'pi-line-chart',
|
||||||
templateUrl: './line-chart.component.html',
|
templateUrl: './line-chart.component.html',
|
||||||
@@ -61,6 +50,9 @@ export class PiLineChartComponent extends PiBaseCartesianChartComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected buildChart(): Chart {
|
protected buildChart(): Chart {
|
||||||
|
// Aquí y no al importar: así una app que nunca pinta este gráfico no
|
||||||
|
// arrastra Chart.js. Registrar dos veces no hace nada.
|
||||||
|
Chart.register(CategoryScale, LinearScale, LineController, LineElement, PointElement, Filler, Tooltip, Legend);
|
||||||
return new Chart(this.chartRef().nativeElement, {
|
return new Chart(this.chartRef().nativeElement, {
|
||||||
type: 'line',
|
type: 'line',
|
||||||
data: {
|
data: {
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ import { ChangeDetectionStrategy, Component } from '@angular/core';
|
|||||||
import { ArcElement, Chart, Legend, PieController, Tooltip } from 'chart.js';
|
import { ArcElement, Chart, Legend, PieController, Tooltip } from 'chart.js';
|
||||||
import { PiBaseArcChartComponent } from '../base-arc-chart.component';
|
import { PiBaseArcChartComponent } from '../base-arc-chart.component';
|
||||||
|
|
||||||
Chart.register(ArcElement, PieController, Tooltip, Legend);
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'pi-pie-chart',
|
selector: 'pi-pie-chart',
|
||||||
templateUrl: './pie-chart.component.html',
|
templateUrl: './pie-chart.component.html',
|
||||||
@@ -12,6 +10,9 @@ Chart.register(ArcElement, PieController, Tooltip, Legend);
|
|||||||
})
|
})
|
||||||
export class PiPieChartComponent extends PiBaseArcChartComponent {
|
export class PiPieChartComponent extends PiBaseArcChartComponent {
|
||||||
protected buildChart(): Chart {
|
protected buildChart(): Chart {
|
||||||
|
// Aquí y no al importar: así una app que nunca pinta este gráfico no
|
||||||
|
// arrastra Chart.js. Registrar dos veces no hace nada.
|
||||||
|
Chart.register(ArcElement, PieController, Tooltip, Legend);
|
||||||
return new Chart(this.chartRef().nativeElement, {
|
return new Chart(this.chartRef().nativeElement, {
|
||||||
type: 'pie',
|
type: 'pie',
|
||||||
data: this.getArcData<'pie'>(),
|
data: this.getArcData<'pie'>(),
|
||||||
|
|||||||
@@ -17,16 +17,6 @@ import { hexToRgba, lineDatasetConfig, tooltipConfig } from '../chart.configs';
|
|||||||
import { PiBaseCartesianChartComponent } from '../base-cartesian-chart.component';
|
import { PiBaseCartesianChartComponent } from '../base-cartesian-chart.component';
|
||||||
import { PiChartLegendComponent } from '../chart-legend/chart-legend.component';
|
import { PiChartLegendComponent } from '../chart-legend/chart-legend.component';
|
||||||
|
|
||||||
Chart.register(
|
|
||||||
RadarController,
|
|
||||||
RadialLinearScale,
|
|
||||||
LineElement,
|
|
||||||
PointElement,
|
|
||||||
Filler,
|
|
||||||
Tooltip,
|
|
||||||
Legend,
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A diferencia de `pi-line-chart`/`pi-bar-chart`, la escala radial (`r`) no
|
* A diferencia de `pi-line-chart`/`pi-bar-chart`, la escala radial (`r`) no
|
||||||
* encaja en `scalesConfig` (pensado para `x`/`y`), así que no reutiliza
|
* encaja en `scalesConfig` (pensado para `x`/`y`), así que no reutiliza
|
||||||
@@ -94,6 +84,9 @@ export class PiRadarChartComponent extends PiBaseCartesianChartComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected buildChart(): Chart {
|
protected buildChart(): Chart {
|
||||||
|
// Aquí y no al importar: así una app que nunca pinta este gráfico no
|
||||||
|
// arrastra Chart.js. Registrar dos veces no hace nada.
|
||||||
|
Chart.register(RadarController, RadialLinearScale, LineElement, PointElement, Filler, Tooltip, Legend);
|
||||||
return new Chart(this.chartRef().nativeElement, {
|
return new Chart(this.chartRef().nativeElement, {
|
||||||
type: 'radar',
|
type: 'radar',
|
||||||
data: {
|
data: {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { ComponentFamilyClass, theming } from '../../../styles';
|
|||||||
import { Date } from '../../../util/models';
|
import { Date } from '../../../util/models';
|
||||||
import { PiBaseChipComponent } from '../base-chip.component';
|
import { PiBaseChipComponent } from '../base-chip.component';
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import _ from 'lodash';
|
import capitalize from 'lodash/capitalize';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'pi-chip-date',
|
selector: 'pi-chip-date',
|
||||||
@@ -73,8 +73,8 @@ export class PiChipDateComponent extends PiBaseChipComponent {
|
|||||||
day: dateObj.day.toString() || '',
|
day: dateObj.day.toString() || '',
|
||||||
month: dateObj.monthShort || '',
|
month: dateObj.monthShort || '',
|
||||||
weekDay: isDateToday
|
weekDay: isDateToday
|
||||||
? _.capitalize(dateObj.toRelativeCalendar() ?? '')
|
? capitalize(dateObj.toRelativeCalendar() ?? '')
|
||||||
: _.capitalize(dateObj.weekdayShort || ''),
|
: capitalize(dateObj.weekdayShort || ''),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import {
|
|||||||
inject,
|
inject,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { TranslateService, TranslatePipe } from '@ngx-translate/core';
|
import { TranslateService, TranslatePipe } from '@ngx-translate/core';
|
||||||
import _ from 'lodash';
|
import isEqual from 'lodash/isEqual';
|
||||||
import { PiBaseSelectComponent } from '../base-select.component';
|
import { PiBaseSelectComponent } from '../base-select.component';
|
||||||
import { PiSelectOptionComponent } from '../select-option/select-option.component';
|
import { PiSelectOptionComponent } from '../select-option/select-option.component';
|
||||||
import { Device } from '../../../util/device-configs';
|
import { Device } from '../../../util/device-configs';
|
||||||
@@ -97,7 +97,7 @@ export class PiSelectCarouselComponent
|
|||||||
* @returns The selected option.
|
* @returns The selected option.
|
||||||
*/
|
*/
|
||||||
get selectedOption(): any {
|
get selectedOption(): any {
|
||||||
return this.options().find((option) => _.isEqual(this.value(), option));
|
return this.options().find((option) => isEqual(this.value(), option));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import {
|
|||||||
import { TranslateService, TranslatePipe } from '@ngx-translate/core';
|
import { TranslateService, TranslatePipe } from '@ngx-translate/core';
|
||||||
import { PiBaseSelectComponent } from '../base-select.component';
|
import { PiBaseSelectComponent } from '../base-select.component';
|
||||||
import { PiSelectOptionComponent } from '../select-option/select-option.component';
|
import { PiSelectOptionComponent } from '../select-option/select-option.component';
|
||||||
import _ from 'lodash';
|
import some from 'lodash/some';
|
||||||
import { MatCheckboxChange, MatCheckbox } from '@angular/material/checkbox';
|
import { MatCheckboxChange, MatCheckbox } from '@angular/material/checkbox';
|
||||||
import { NgTemplateOutlet } from '@angular/common';
|
import { NgTemplateOutlet } from '@angular/common';
|
||||||
import { PiIconComponent } from '../../icon/icon/icon.component';
|
import { PiIconComponent } from '../../icon/icon/icon.component';
|
||||||
@@ -114,7 +114,7 @@ export class PiSelectMultipleComponent
|
|||||||
get selectedOptions(): Array<any> {
|
get selectedOptions(): Array<any> {
|
||||||
const value = this.value();
|
const value = this.value();
|
||||||
if (!value) return [];
|
if (!value) return [];
|
||||||
return this.options().filter((option) => _.some(value, option));
|
return this.options().filter((option) => some(value, option));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import {
|
|||||||
import { TranslateService, TranslatePipe } from '@ngx-translate/core';
|
import { TranslateService, TranslatePipe } from '@ngx-translate/core';
|
||||||
import { PiBaseSelectComponent } from '../base-select.component';
|
import { PiBaseSelectComponent } from '../base-select.component';
|
||||||
import { PiSelectOptionComponent } from '../select-option/select-option.component';
|
import { PiSelectOptionComponent } from '../select-option/select-option.component';
|
||||||
import _ from 'lodash';
|
import isEqual from 'lodash/isEqual';
|
||||||
import { NgTemplateOutlet } from '@angular/common';
|
import { NgTemplateOutlet } from '@angular/common';
|
||||||
import { PiIconComponent } from '../../icon/icon/icon.component';
|
import { PiIconComponent } from '../../icon/icon/icon.component';
|
||||||
import { PiDisabledDirective } from '../../../util/status/directives/status.directive';
|
import { PiDisabledDirective } from '../../../util/status/directives/status.directive';
|
||||||
@@ -103,7 +103,7 @@ export class PiSelectPeriodComponent
|
|||||||
* @returns The selected option.
|
* @returns The selected option.
|
||||||
*/
|
*/
|
||||||
get selectedOption(): any {
|
get selectedOption(): any {
|
||||||
return this.options().find((option) => _.isEqual(this.value(), option));
|
return this.options().find((option) => isEqual(this.value(), option));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import {
|
|||||||
import { TranslateService, TranslatePipe } from '@ngx-translate/core';
|
import { TranslateService, TranslatePipe } from '@ngx-translate/core';
|
||||||
import { PiBaseSelectComponent } from '../base-select.component';
|
import { PiBaseSelectComponent } from '../base-select.component';
|
||||||
import { PiSelectOptionComponent } from '../select-option/select-option.component';
|
import { PiSelectOptionComponent } from '../select-option/select-option.component';
|
||||||
import _ from 'lodash';
|
import isEqual from 'lodash/isEqual';
|
||||||
import { NgTemplateOutlet } from '@angular/common';
|
import { NgTemplateOutlet } from '@angular/common';
|
||||||
import { PiIconComponent } from '../../icon/icon/icon.component';
|
import { PiIconComponent } from '../../icon/icon/icon.component';
|
||||||
import { PiDisabledDirective } from '../../../util/status/directives/status.directive';
|
import { PiDisabledDirective } from '../../../util/status/directives/status.directive';
|
||||||
@@ -116,7 +116,7 @@ export class PiSelectComponent
|
|||||||
* @returns The selected option.
|
* @returns The selected option.
|
||||||
*/
|
*/
|
||||||
get selectedOption(): any {
|
get selectedOption(): any {
|
||||||
return this.options().find((option) => _.isEqual(this.value(), option));
|
return this.options().find((option) => isEqual(this.value(), option));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user