Якщо ви стикаєтесь з повільними часами побудови у вашому монорепозиторії на базі Nx і у вас багато проєктів, інкрементальні побудови можуть допомогти вам заощадити частину втраченого часу. У цій статті ми розглянемо, як працюють інкрементальні побудови в Nx і як ви можете використати їх для пришвидшення вашого процесу розробки.
Оригінал статті опубліковано на моєму вебсайті:
https://stefanhaas.dev/blog/incremental-builds-with-nx
Що таке інкрементальні побудови?
Основна ідея інкрементальної побудови полягає в тому, щоб переробляти лише ту частину вашої кодової бази, яка змінилася з моменту останньої побудови. Це може суттєво зменшити час, необхідний для побудови вашого проєкту, особливо якщо у вас велика кодова база з багатьма проєктами та бібліотеками.
Nx має локальний кеш, у якому зберігаються кожне завдання та його результати. Коли ви запускаєте кешоване завдання двічі в Nx, другий запуск буде значно швидшим, оскільки буде використано результати першого запуску. Інкрементальні побудови йдуть ще далі, надаючи цілі для побудови для багатьох залежних проєктів, так що всі ці вихідні результати кожного проєкту з цією ціллю можна буде повторно використати в наступній побудові.
Застереження
Інкрементальні побудови — це не панацея, і вони можуть не підходити для всіх проєктів. Ви точно не повинні робити кожну бібліотеку будівельною. Причина в тому, що холодні побудови (коли нічого не було кешовано) будуть повільнішими з інкрементальними побудовами, оскільки більше проєктів потрібно буде будувати з нуля. Якщо у вас є багато змін у деякій бібліотеці, ця бібліотека, ймовірно, не повинна бути будівельною.
Проте інкрементальні побудови можуть стати чудовим інструментом для пришвидшення вашого процесу розробки та покращення ефективності побудови, якщо їх застосовувати обережно. Чудовими кандидатами для будівельних бібліотек є спільні бібліотеки з низьким рівнем змін, які використовуються в багатьох інших проєктах. Гарним прикладом буде бібліотека UI або система дизайну, що використовується в кількох додатках.
Увімкнення інкрементальних побудов
Якщо ви використовуєте один із виконавців Nx, наприклад @nx/angular:webpack-browser
, ви можете увімкнути інкрементальні побудови, встановивши параметр buildLibsFromSource
у значення false
в файлі project.json
для застосунку, який повинен підтримувати інкрементальні побудови.
{
"targets": {
"build": {
"executor": "@nx/angular:webpack-browser",
"options": {
"buildLibsFromSource": false
}
}
}
}
Це повідомить Nx, що не потрібно будувати залежні бібліотеки з вихідного коду, а потрібно використовувати результати попередньої побудови. До речі, Nx будує граф завдань, і якщо у вас є бібліотеки, що можна будувати, вони будуть збудовані паралельно до побудови застосунку. Якщо ж побудова бібліотеки не потрібна через кеш, вона не буде побудована взагалі, що дозволить зекономити час на побудову.
Однак для того, щоб інкрементальні побудови мали сенс, вам потрібні будівельні бібліотеки.
Будівельні бібліотеки
За замовчуванням бібліотека, створена за допомогою Nx, не є будівельною, якщо ви спеціально не додаєте прапор buildable: true
під час генерації бібліотеки. Різниця між будівельною і не будівельною бібліотекою полягає в тому, що у будівельної бібліотеки є ціль для побудови. Ця ціль може використовувати будь-який виконавець, який ви хочете, але якщо ви використовуєте Angular, вам, ймовірно, підійде виконавець @nx/angular:ng-packagr-lite
.
Створити будівельну бібліотеку дуже просто — достатньо додати прапор buildable: true
під час генерації бібліотеки:
nx g @nx/angular:library my-lib --buildable
Якщо ви хочете зробити вже існуючу не будівельну бібліотеку будівельною, ви можете вручну виконати роботу, яку бібліотечний генератор зробить за вас:
- Додайте ціль побудови в файл
project.json
бібліотеки. - Створіть файл
package.json
в папці бібліотеки зі наступним вмістом:
3.
Створіть файл ng-package.json, якщо бібліотека містить код Angular
Якщо ви хочете побачити, як ці файли повинні виглядати, ви можете ознайомитися з цим проєктом.
Правила
Щоб інкрементальні побудови працювали, вам потрібно дотримуватись деяких правил:
- Ви повинні мати всі alias шляхи TypeScript у кореневому файлі tsconfig.base.json. Alias шляхи на рівні проєкту не дозволяються.
- Будівельна бібліотека не повинна залежати від не будівельної бібліотеки.
Якщо ви випадково залежите від не будівельної бібліотеки, ви отримаєте помилку лінтингу, якщо у вас увімкнено правило nx/enforce-module-boundaries
.
Як це працює за лаштунками
По-перше, коли ви запускаєте побудову для застосунку з увімкненим параметром buildLibsFromSource
, Nx спочатку побудує всі будівельні бібліотеки або пропустить побудову, якщо її можна відновити з кешу. Після цього Nx тимчасово змінить файл tsconfig.base.json
, щоб переопреділяти шляхи для будівельних бібліотек, вказуючи на папку dist бібліотеки.
{
"compilerOptions": {
"paths": {
- "@my-org/my-lib": ["libs/my-lib/src/index.ts"]
+ "@my-org/my-lib": ["dist/libs/my-lib"]
}
}
}
Тепер давайте подивимося, як це працює за лаштунками в Nx. Ось тут ви можете побачити код, який відповідає за створення тимчасового файлу tsconfig.
import { readCachedProjectGraph, type ExecutorContext } from '@nx/devkit';
import {
calculateProjectDependencies,
createTmpTsConfig,
type DependentBuildableProjectNode,
} from '@nx/js/src/utils/buildable-libs-utils';
import { join } from 'path';
export function createTmpTsConfigForBuildableLibs(
tsConfigPath: string,
context: ExecutorContext
) {
let dependencies: DependentBuildableProjectNode[];
const result = calculateProjectDependencies(
context.projectGraph ?? readCachedProjectGraph(),
context.root,
context.projectName,
context.targetName,
context.configurationName
);
dependencies = result.dependencies;
const tmpTsConfigPath = createTmpTsConfig(
join(context.root, tsConfigPath),
context.root,
result.target.data.root,
dependencies
);
process.env.NX_TSCONFIG_PATH = tmpTsConfigPath;
const tmpTsConfigPathWithoutWorkspaceRoot = tmpTsConfigPath.replace(
context.root,
''
);
return { tsConfigPath: tmpTsConfigPathWithoutWorkspaceRoot, dependencies };
}
Цікава частина — це функція createTmpTsConfig
, яка створює тимчасовий файл tsconfig. Більше утиліт для будівельних бібліотек можна знайти тут.
export function createTmpTsConfig(
tsconfigPath: string,
workspaceRoot: string,
projectRoot: string,
dependencies: DependentBuildableProjectNode[],
useWorkspaceAsBaseUrl: boolean = false
) {
const tmpTsConfigPath = join(
workspaceRoot,
'tmp',
projectRoot,
process.env.NX_TASK_TARGET_TARGET ?? 'build',
'tsconfig.generated.json'
);
if (tsconfigPath === tmpTsConfigPath) {
return tsconfigPath;
}
const parsedTSConfig = readTsConfigWithRemappedPaths(
tsconfigPath,
tmpTsConfigPath,
dependencies,
workspaceRoot
);
process.on('exit', () => cleanupTmpTsConfigFile(tmpTsConfigPath));
if (useWorkspaceAsBaseUrl) {
parsedTSConfig.compilerOptions ??= {};
parsedTSConfig.compilerOptions.baseUrl = workspaceRoot;
}
writeJsonFile(tmpTsConfigPath, parsedTSConfig);
return join(tmpTsConfigPath);
}
Функція readTsConfigWithRemappedPaths
відповідає за читання оригінального файлу tsconfig і змінення шляхів для будівельних бібліотек.
Це фактично поширює залежності, які є будівельними бібліотеками, і замінює ці шляхи у файлі конфігурації ts.
Ви можете побачити тимчасовий файл tsconfig у папці tmp
вашого робочого простору, якщо ви дуже швидко, тому що він буде видалений після цього. Як видно, все залежить від з'єднання змінених шляхів з файлом tsconfig.base.json
, тому важливо, щоб усі шляхи були в файлі tsconfig.base.json
.
Висновок
Сподіваюся, що ця стаття дала вам гарне уявлення про те, як працюють інкрементальні побудови з Nx і як ви можете використати їх для прискорення вашого процесу розробки. Тепер ви повинні мати чітке розуміння того, що таке інкрементальні побудови, як їх увімкнути у вашому проєкті та як вони працюють за лаштунками. Ви також повинні знати, що це не універсальне рішення для всіх і ви повинні ретельно обирати, які бібліотеки робити будівельними.
Добре правило — робити спільні бібліотеки будівельними, оскільки вони використовуються в кількох проєктах і мають низький рівень змін.
“Думки є моїми власними і не відображають погляди мого роботодавця.”
Перекладено з: Incremental Builds with Nx