شماتیکها برای کتابخانهها
هنگام ایجاد یک کتابخانه Angular، میتوانید شماتیکهایی را همراه آن ارائه و بستهبندی کنید که با Angular CLI یکپارچه میشوند. با این شماتیکها، کاربران میتوانند برای نصب نسخه اولیه کتابخانه از ng add، برای ایجاد مصنوعات تعریفشده در کتابخانه از ng generate و برای تطبیق پروژه با نسخه جدیدی از کتابخانه که تغییرات ناسازگار دارد از ng update استفاده کنند.
هر سه نوع شماتیک میتوانند بخشی از یک collection باشند که همراه کتابخانه بستهبندی میکنید.
ایجاد یک collection شماتیک
برای شروع یک collection، باید فایلهای شماتیک را ایجاد کنید. مراحل زیر نحوه افزودن پشتیبانی اولیه را بدون تغییر هیچیک از فایلهای پروژه نشان میدهند.
- در پوشه ریشه کتابخانه، پوشهای به نام
schematicsایجاد کنید. - در پوشه
schematics/، برای نخستین شماتیک خود پوشهای به نامng-addبسازید. - در سطح ریشه پوشه
schematics، فایلcollection.jsonرا ایجاد کنید. - فایل
collection.jsonرا ویرایش و schema اولیه collection را تعریف کنید.
{
"$schema": "../../../node_modules/@angular-devkit/schematics/collection-schema.json",
"schematics": {
"ng-add": {
"description": "Add my library to the project.",
"factory": "./ng-add/index#ngAdd",
"schema": "./ng-add/schema.json"
}
}
}این ورودی شامل توضیحات است و به تابع factory اشاره میکند که هنگام اجرای شماتیک فراخوانی میشود.
- مسیر
$schemaنسبت به schema مربوط به collection در Angular Devkit تعیین میشود. - شیء
schematicsشماتیکهای نامگذاریشدهای را توصیف میکند که بخشی از این collection هستند. - نخستین ورودی مربوط به شماتیکی با نام
ng-addاست.
Angular CLI هنگام اجرای فرمانها از این ورودی برای یافتن شماتیکهای نامگذاریشده در collection استفاده میکند.
- در فایل
package.jsonپروژه کتابخانه، یک ورودی «schematics» با مسیر فایل schema خود اضافه کنید.
// #docplaster
// #docregion collection
{
"name": "my-lib",
"version": "0.0.1",
// #enddocregion collection
"scripts": {
"build": "tsc -p tsconfig.schematics.json",
"postbuild": "copyfiles schematics/*/schema.json schematics/*/files/** schematics/collection.json ../../dist/my-lib/"
},
"peerDependencies": {
"@angular/common": "^22.0.0",
"@angular/core": "^22.0.0"
},
// #docregion collection
"schematics": "./schematics/collection.json",
// #enddocregion collection
// #docregion ng-add
"ng-add": {
"save": "devDependencies"
},
// #enddocregion ng-add
"devDependencies": {
"copyfiles": "file:../../node_modules/copyfiles",
"typescript": "file:../../node_modules/typescript"
}
// #docregion collection
}schema اولیهای که ایجاد کردهاید به CLI میگوید شماتیک پشتیبان فرمان ng add را کجا پیدا کند. اکنون آماده ایجاد آن شماتیک هستید.
فراهم کردن پشتیبانی نصب
یک شماتیک برای فرمان ng add میتواند فرایند نصب اولیه را برای کاربران بهبود دهد. مراحل زیر این نوع شماتیک را تعریف میکنند.
- به پوشه
<lib-root>/schematics/ng-addبروید. - یک فایل
schema.jsonبرای تعریف گزینههای قابل پذیرش شماتیک ایجاد کنید.
{
"$schema": "http://json-schema.org/schema",
"$id": "SchematicsMyLibNgAdd",
"title": "MyLib ng add Schema",
"type": "object",
"properties": {
"project": {
"type": "string",
"description": "Name of the project.",
"$default": {
"$source": "projectName"
}
}
}
}- فایل
schema.tsرا برای تعریف interface گزینههای موجود در فایلschema.jsonایجاد کنید.
export interface Schema {
// Name of the project.
project: string;
}- فایل اصلی یعنی
index.tsرا ایجاد و کد منبع تابع factory شماتیک را اضافه کنید.
import {Rule} from '@angular-devkit/schematics';
import {addRootImport} from '@schematics/angular/utility';
import {Schema} from './schema';
export function ngAdd(options: Schema): Rule {
// Add an import `MyLibModule` from `my-lib` to the root of the user's project.
return addRootImport(
options.project,
({code, external}) => code`${external('MyLibModule', 'my-lib')}`,
);
}Angular CLI جدیدترین نسخه کتابخانه را بهطور خودکار نصب میکند و این نمونه با افزودن MyLibModule به ریشه application یک گام فراتر میرود. تابع addRootImport یک callback دریافت میکند که باید یک بلوک کد برگرداند. میتوانید هر کدی را داخل رشتهای بنویسید که با تابع code برچسب خورده است؛ هر نماد خارجی نیز باید با تابع external پوشانده شود تا import statementهای مناسب تولید شوند.
تعریف نوع dependency
از گزینه save مربوط به ng-add استفاده کنید تا مشخص شود کتابخانه به dependencies یا devDependencies افزوده شود، یا اصلاً در فایل پیکربندی package.json پروژه ذخیره نشود.
// #docplaster
// #docregion collection
{
"name": "my-lib",
"version": "0.0.1",
// #enddocregion collection
"scripts": {
"build": "tsc -p tsconfig.schematics.json",
"postbuild": "copyfiles schematics/*/schema.json schematics/*/files/** schematics/collection.json ../../dist/my-lib/"
},
"peerDependencies": {
"@angular/common": "^22.0.0",
"@angular/core": "^22.0.0"
},
// #docregion collection
"schematics": "./schematics/collection.json",
// #enddocregion collection
// #docregion ng-add
"ng-add": {
"save": "devDependencies"
},
// #enddocregion ng-add
"devDependencies": {
"copyfiles": "file:../../node_modules/copyfiles",
"typescript": "file:../../node_modules/typescript"
}
// #docregion collection
}مقادیر ممکن عبارتاند از:
| مقدارها | جزئیات |
|---|---|
false | package را به package.json اضافه نمیکند |
true | package را به dependencies اضافه میکند |
"dependencies" | package را به dependencies اضافه میکند |
"devDependencies" | package را به devDependencies اضافه میکند |
ساخت شماتیکها
برای بستهبندی شماتیکها همراه کتابخانه، باید کتابخانه را طوری پیکربندی کنید که شماتیکها را جداگانه بسازد و سپس آنها را به bundle اضافه کند. شماتیکها را باید پس از ساخت کتابخانه بسازید تا در دایرکتوری درست قرار گیرند.
- کتابخانه به یک فایل پیکربندی سفارشی TypeScript نیاز دارد که دستورالعملهای کامپایل شماتیکها در کتابخانه توزیعشده را مشخص کند
- برای افزودن شماتیکها به bundle کتابخانه، scriptهایی را به فایل
package.jsonکتابخانه اضافه کنید
فرض کنید در workspace انگولار خود پروژه کتابخانهای به نام my-lib دارید. برای مشخص کردن نحوه ساخت شماتیکها، در کنار فایل تولیدشده tsconfig.lib.json که build کتابخانه را پیکربندی میکند، یک فایل tsconfig.schematics.json اضافه کنید.
- فایل
tsconfig.schematics.jsonرا ویرایش و محتوای زیر را اضافه کنید.
{
"compilerOptions": {
"baseUrl": ".",
"lib": [
"es2018",
"dom"
],
"declaration": true,
"module": "commonjs",
"moduleResolution": "node",
"noEmitOnError": true,
"noFallthroughCasesInSwitch": true,
"noImplicitAny": true,
"noImplicitThis": true,
"noUnusedParameters": true,
"noUnusedLocals": true,
"rootDir": "schematics",
"outDir": "../../dist/my-lib/schematics",
"skipDefaultLibCheck": true,
"skipLibCheck": true,
"sourceMap": true,
"strictNullChecks": true,
"target": "es6",
"types": [
"jasmine",
"node"
]
},
"include": [
"schematics/**/*"
],
"exclude": [
"schematics/*/files/**/*"
]
}| گزینهها | جزئیات |
|---|---|
rootDir | مشخص میکند پوشه schematics شامل فایلهای ورودی قابل کامپایل است. |
outDir | به پوشه خروجی کتابخانه نگاشت میشود. بهطور پیشفرض، این پوشه dist/my-lib در ریشه workspace شما است. |
- برای اطمینان از کامپایل فایلهای منبع شماتیک در bundle کتابخانه، scriptهای زیر را به فایل
package.jsonدر پوشه ریشه پروژه کتابخانه \(projects/my-lib\) اضافه کنید.
// #docplaster
// #docregion collection
{
"name": "my-lib",
"version": "0.0.1",
// #enddocregion collection
"scripts": {
"build": "tsc -p tsconfig.schematics.json",
"postbuild": "copyfiles schematics/*/schema.json schematics/*/files/** schematics/collection.json ../../dist/my-lib/"
},
"peerDependencies": {
"@angular/common": "^22.0.0",
"@angular/core": "^22.0.0"
},
// #docregion collection
"schematics": "./schematics/collection.json",
// #enddocregion collection
// #docregion ng-add
"ng-add": {
"save": "devDependencies"
},
// #enddocregion ng-add
"devDependencies": {
"copyfiles": "file:../../node_modules/copyfiles",
"typescript": "file:../../node_modules/typescript"
}
// #docregion collection
}برای نصب dependencyها، به مسیر تعریفشده در devDependencies بروید و پیش از اجرای scriptها، npm install را اجرا کنید.
- script مربوط به
buildشماتیک را با استفاده از فایل سفارشیtsconfig.schematics.jsonکامپایل میکند - script مربوط به
postbuildپس از تکمیل script مربوط بهbuildفایلهای شماتیک را کپی میکند - هر دو script مربوط به
buildوpostbuildبه dependencyهایcopyfilesوtypescriptنیاز دارند.
فراهم کردن پشتیبانی تولید
میتوانید یک شماتیک نامگذاریشده به collection خود اضافه کنید تا کاربران بتوانند با فرمان ng generate یک artifact تعریفشده در کتابخانه را ایجاد کنند.
فرض میکنیم کتابخانه شما serviceای به نام my-service تعریف میکند که به مقداری راهاندازی نیاز دارد. میخواهید کاربران بتوانند آن را با فرمان CLI زیر تولید کنند.
ng generate my-lib:my-serviceبرای شروع، در پوشه schematics زیرپوشه جدیدی با نام my-service ایجاد کنید.
پیکربندی شماتیک جدید
هنگام افزودن یک شماتیک به collection باید در schema مربوط به collection به آن اشاره کنید و فایلهای پیکربندی لازم را برای تعریف گزینههای قابل ارسال کاربر به فرمان فراهم کنید.
- فایل
schematics/collection.jsonرا ویرایش کنید تا به زیرپوشه شماتیک جدید و فایل schema مشخصکننده ورودیهای آن اشاره کند.
{
"$schema": "../../../node_modules/@angular-devkit/schematics/collection-schema.json",
"schematics": {
"ng-add": {
"description": "Add my library to the project.",
"factory": "./ng-add/index#ngAdd",
"schema": "./ng-add/schema.json"
},
"my-service": {
"description": "Generate a service in the project.",
"factory": "./my-service/index#myService",
"schema": "./my-service/schema.json"
}
}
}- به پوشه
<lib-root>/schematics/my-serviceبروید. - فایل
schema.jsonرا ایجاد و گزینههای در دسترس شماتیک را تعریف کنید.
{
"$schema": "http://json-schema.org/schema",
"$id": "SchematicsMyService",
"title": "My Service Schema",
"type": "object",
"properties": {
"name": {
"description": "The name of the service.",
"type": "string"
},
"path": {
"type": "string",
"format": "path",
"description": "The path to create the service.",
"visible": false,
"$default": {
"$source": "workingDirectory"
}
},
"project": {
"type": "string",
"description": "The name of the project.",
"$default": {
"$source": "projectName"
}
}
},
"required": [
"name"
]
}- id: شناسهای یکتا برای schema در collection.
- title: توضیحی خوانا برای schema.
- type: توصیفکننده نوع ارائهشده توسط propertyها.
- properties: شیئی که گزینههای در دسترس شماتیک را تعریف میکند.
هر گزینه، یک کلید را به نوع، توضیحات و alias اختیاری مرتبط میکند. نوع، شکل مقدار مورد انتظار را تعریف میکند و وقتی کاربر راهنمای استفاده از شماتیک را درخواست کند، توضیحات نمایش داده میشوند.
برای سفارشیسازی بیشتر گزینههای شماتیک، schema مربوط به workspace را ببینید.
- فایل
schema.tsرا ایجاد و interfaceای تعریف کنید که مقادیر گزینههای موجود در فایلschema.jsonرا نگه دارد.
export interface Schema {
// The name of the service.
name: string;
// The path to create the service.
path?: string;
// The name of the project.
project?: string;
}| گزینهها | جزئیات |
|---|---|
| name | نامی که میخواهید برای service ایجادشده ارائه کنید. |
| path | مسیر ارائهشده به شماتیک را بازنویسی میکند. مقدار پیشفرض مسیر بر اساس دایرکتوری کاری فعلی است. |
| project | پروژه مشخصی را برای اجرای شماتیک فراهم میکند. اگر کاربر گزینه را ارائه نکند، میتوانید در شماتیک یک مقدار پیشفرض تعیین کنید. |
افزودن فایلهای template
برای افزودن artifactها به پروژه، شماتیک به فایلهای template مخصوص خود نیاز دارد. templateهای شماتیک از syntax ویژهای برای اجرای کد و جایگزینی متغیرها پشتیبانی میکنند.
این template یک service ایجاد میکند که HttpClient انگولار از قبل در property به نام http آن inject شده است.
- داخل پوشه
schematics/my-service/یک پوشهfiles/ایجاد کنید. - فایلی با نام
_name@dasherize_.service.ts.templateایجاد کنید که template مورد استفاده برای تولید فایلها را تعریف کند.
```ts {header:projects/my-lib/schematics/my-service/files/_name@dasherize_.service.ts.template (Schematic Template)}
import { Service } from '@angular/core'; import { HttpClient } from '@angular/common/http';
@Service() export class <%= classify(name) %>Service { private http = inject(HttpClient); }
```
این همان name تعریفشده در schema است.
- متدهای
classifyوdasherizeتوابع کمکیای هستند که شماتیک برای تبدیل template منبع و نام فایل به کار میبرد. nameبهعنوان یک property از تابع factory شما ارائه میشود.
افزودن تابع factory
اکنون که زیرساخت آماده است، میتوانید تابع اصلی انجامدهنده تغییرات مورد نیاز در پروژه کاربر را تعریف کنید.
فریمورک Schematics یک سیستم templateسازی فایل فراهم میکند که هم از templateهای مسیر و هم محتوا پشتیبانی میکند. این سیستم بر placeholderهای تعریفشده داخل فایلها یا مسیرهای بارگذاریشده در Tree عمل میکند. سپس با مقادیر ارسالشده به Rule، آنها را پر میکند.
برای جزئیات این ساختارهای داده و syntax، README مربوط به Schematics را ببینید.
فریمورک Schematics توابع کمکی بسیاری برای ایجاد و استفاده از ruleها هنگام اجرای شماتیک ارائه میکند.
- فایل اصلی
index.tsرا ایجاد و کد منبع تابع factory شماتیک را اضافه کنید. - ابتدا تعریفهای شماتیک مورد نیاز را import کنید.
// #docplaster
// #docregion schematics-imports, schema-imports, workspace
import {
Rule,
Tree,
SchematicsException,
apply,
url,
applyTemplates,
move,
chain,
mergeWith,
} from '@angular-devkit/schematics';
import {strings, normalize, virtualFs, workspaces} from '@angular-devkit/core';
// #enddocregion schematics-imports
import {Schema as MyServiceSchema} from './schema';
// #enddocregion schema-imports
function createHost(tree: Tree): workspaces.WorkspaceHost {
return {
async readFile(path: string): Promise<string> {
const data = tree.read(path);
if (!data) {
throw new SchematicsException('File not found.');
}
return virtualFs.fileBufferToString(data);
},
async writeFile(path: string, data: string): Promise<void> {
return tree.overwrite(path, data);
},
async isDirectory(path: string): Promise<boolean> {
return !tree.exists(path) && tree.getDir(path).subfiles.length > 0;
},
async isFile(path: string): Promise<boolean> {
return tree.exists(path);
},
};
}
export function myService(options: MyServiceSchema): Rule {
return async (tree: Tree) => {
const host = createHost(tree);
const {workspace} = await workspaces.readWorkspace('/', host);
// #enddocregion workspace
// #docregion project-info
const project = options.project != null ? workspace.projects.get(options.project) : null;
if (!project) {
throw new SchematicsException(`Invalid project name: ${options.project}`);
}
const projectType = project.extensions.projectType === 'application' ? 'app' : 'lib';
// #enddocregion project-info
// #docregion path
if (options.path === undefined) {
options.path = `${project.sourceRoot}/${projectType}`;
}
// #enddocregion path
// #docregion template
const templateSource = apply(url('./files'), [
applyTemplates({
classify: strings.classify,
dasherize: strings.dasherize,
name: options.name,
}),
move(normalize(options.path as string)),
]);
// #enddocregion template
// #docregion chain
return chain([mergeWith(templateSource)]);
// #enddocregion chain
// #docregion workspace
};
}- interface تعریفشده schema را که اطلاعات نوع گزینههای شماتیک را فراهم میکند import کنید.
// #docplaster
// #docregion schematics-imports, schema-imports, workspace
import {
Rule,
Tree,
SchematicsException,
apply,
url,
applyTemplates,
move,
chain,
mergeWith,
} from '@angular-devkit/schematics';
import {strings, normalize, virtualFs, workspaces} from '@angular-devkit/core';
// #enddocregion schematics-imports
import {Schema as MyServiceSchema} from './schema';
// #enddocregion schema-imports
function createHost(tree: Tree): workspaces.WorkspaceHost {
return {
async readFile(path: string): Promise<string> {
const data = tree.read(path);
if (!data) {
throw new SchematicsException('File not found.');
}
return virtualFs.fileBufferToString(data);
},
async writeFile(path: string, data: string): Promise<void> {
return tree.overwrite(path, data);
},
async isDirectory(path: string): Promise<boolean> {
return !tree.exists(path) && tree.getDir(path).subfiles.length > 0;
},
async isFile(path: string): Promise<boolean> {
return tree.exists(path);
},
};
}
export function myService(options: MyServiceSchema): Rule {
return async (tree: Tree) => {
const host = createHost(tree);
const {workspace} = await workspaces.readWorkspace('/', host);
// #enddocregion workspace
// #docregion project-info
const project = options.project != null ? workspace.projects.get(options.project) : null;
if (!project) {
throw new SchematicsException(`Invalid project name: ${options.project}`);
}
const projectType = project.extensions.projectType === 'application' ? 'app' : 'lib';
// #enddocregion project-info
// #docregion path
if (options.path === undefined) {
options.path = `${project.sourceRoot}/${projectType}`;
}
// #enddocregion path
// #docregion template
const templateSource = apply(url('./files'), [
applyTemplates({
classify: strings.classify,
dasherize: strings.dasherize,
name: options.name,
}),
move(normalize(options.path as string)),
]);
// #enddocregion template
// #docregion chain
return chain([mergeWith(templateSource)]);
// #enddocregion chain
// #docregion workspace
};
}- برای ساخت شماتیک تولید، با یک rule factory خالی شروع کنید.
import {Rule, Tree} from '@angular-devkit/schematics';
import {Schema as MyServiceSchema} from './schema';
// #docregion factory
export function myService(options: MyServiceSchema): Rule {
return (tree: Tree) => tree;
}
// #enddocregion factoryاین rule factory، tree را بدون تغییر برمیگرداند. گزینهها همان مقادیری هستند که از فرمان ng generate ارسال شدهاند.
تعریف rule تولید
اکنون چارچوب لازم برای ایجاد کدی را دارید که application کاربر را واقعاً تغییر میدهد تا برای service تعریفشده در کتابخانه آماده شود.
workspace انگولاری که کاربر کتابخانه شما را در آن نصب کرده شامل چندین پروژه \(application و کتابخانه\) است. کاربر میتواند پروژه را در command line مشخص کند یا اجازه دهد مقدار پیشفرض استفاده شود. در هر دو حالت، کد شما باید پروژه مشخصی را که شماتیک بر آن اعمال میشود شناسایی کند تا بتوانید اطلاعات پیکربندی پروژه را دریافت کنید.
این کار را با شیء Tree ارسالشده به تابع factory انجام دهید. متدهای Tree به شما امکان دسترسی به کل file tree در workspace را میدهند تا هنگام اجرای شماتیک فایلها را بخوانید و بنویسید.
دریافت پیکربندی پروژه
برای استفاده از workspaces.readWorkspace باید یک workspaces.WorkspaceHost از Tree ایجاد کنید. کد زیر را به تابع factory اضافه کنید.
- برای تعیین پروژه مقصد، از متد
workspaces.readWorkspaceبرای خواندن محتوای فایل پیکربندی workspace یعنیangular.jsonاستفاده کنید.
// #docplaster
// #docregion schematics-imports, schema-imports, workspace
import {
Rule,
Tree,
SchematicsException,
apply,
url,
applyTemplates,
move,
chain,
mergeWith,
} from '@angular-devkit/schematics';
import {strings, normalize, virtualFs, workspaces} from '@angular-devkit/core';
// #enddocregion schematics-imports
import {Schema as MyServiceSchema} from './schema';
// #enddocregion schema-imports
function createHost(tree: Tree): workspaces.WorkspaceHost {
return {
async readFile(path: string): Promise<string> {
const data = tree.read(path);
if (!data) {
throw new SchematicsException('File not found.');
}
return virtualFs.fileBufferToString(data);
},
async writeFile(path: string, data: string): Promise<void> {
return tree.overwrite(path, data);
},
async isDirectory(path: string): Promise<boolean> {
return !tree.exists(path) && tree.getDir(path).subfiles.length > 0;
},
async isFile(path: string): Promise<boolean> {
return tree.exists(path);
},
};
}
export function myService(options: MyServiceSchema): Rule {
return async (tree: Tree) => {
const host = createHost(tree);
const {workspace} = await workspaces.readWorkspace('/', host);
// #enddocregion workspace
// #docregion project-info
const project = options.project != null ? workspace.projects.get(options.project) : null;
if (!project) {
throw new SchematicsException(`Invalid project name: ${options.project}`);
}
const projectType = project.extensions.projectType === 'application' ? 'app' : 'lib';
// #enddocregion project-info
// #docregion path
if (options.path === undefined) {
options.path = `${project.sourceRoot}/${projectType}`;
}
// #enddocregion path
// #docregion template
const templateSource = apply(url('./files'), [
applyTemplates({
classify: strings.classify,
dasherize: strings.dasherize,
name: options.name,
}),
move(normalize(options.path as string)),
]);
// #enddocregion template
// #docregion chain
return chain([mergeWith(templateSource)]);
// #enddocregion chain
// #docregion workspace
};
}حتماً وجود context را بررسی کنید و خطای مناسب را صادر کنید.
- اکنون که نام پروژه را دارید، از آن برای دریافت اطلاعات پیکربندی مخصوص پروژه استفاده کنید.
// #docplaster
// #docregion schematics-imports, schema-imports, workspace
import {
Rule,
Tree,
SchematicsException,
apply,
url,
applyTemplates,
move,
chain,
mergeWith,
} from '@angular-devkit/schematics';
import {strings, normalize, virtualFs, workspaces} from '@angular-devkit/core';
// #enddocregion schematics-imports
import {Schema as MyServiceSchema} from './schema';
// #enddocregion schema-imports
function createHost(tree: Tree): workspaces.WorkspaceHost {
return {
async readFile(path: string): Promise<string> {
const data = tree.read(path);
if (!data) {
throw new SchematicsException('File not found.');
}
return virtualFs.fileBufferToString(data);
},
async writeFile(path: string, data: string): Promise<void> {
return tree.overwrite(path, data);
},
async isDirectory(path: string): Promise<boolean> {
return !tree.exists(path) && tree.getDir(path).subfiles.length > 0;
},
async isFile(path: string): Promise<boolean> {
return tree.exists(path);
},
};
}
export function myService(options: MyServiceSchema): Rule {
return async (tree: Tree) => {
const host = createHost(tree);
const {workspace} = await workspaces.readWorkspace('/', host);
// #enddocregion workspace
// #docregion project-info
const project = options.project != null ? workspace.projects.get(options.project) : null;
if (!project) {
throw new SchematicsException(`Invalid project name: ${options.project}`);
}
const projectType = project.extensions.projectType === 'application' ? 'app' : 'lib';
// #enddocregion project-info
// #docregion path
if (options.path === undefined) {
options.path = `${project.sourceRoot}/${projectType}`;
}
// #enddocregion path
// #docregion template
const templateSource = apply(url('./files'), [
applyTemplates({
classify: strings.classify,
dasherize: strings.dasherize,
name: options.name,
}),
move(normalize(options.path as string)),
]);
// #enddocregion template
// #docregion chain
return chain([mergeWith(templateSource)]);
// #enddocregion chain
// #docregion workspace
};
}شیء workspace.projects تمام اطلاعات پیکربندی مخصوص پروژه را در خود دارد.
- مقدار
options.pathتعیین میکند فایلهای template شماتیک پس از اعمال شماتیک به کجا منتقل شوند.
گزینه path در schema شماتیک بهطور پیشفرض با دایرکتوری کاری فعلی جایگزین میشود. اگر path تعریف نشده باشد، از sourceRoot موجود در پیکربندی پروژه همراه با projectType استفاده کنید.
// #docplaster
// #docregion schematics-imports, schema-imports, workspace
import {
Rule,
Tree,
SchematicsException,
apply,
url,
applyTemplates,
move,
chain,
mergeWith,
} from '@angular-devkit/schematics';
import {strings, normalize, virtualFs, workspaces} from '@angular-devkit/core';
// #enddocregion schematics-imports
import {Schema as MyServiceSchema} from './schema';
// #enddocregion schema-imports
function createHost(tree: Tree): workspaces.WorkspaceHost {
return {
async readFile(path: string): Promise<string> {
const data = tree.read(path);
if (!data) {
throw new SchematicsException('File not found.');
}
return virtualFs.fileBufferToString(data);
},
async writeFile(path: string, data: string): Promise<void> {
return tree.overwrite(path, data);
},
async isDirectory(path: string): Promise<boolean> {
return !tree.exists(path) && tree.getDir(path).subfiles.length > 0;
},
async isFile(path: string): Promise<boolean> {
return tree.exists(path);
},
};
}
export function myService(options: MyServiceSchema): Rule {
return async (tree: Tree) => {
const host = createHost(tree);
const {workspace} = await workspaces.readWorkspace('/', host);
// #enddocregion workspace
// #docregion project-info
const project = options.project != null ? workspace.projects.get(options.project) : null;
if (!project) {
throw new SchematicsException(`Invalid project name: ${options.project}`);
}
const projectType = project.extensions.projectType === 'application' ? 'app' : 'lib';
// #enddocregion project-info
// #docregion path
if (options.path === undefined) {
options.path = `${project.sourceRoot}/${projectType}`;
}
// #enddocregion path
// #docregion template
const templateSource = apply(url('./files'), [
applyTemplates({
classify: strings.classify,
dasherize: strings.dasherize,
name: options.name,
}),
move(normalize(options.path as string)),
]);
// #enddocregion template
// #docregion chain
return chain([mergeWith(templateSource)]);
// #enddocregion chain
// #docregion workspace
};
}تعریف rule
یک Rule میتواند از فایلهای template خارجی استفاده کند، آنها را تبدیل کند و شیء Rule دیگری را با template تبدیلشده برگرداند. از templateسازی برای تولید هر فایل سفارشی مورد نیاز شماتیک استفاده کنید.
- کد زیر را به تابع factory اضافه کنید.
// #docplaster
// #docregion schematics-imports, schema-imports, workspace
import {
Rule,
Tree,
SchematicsException,
apply,
url,
applyTemplates,
move,
chain,
mergeWith,
} from '@angular-devkit/schematics';
import {strings, normalize, virtualFs, workspaces} from '@angular-devkit/core';
// #enddocregion schematics-imports
import {Schema as MyServiceSchema} from './schema';
// #enddocregion schema-imports
function createHost(tree: Tree): workspaces.WorkspaceHost {
return {
async readFile(path: string): Promise<string> {
const data = tree.read(path);
if (!data) {
throw new SchematicsException('File not found.');
}
return virtualFs.fileBufferToString(data);
},
async writeFile(path: string, data: string): Promise<void> {
return tree.overwrite(path, data);
},
async isDirectory(path: string): Promise<boolean> {
return !tree.exists(path) && tree.getDir(path).subfiles.length > 0;
},
async isFile(path: string): Promise<boolean> {
return tree.exists(path);
},
};
}
export function myService(options: MyServiceSchema): Rule {
return async (tree: Tree) => {
const host = createHost(tree);
const {workspace} = await workspaces.readWorkspace('/', host);
// #enddocregion workspace
// #docregion project-info
const project = options.project != null ? workspace.projects.get(options.project) : null;
if (!project) {
throw new SchematicsException(`Invalid project name: ${options.project}`);
}
const projectType = project.extensions.projectType === 'application' ? 'app' : 'lib';
// #enddocregion project-info
// #docregion path
if (options.path === undefined) {
options.path = `${project.sourceRoot}/${projectType}`;
}
// #enddocregion path
// #docregion template
const templateSource = apply(url('./files'), [
applyTemplates({
classify: strings.classify,
dasherize: strings.dasherize,
name: options.name,
}),
move(normalize(options.path as string)),
]);
// #enddocregion template
// #docregion chain
return chain([mergeWith(templateSource)]);
// #enddocregion chain
// #docregion workspace
};
}| متدها | جزئیات |
|---|---|
apply() | چندین rule را روی یک source اعمال میکند و source تبدیلشده را برمیگرداند. این متد دو آرگومان میگیرد: یک source و آرایهای از ruleها. |
url() | فایلهای source را نسبت به شماتیک از فایلسیستم میخواند. |
applyTemplates() | آرگومانی از متدها و propertyهایی را میگیرد که میخواهید در دسترس template شماتیک و نام فایلهای شماتیک قرار گیرند و یک Rule برمیگرداند. متدهای classify() و dasherize() و property به نام name را اینجا تعریف میکنید. |
classify() | مقداری را میگیرد و آن را به حالت title case برمیگرداند. برای نمونه، اگر نام ارائهشده my service باشد، خروجی MyService است. |
dasherize() | مقداری را میگیرد و آن را به حالت حروف کوچک و خطتیرهدار برمیگرداند. برای نمونه، اگر نام ارائهشده MyService باشد، خروجی my-service است. |
move() | هنگام اعمال شماتیک، فایلهای source ارائهشده را به مقصد منتقل میکند. |
- در پایان، rule factory باید یک rule برگرداند.
// #docplaster
// #docregion schematics-imports, schema-imports, workspace
import {
Rule,
Tree,
SchematicsException,
apply,
url,
applyTemplates,
move,
chain,
mergeWith,
} from '@angular-devkit/schematics';
import {strings, normalize, virtualFs, workspaces} from '@angular-devkit/core';
// #enddocregion schematics-imports
import {Schema as MyServiceSchema} from './schema';
// #enddocregion schema-imports
function createHost(tree: Tree): workspaces.WorkspaceHost {
return {
async readFile(path: string): Promise<string> {
const data = tree.read(path);
if (!data) {
throw new SchematicsException('File not found.');
}
return virtualFs.fileBufferToString(data);
},
async writeFile(path: string, data: string): Promise<void> {
return tree.overwrite(path, data);
},
async isDirectory(path: string): Promise<boolean> {
return !tree.exists(path) && tree.getDir(path).subfiles.length > 0;
},
async isFile(path: string): Promise<boolean> {
return tree.exists(path);
},
};
}
export function myService(options: MyServiceSchema): Rule {
return async (tree: Tree) => {
const host = createHost(tree);
const {workspace} = await workspaces.readWorkspace('/', host);
// #enddocregion workspace
// #docregion project-info
const project = options.project != null ? workspace.projects.get(options.project) : null;
if (!project) {
throw new SchematicsException(`Invalid project name: ${options.project}`);
}
const projectType = project.extensions.projectType === 'application' ? 'app' : 'lib';
// #enddocregion project-info
// #docregion path
if (options.path === undefined) {
options.path = `${project.sourceRoot}/${projectType}`;
}
// #enddocregion path
// #docregion template
const templateSource = apply(url('./files'), [
applyTemplates({
classify: strings.classify,
dasherize: strings.dasherize,
name: options.name,
}),
move(normalize(options.path as string)),
]);
// #enddocregion template
// #docregion chain
return chain([mergeWith(templateSource)]);
// #enddocregion chain
// #docregion workspace
};
}متد chain() اجازه میدهد چندین rule را در یک rule واحد ترکیب کنید تا بتوانید چندین عملیات را در یک شماتیک انجام دهید. در اینجا فقط ruleهای template را با هر کدی که شماتیک اجرا میکند ادغام میکنید.
نمونه کامل تابع rule شماتیک زیر را ببینید.
// #docplaster
// #docregion schematics-imports, schema-imports, workspace
import {
Rule,
Tree,
SchematicsException,
apply,
url,
applyTemplates,
move,
chain,
mergeWith,
} from '@angular-devkit/schematics';
import {strings, normalize, virtualFs, workspaces} from '@angular-devkit/core';
// #enddocregion schematics-imports
import {Schema as MyServiceSchema} from './schema';
// #enddocregion schema-imports
function createHost(tree: Tree): workspaces.WorkspaceHost {
return {
async readFile(path: string): Promise<string> {
const data = tree.read(path);
if (!data) {
throw new SchematicsException('File not found.');
}
return virtualFs.fileBufferToString(data);
},
async writeFile(path: string, data: string): Promise<void> {
return tree.overwrite(path, data);
},
async isDirectory(path: string): Promise<boolean> {
return !tree.exists(path) && tree.getDir(path).subfiles.length > 0;
},
async isFile(path: string): Promise<boolean> {
return tree.exists(path);
},
};
}
export function myService(options: MyServiceSchema): Rule {
return async (tree: Tree) => {
const host = createHost(tree);
const {workspace} = await workspaces.readWorkspace('/', host);
// #enddocregion workspace
// #docregion project-info
const project = options.project != null ? workspace.projects.get(options.project) : null;
if (!project) {
throw new SchematicsException(`Invalid project name: ${options.project}`);
}
const projectType = project.extensions.projectType === 'application' ? 'app' : 'lib';
// #enddocregion project-info
// #docregion path
if (options.path === undefined) {
options.path = `${project.sourceRoot}/${projectType}`;
}
// #enddocregion path
// #docregion template
const templateSource = apply(url('./files'), [
applyTemplates({
classify: strings.classify,
dasherize: strings.dasherize,
name: options.name,
}),
move(normalize(options.path as string)),
]);
// #enddocregion template
// #docregion chain
return chain([mergeWith(templateSource)]);
// #enddocregion chain
// #docregion workspace
};
}برای اطلاعات بیشتر درباره ruleها و متدهای کمکی، Ruleهای ارائهشده را ببینید.
اجرای شماتیک کتابخانه
پس از ساخت کتابخانه و شماتیکها میتوانید collection شماتیک را نصب و آن را روی پروژه اجرا کنید. مراحل زیر نحوه تولید یک service با شماتیکی را نشان میدهند که پیشتر ایجاد کردید.
ساخت کتابخانه و شماتیکها
از ریشه workspace، فرمان ng build را برای کتابخانه اجرا کنید.
ng build my-libسپس برای ساخت شماتیک وارد دایرکتوری کتابخانه شوید.
cd projects/my-lib
npm run buildلینک کردن کتابخانه
کتابخانه و شماتیکهای شما بستهبندی میشوند و در پوشه dist/my-lib در ریشه workspace قرار میگیرند. برای اجرای شماتیک باید کتابخانه را به پوشه node_modules لینک کنید. از ریشه workspace، فرمان npm link را با مسیر کتابخانه قابل توزیع اجرا کنید.
npm link dist/my-libاجرای شماتیک
اکنون که کتابخانه نصب شده است، شماتیک را با فرمان ng generate اجرا کنید.
ng generate my-lib:my-service --name my-dataدر console میبینید که شماتیک اجرا و فایل my-data.service.ts در پوشه application ایجاد شده است.
CREATE src/app/my-data.service.ts (208 bytes)