برنامه نویسی

مهاجرت یک پروژه Angular به یک فضای کاری Nx: Ng-Morph برای نجات!

من در حال حاضر در حال انتقال چندین پروژه و کتابخانه های مشترک در یک فضای کاری Nx هستم تا از قدرت، ابزار و تجربه توسعه دهنده (DX) ارائه شده توسط Nx استفاده کنم. من روند را در این مقاله توضیح نمی دهم، اما در طول این انتقال، مجبور شدم کارهای خسته کننده و تکراری متعددی را انجام دهم. خوشبختانه، من یک کتابخانه شگفت انگیز به نام کشف کردم آف-مورف که به ما امکان می دهد اسکریپت هایی برای خودکارسازی چنین کارهای تکراری در درخت پروژه خود بنویسیم.

در این مقاله سه نمونه از فیلمنامه هایی که نوشتم را برای سرعت بخشیدن به کار و کاهش درد آن توضیح خواهم داد.

تمام فایل های تست غیر ضروری را حذف کنید.

هنگام تولید کامپوننت، دایرکتیو یا لوله با استفاده از Angular CLI، یک فایل مشخصات با یک آزمایش “باید ایجاد” ایجاد می کند. در فضای کاری فعلی من، بسیاری از این فایل ها نگهداری می شدند، اما در واقع غیر ضروری هستند و زمان اجرای دستور تست را کاهش می دهند. هدف حذف تمام فایل هایی است که فقط حاوی تست “باید ایجاد” هستند.

با استفاده از Ng-morph، با تعریف درخت فایل و نوع فایل هایی که می خواهیم با آنها کار کنیم، شروع می کنیم. تنظیم پروژه فعال ما با خط زیر الزامی است:

setActiveProject(createProject(new NgMorphTree(), '/', ['**/*.spec.ts']));
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

const sourceFiles = getSourceFiles(['apps/**/*.spec.ts', 'libs/**/*.spec.ts']);

sourceFiles.forEach(s => {
  const text = s.getFullText();
  if (text.match(new RegExp("(it\\('should be created'|it\\('should create)", 'g'))) {
    const secondTest = text.match(new RegExp("(it\\(')", 'g'));
    if (secondTest && secondTest.length === 1) {
      s.delete();
    }
  }
});
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در مرحله بعد همه فایل های مشخصات را با استفاده از آن بارگذاری می کنیم getSourceFiles. پس از اتمام، هر فایل را تکرار می کنیم، بررسی می کنیم که آیا با معیارهای ما مطابقت دارد یا خیر (با استفاده از جستجوی regex اولیه روی متن فایل)و در صورت وجود آن را حذف کنید.

در نهایت، تغییرات خود را با تماس ذخیره می کنیم saveActiveProject.

همانطور که می بینید، اسکریپت ساده و آسان برای نوشتن و خواندن است. حتی بدون اطلاع قبلی از کتابخانه، نوشتن آن کمتر از 15 دقیقه طول کشید. با تمرین می توانید این اسکریپت را تنها در 5 دقیقه بنویسید. اگر بخواهید این کار را به صورت دستی انجام دهید، بسیار بیشتر طول می کشد و لذت کمتری خواهد داشت.

نکته مهمی که باید به آن توجه داشت این است که نیازی به صرف زمان برای نوشتن اسکریپت با متغیرهای نام‌گذاری شده یا کد قابل نگهداری نیست، زیرا قرار است اسکریپت یک بار اجرا شود تا کار مورد نظر انجام شود.

در نهایت برای اجرای اسکریپت از آن استفاده می کنیم ts-node:

npx ts-node path/to/script
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

صادرات همه فایل ها به فایل api عمومی:

در Nx، ما libs/packages/modules ایجاد می کنیم (آنها را هر طور که دوست دارید نام ببرید) برای کپسوله کردن کد برای افشای کد به صورت خارجی، باید آن را در یک فایل بشکه یا فایل API عمومی صادر کنیم. در پروژه فعلی که من روی آن کار می‌کنم، هر تابع/کلاس مستقیماً از فایل مربوطه وارد می‌شود و در نتیجه مسیرهایی مانند زیر ایجاد می‌شود:

import { FooComponent } from 'src/path/to/foo.component';
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اما در Nx، اگر می‌خواهید از FooComponentin کتابخانه دیگری استفاده کنید، باید آن را از فایلی که API کتابخانه ما را صادر می‌کند صادر کنیم. انجام دستی این کار می تواند خسته کننده باشد، زیرا ما باید مسیرهای همه فایل های داخل کتابخانه را پیدا کرده و صادر کنیم.

با Ng-Morph می توانیم این کار را به سرعت خودکار کنیم. بیایید نگاهی به فیلمنامه بیاندازیم:

const project = process.argv[2];

const folder = `libs/${project}`;

