我想为 Angular 创建一个自定义原理图,它将在执行原理图的同一目录中创建一个文件。我遵循了这篇文章,但我不确定如何将新文件添加到所需的目录中。 (https://developer.okta.com/blog/2019/02/13/angular-schematics#create-your-first-schematic)
例如,如果我有以下目录结构:
src/app/ !-- Angular Root
|- modules
|_ foo
|- foo.component.ts
|_ foo.component.html
|- app.component.ts
|- app.component.html
|_ app.module.ts
如果我执行以下命令:
> cd /src/app/modules/foo
> ng g my-component:my-component
我希望新创建/更新的文件位于 /src/app/modules/foo 目录而不是根目录中。想想 ng generated 是如何工作的。如果我从目录 /src/app/modules/foo 中执行
ng g component bar
,那么将在该目录中生成一个新组件。这是我需要复制的行为。
这是我的工厂。现在它显然是用
project.root
来定位根目录,但是我没有找到任何替代方案,如果我不提供路径,则会收到错误。如何获取当前路径(src/app/modules/foo)以存储在options.path
中?
export function setupOptions(host: Tree, options: any): Tree {
const workspace = getWorkspace(host);
if (!options.project) {
options.project = Object.keys(workspace.projects)[0];
}
const project = workspace.projects[options.project];
options.path = join(normalize(project.root), 'src');
return host;
}
// You don't have to export the function as default. You can also have more than one rule factory
// per file.
export function myComponent(_options: any): Rule {
return (tree: Tree, _context: SchematicContext) => {
setupOptions(tree, _options);
const movePath = normalize(_options.path + '/');
const templateSource = apply(url('./files/src'), [
template({ ..._options }),
move(movePath),
forEach((fileEntry: FileEntry) => {
if (tree.exists(fileEntry.path)) {
tree.overwrite(fileEntry.path, fileEntry.content);
}
return fileEntry;
})
]);
const rule = mergeWith(templateSource, MergeStrategy.Overwrite);
return rule(tree, _context);
};
}
将其添加到您的 schema.json
{
...,
"properties": {
..
"path": {
"type": "string",
"format": "path",
"description": "The path to create the simple schematic within.",
"visible": false
}
您会遇到一些问题。例如
1) constworkspaceConfig = tree.read('/angular.json');
// 在使用“schematics”命令时将为空,但在使用“ng g”命令时有效。
2) 同样,当使用“schematics”命令时,“options.path”将是未定义的,但在使用“ng g”命令时将起作用。
上面的回复是正确的,您需要添加 schema.json 文件的路径,然后在您的函数中
'导出函数 myComponent(_options: any): 规则 {'
您应该能够使用“options.path”来获取当前位置。然而,正如我所提到的,在使用“原理图”命令时我无法让它工作。我只能在使用“ng g”命令时才能使其工作。
作为示例,这是我的文件
1)..schematics/ng-generate/customComponent/schema.json
{
"$schema": "http://json-schema.org/schema",
"id": "GenerateCustomComponent",
"title": "Generate Custom Component",
"type": "object",
"properties": {
"name": {
"description": "The name for the custom component.",
"type": "string",
"x-prompt": "What is the name for the custom component?"
},
"path": {
"type": "string",
"format": "path",
"description": "The path at which to create the component file, relative to the current workspace. Default is a folder with the same name as the component in the project root.",
"visible": false
}
},
"required": [
"name"
]
}
2)..schematics/ng-generate/customComponent/schema.ts
import { Schema as ComponentSChema } from '@schematics/angular/component/schema';
export interface Schema extends ComponentSChema {
// The name of the custom component
name: string;
}
2)..schematics/ng-generate/customComponent/index.ts
import {
Rule, Tree, SchematicsException,
apply, url, applyTemplates, move,
chain, mergeWith
} from '@angular-devkit/schematics';
import { strings, experimental, normalize } from '@angular-devkit/core';
import { Schema as CustomSchema } from './schema';
export function generate(options: CustomSchema): Rule {
return (tree: Tree) => {
const workspaceConfig = tree.read('/angular.json'); // will return null when using schematics command but will work when using ng g
console.log('workspaceConfig::', workspaceConfig);
console.log('path:', options.path); // will be undefined when using schematics command but will work when using ng g
// from now following along with angular docs with slight modifications.
if (workspaceConfig && !options.path) {
const workspaceContent = workspaceConfig.toString();
console.log('workspaceContent::', workspaceContent);
const workspace: experimental.workspace.WorkspaceSchema = JSON.parse(workspaceContent);
console.log('workspace', workspace);
options.project = workspace.defaultProject;
const projectName = options.project as string;
const project = workspace.projects[projectName];
const projectType = project.projectType === 'application' ? 'app' : 'lib';
console.log('projectType::', projectType);
options.path = `${project.sourceRoot}/${projectType}`;
}
if (options.path) {
// this will be used by the ng g command
const templateSource = apply(url('./files'), [
applyTemplates({
classify: strings.classify,
dasherize: strings.dasherize,
name: options.name
}),
move(normalize(options.path as string))
]);
return chain([
mergeWith(templateSource)
]);
} else {
// this will be used by the schematics command
const templateSource = apply(url('./files'), [
applyTemplates({
classify: strings.classify,
dasherize: strings.dasherize,
name: options.name
})
]);
return chain([
mergeWith(templateSource)
]);
}
};
}
有件事让我困惑了一段时间并需要注意。
在 Angular 项目的 Root中执行原理图命令 时,'options.path' 将是未定义的。只有当您将目录更改为根目录的子目录时,该路径才可用。
可能这是丑陋的解决方案,但我找到它的唯一方法。
您必须更改树的根目录才能访问当前目录的父目录
const currentPath = (tree as any)['_backend']['_root'] ;
(tree as any)['_backend']['_root'] = '/' ;
const currentDir = tree.getDir(currentPath);
const parent = currentDir.parent ;
知道您可以访问父目录并可以编写自己的函数来更改所有内容
见样本
const rootModelDirectory = 'model-form-request'
export function model(_options: any): Rule {
return (tree: Tree, _context: SchematicContext) => {
const currentPath = (tree as any)['_backend']['_root'] ;
(tree as any)['_backend']['_root'] = '/' ;
console.log(currentPath);
const rootdir = findParentDirectoryPath(tree.getDir(currentPath) , rootModelDirectory);
const templateSource = apply(url('./files'), [
// filter(path => path.endsWith('__name@dasherize__.module.ts.template')),
template({
...strings,
..._options
}),
move(normalize(`/${rootdir}/${dasherize(_options.name)}/`))
]);
return mergeWith(templateSource);
};
}
export function findParentDirectoryPath(location :ReturnType<Tree['getDir']>, directoryName : string): string {
for(const dirPath of location.subdirs){
if(new RegExp(directoryName).test(dirPath)){
return `${location.path}/${dirPath}` ;
}
}
if(location.parent){
return findParentDirectoryPath(location.parent , directoryName);
}
throw new Error('root model directory not found')
}
对于仍在寻找解决方案的任何人,您需要将
$default
属性添加到 path
文件中的 schema.json
定义。
{
"$schema": "http://json-schema.org/schema",
"$id": "SchematicsMyService",
"title": "My Service Schema",
"type": "object",
"properties": {
"path": {
"type": "string",
"format": "path",
"description": "The path to create the service.",
"visible": false,
"$default": {
"$source": "workingDirectory"
}
}
}
}
未提供路径时,这会将默认值更改为工作目录。
这就是 Angular 默认原理图的工作原理。
https://github.com/angular/angular-cli/blob/main/packages/schematics/angular/service/schema.json