Многу често во проекти се среќаваме со користење на релативни import/require патеки. Ако проектот е мал и се поврзува модул од тековната папка, тоа е прифатливо. Но кога проектот ќе порасне и ќе се зголеми длабочината на структурата на папките, ваквите патеки стануваат ужас за гледање:
import { User } from '../../user/model';
import { Article } from '../../article/model';
import { Cache } from '../../../../cache';
import { MongoDB } from '../../../../mongodb';
Главните недостатоци на релативните патеки:
- Тешко се читаат и го загадува кодот. На човек му требаат дополнителни напори за да ги „преведе“ сите тие ../../ во реална патека. Многу полесно се читаат патеки кои почнуваат од коренот на проектот.
- IDE не секогаш ги поправа релативните патеки кога датотеката ќе се премести. Ако уредувачот не успее автоматски да додаде autoimport при пишување код, тогаш е вистинска мака рачно да се внесуваат релативни импорти во поголем проект. И копирањето import/require од друга датотека е проблем поготово ако новиот фајл е на друга длабочина, мора рачно да се додаваат или бришат ../../.
- При преместување датотека, мора да се менуваат не само import/require во другите датотеки, туку и во самата преместена датотека. Се додаваат или отстрануваат „скокови“ низ папките, што создава дополнителен „шок’ во системите за контрола на верзии, иако суштински ништо не се сменило. Со апсолутни патеки оваа мака ја нема.
- Во некои јазици (како Python) импортите од коренот на проектот доаѓаат „од корен“, но во JavaScript тоа не е така. За среќа, ова може релативно лесно да се додаде.
Постојат неколку начини да се реши проблемот:
Симлинк
Создадете симлинк во коренот на системот кој покажува кон вашиот проект, и користете го за import/require.
// sudo ln -s /Users/kalcovski/project prj
Наместо:
const User = require('../../model/User');
Може да се користи:
const User = require('/prj/src/model/User');
Недостаток е што со симлинковите често има „изненадувања“. Не во сите оперативни системи ова функционира лесно. На новите macOS има одредени нијанси (мора да се користи synthetic.conf). Покрај тоа, поради чудното однесување на симлинковите од корен на macOS, TypeScript компајлерот при компилација преку симлинк генерира нешто необично.
На Windows и слично има одредни проблеми или ограничувања. Според сè, единствениот систем каде што речиси сигурно нема да има проблеми е Linux.
Во недостатоци може да се додаде и тоа што, хипотетички, името на симлинкот може да влезе во конфликт (на пример, ако домашниот и работниот проект користат симлинк prj) и тоа што не може лесно да се сместат копии од проектот во различни папки и да се стартуваат без менување на патеките до модулите (тука виртуализацијата и контејнеризацијата помагаат).
NODE_PATH
Другата опција е да ги додадете вашите папки во променливата на околина NODE_PATH.
По правило, таа содржи патеки што Node.js ги користи за да ги пронајде модулите во node_modules, но ние можеме да ја искористиме и за сопствени потреби:
Наместо:
const User = require('../../model/User');
Може да се користи:
const User = require('src/model/User');
При стартување, му кажуваме на Node каде да бара модули:
NODE_PATH=./ node src/app/main.js
За TypeScript потребно е да се додаде baseUrl, кој треба да се совпаѓа со NODE_PATH:
{
"compilerOptions": {
"baseUrl": "."
}
}
Оваа метода добро работи со сите уредувачи на NODE_PATH особено кога се обидуваат да скокнат до датотеката од import/require. Но има и подобро решение.
Path aliases
Path aliases е функционалност што дозволува да користиме алијаси (кратенки) за патеките во require/import.
Наместо:
const User = require('../../model/User');
Може да се користи:
const User = require('@/model/User');
1 Инсталација на module-alias
За вакви импорти да функционираат, ни е потребен модулот module-alias:
npm i module-alias -S
2 Додавање алијаси во package.json
За да може module-alias да ги мапира алијасите кон реалните патеки, во package.json додаваме:
"_moduleAliases": {
"@": "src"
}
3 Вклучување на module-alias во влезниот фајл
На самиот врв од главниот фајл (entry point) додаваме:
// src/app/main.js
require('module-alias/register');
// или:
import 'module-alias/register';
4 Алтернативно стартување преку командна линија
node -r ./node_modules/module-alias/register src/app/main.js
5 Path aliases во TypeScript
Во tsconfig.json мора да додадеме директива paths:
{
"compilerOptions": {
"paths": {
"@/*": ["./src/*"]
}
}
}
А во package.json го менуваме алијасот да покажува кон финалната компилација (dist):
"_moduleAliases": {
"@": "dist"
}
Стартување по компилација
node -r ./node_modules/module-alias/register dist/app/main.js
6 Ако користите ts-node
За TypeScript backend со ts-node е потребно:
npm i tsconfig-paths -S
npx ts-node -r tsconfig-paths/register src/app/main.ts
7 Ако користите tsx
Јас за backend користам tsx кој има вградена поддршка за path aliases:
npm i tsx -D
npx tsx src/app/main.ts
8 Може да креирате колку сакате алијаси
{
"compilerOptions": {
"paths": {
"app/*": ["./src/app/*"],
"config/*": ["./src/config/*"],
"shared/*": ["./src/shared/*"],
"cache/*": ["./src/cache/*"],
"tests/*": ["./src/tests/*"]
}
}
}
Сега ќе можете да пишувате поубави и разбирливи import/require
import { User } from '@component/user/model';
import { Article } from '@component/article/model';
import { Cache } from '@cache/cache';
import { MongoDB } from '@db/mongodb';
Happy Coding!