const fs = require('fs');
if (!fs.existsSync(folder)) {
  console.log("name of project doesn't exist");
  process.exit();
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

این بار، تصمیم گرفتم اسکریپت را با یک استدلال بپذیرم، زیرا می توان از آن برای بسیاری از کتابخانه های دیگر استفاده کرد.

setActiveProject(createProject(new NgMorphTree(), '/', ['**/*.ts']));
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

همانطور که قبلا ذکر شد، ما باید پروژه فعال را برای ثبت درخت خود راه اندازی کنیم.

const sourceFiles = getSourceFiles([`/${folder}/src/lib/**/*.ts`, `!/${folder}/src/lib/**/*.spec.ts`])
  .map(s => s.getFilePath().replace(`/${folder}/src`, '.').replace('.ts', ''))
  .map(n => `export * from '${n}';`);

const barrelFile = sourceFiles.reduce((acc, file) => `${acc}\n${file}`, '');

createSourceFile(`/${folder}/src/index.ts`, barrelFile, {
  overwrite: true
});

saveActiveProject();
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

تمام منطق در این چند خط وجود دارد.

ما همه فایل های TS به جز فایل های spec را بارگذاری می کنیم و آن ها را حذف می کنیم .ts پسوند و اضافه کردن export * from عبارت برای هر مسیر فایل

در مرحله بعد، همه این رشته ها را به یک رشته متصل می کنیم و فایل بشکه ای کتابخانه را بازنویسی می کنیم.

در آخر یادمون نره زنگ بزنیم setActiveProjectبرای ذخیره تغییرات ما

توجه داشته باشید: این اسکریپت پایه است و می توان آن را بهینه کرد. تمام فایل‌ها، از جمله مواردی که در خارج از کتابخانه استفاده نمی‌شوند را صادر می‌کند. با این حال، ما در حال حاضر در حال انتقال چندین پروژه در Nx هستیم و اولویت ما این است که آن را به سرعت کار کنیم. سایر اعضای تیم با تنظیمات قدیمی کار می کنند، و هر چه شاخه ها بیشتر از هم جدا شوند، ادغام چالش برانگیزتر خواهد بود.

در مرحله بعدی، می‌توانیم فرآیند را با تقسیم کتابخانه و حذف هرگونه فایل صادراتی غیرضروری از API عمومی، بهینه کنیم.

تغییر نام واردات فایل:
این وضعیت اغلب هنگام جابجایی فایل ها و توابع رخ می دهد. به عنوان مثال، ممکن است یک فایل utils بزرگ داشته باشیم و بخواهیم آن را تقسیم کنیم و برخی از توابع را به خارج از آن فایل و به یک کتابخانه اشتراکی/utils منتقل کنیم. در نتیجه واردات قدیمی باید حذف شود و واردات جدید جایگزین شود.

ما می توانیم واردات را مانند این پیدا کنیم:

import { addDate, removeDate } from '@test/shared'
import { addDate } from '@test/shared'
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

مورد دوم را می توان به راحتی با ویژگی جستجو و جایگزینی در هر IDE مدیریت کرد. با این حال، مورد اول چالش برانگیزتر است زیرا باید حذف کنیم addDateاز آرایه واردات در حین نگهداری removeDateسالم.

با Ng-morph، این کار می تواند به راحتی خودکار شود.

const importToReplace = 'addDate';
const newNamespace = '@test/shared/date-utils';

function importIfNotPresent(source: string) {
  addImports(source, [
    {
      namedImports: [importToReplace],
      moduleSpecifier: newNamespace
    }
  ]);
}

setActiveProject(createProject(new NgMorphTree(), '/', ['**/*.ts']));

const sourceFiles = getSourceFiles([`**/*.ts`]).map(s => s.getFilePath());

sourceFiles.forEach(s => {
  const imports = getImports(s, { namedImports: importToReplace }).filter(
    s => s.getModuleSpecifier().getText().replace(/'/g, '') !== newNamespace
  );

  imports.forEach(i => {
    const namedImports = i.getNamedImports();

    if (namedImports.length === 1) {
      i.remove();
      importIfNotPresent(s);
    } else {
      namedImports.forEach(n => {
        if (n.getName() === importToReplace) {
          n.remove();
          importIfNotPresent(s);
        }
      });
    }
  });
});

saveActiveProject();
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

خواندن و درک فیلمنامه بسیار آسان است، اما اجازه دهید آن را بشکنیم:

ابتدا وارداتی را که می خواهیم جایگزین کنیم تعریف می کنیم importToReplaceو فضای نام جدید newNamespaceجایی که تابع قرار خواهد گرفت.

طبق معمول با تماس شروع می کنیم setActiveProjectبرای شروع اسکریپت ما ما همه فایل TS را بارگذاری می کنیم و روی آنها تکرار می کنیم.

ما به دنبال رخدادها هستیم addDateدر واردات هر فایل و فیلتر برای اطمینان از اینکه فضای نام قبلاً به روز نشده است.

سپس، روی واردات نام‌گذاری شده تکرار می‌کنیم. اگر فقط یک واردات وجود داشته باشد، کل عبارت import را حذف می کنیم و آن را با عبارت جدید جایگزین می کنیم.

در صورت وجود چند واردات، تابع مورد نظر را حذف کرده و دستور import جدید را به فایل اضافه می کنیم.

توجه داشته باشید: همانطور که می بینید، ما نیازی به کار با عملکرد پیچیده AST یا انجام جستجوهای پیچیده نداریم. Ng-Morph به ما این امکان را می دهد که به روشی ساده و سرراست به فایل های خود دسترسی پیدا کرده و آنها را اصلاح کنیم.

هنگامی که نحوه استفاده از Ng-Morph را فهمیدید، کار با کل مخزن شما آسان و لذت بخش می شود.”


امیدوارم درک بهتری از این کتابخانه شگفت انگیز داشته باشید و همه چیزهای باورنکردنی را که می توانید با آن به دست آورید، دیده باشید. 🚀

می تونی منو پیدا کنی توییتر یا Github. در صورت داشتن هرگونه سوال با من تماس بگیرید.

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا