I try to add new propery to my awesome.model.ts
file.
The original content is like this:
import { TagInterface, TagUIFieldsInterface } from './tag.interface';
export class Tag implements TagInterface {
readonly api_endpoint = '/tag';
id: ID;
name: string;
fields: FieldContainerInterface<TagUIFieldsInterface> = {
// ...
};
init(data?: any): TagInterface {
// ...
}
}
I want to add a new propery color_code: string;
after the name
property's line. To look like this:
import { TagInterface, TagUIFieldsInterface } from './tag.interface';
export class Tag implements TagInterface {
readonly api_endpoint = '/tag';
id: ID;
name: string;
color_code: string;
fields: FieldContainerInterface<TagUIFieldsInterface> = {
// ...
};
init(data?: any): TagInterface {
// ...
}
}
In my Schematics rule function I tried this, but I'm stucked:
export function model(_options: Schema, _fields?: Field[]): Rule {
return (tree: Tree, _context: SchematicContext) => {
// ...
if (tree.exists(file)) {
// read the file content and convert it to ts.Node[]
const text = tree.read(file) ?? '';
let sourceText = text.toString('utf-8');
const sourceFile = ts.createSourceFile(file, sourceText, ts.ScriptTarget.Latest, true);
const nodes = getSourceNodes(sourceFile);
updateModelFields(file, nodes, _options);
return;
}
}
And here is the updateModelFields()
function:
export function updateModelFields(file: string, nodes: ts.Node[], options: Schema) {
// find field definitions
let propertyNodes: ts.Node[] = nodes.filter(n => n.kind === ts.SyntaxKind.PropertyDeclaration) || [];
// create new property declaration
const propertyDeclaration = ts.factory.createPropertyDeclaration(
undefined,
undefined,
'color_code',
undefined,
ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
undefined
);
// add propertyDeclaration to nodes
// ??
}
I tried several ways to add the new property declaration, but always get faild.
When I tried to add with splice()
function it said:
Error: Debug Failure. False expression: Node must have a real position for this operation
Any idea or best practice?
Generally if using the transformation API, this would be used with the
ts.transform
function then usefactory.updateClassDeclaration
to add the property to the class. Once you have the final transformed AST, you can print it out to a string by using the printer (ts.createPrinter
).That said, the transformation API wasn't designed for this purpose—it's meant to transform TS code to JS—and so it's not great at modifying existing TypeScript files. For example, if you transform an AST then print it out, you will lose your formatting information and it might trample over existing comments.
For that reason, I would suggest instead to use the text change API (see
SourceFile#update
)—this is what is used for quick fixes—or more simply just insert the property text into the right position in the string directly. You can figure out where to insert based on the surrounding node positions. For example:Alternatively, you may just want to use ts-morph to handle this for you as it's quite easy to insert properties into a class using it.