diff --git a/src/data/dart_class.ts b/src/data/dart_class.ts index 6a40ca5..fb06eb9 100644 --- a/src/data/dart_class.ts +++ b/src/data/dart_class.ts @@ -30,12 +30,22 @@ class DartClassConstructorField extends DartClassField { constructor( public name: string, public type: string, - public nullable = false, - public named = true, + public positionType = DartClassConstructorFieldPositionType.named, + public nullable = false ) { super(name, type, nullable); } } -export { DartClass, DartClassConstructor, DartClassConstructorField, DartClassField }; +enum DartClassConstructorFieldPositionType { + named, + positional, +} +export { + DartClass, + DartClassConstructor, + DartClassConstructorField, + DartClassConstructorFieldPositionType, + DartClassField, +}; diff --git a/src/generators/file_content/impl/3_0_0_generator.ts b/src/generators/file_content/impl/3_0_0_generator.ts index da81033..7df9d4f 100644 --- a/src/generators/file_content/impl/3_0_0_generator.ts +++ b/src/generators/file_content/impl/3_0_0_generator.ts @@ -1,5 +1,8 @@ import { camelCase } from "change-case"; -import { DartClassConstructor } from "../../../data/dart_class"; +import { + DartClassConstructor, + DartClassConstructorFieldPositionType, +} from "../../../data/dart_class"; import { BaseFileContentGenerator } from "../base_generator"; class FileContentGenerator3_0_0 extends BaseFileContentGenerator { @@ -49,7 +52,7 @@ class FileContentGenerator3_0_0 extends BaseFileContentGenerator { `; for (const field of constructor.fields) { - if (field.named) { + if (field.positionType == DartClassConstructorFieldPositionType.named) { output += `${field.name}: ${this.knobForField(field)},\n`; } else { output += `${this.knobForField(field)},\n`; diff --git a/src/test/dart_class_parser.test.ts b/src/test/dart_class_parser.test.ts index 0cb24e3..18c3090 100644 --- a/src/test/dart_class_parser.test.ts +++ b/src/test/dart_class_parser.test.ts @@ -1,4 +1,9 @@ -import { DartClassConstructor, DartClassConstructorField, DartClassField } from "../data/dart_class"; +import { + DartClassConstructor, + DartClassConstructorField, + DartClassConstructorFieldPositionType, + DartClassField, +} from "../data/dart_class"; import { doesLookingFurtherMakeSense, isConstructorLine, @@ -435,15 +440,22 @@ describe("parseLinesToConstructor", () => { " })", ]; const classFields = [ - new DartClassConstructorField("active", "bool"), - new DartClassConstructorField("semanticsLabel", "String"), + new DartClassField("active", "bool"), + new DartClassField("semanticsLabel", "String"), // class can have more fields which are not in the main constructor part because they are in its initializer list - new DartClassConstructorField("text", "String"), - new DartClassConstructorField("icon", "IconData"), + new DartClassField("text", "String"), + new DartClassField("icon", "IconData"), ]; expect(parseLinesToConstructor(lines, "Loader", classFields)).toEqual( - new DartClassConstructor(false, [classFields[0], classFields[1]], null) + new DartClassConstructor( + false, + [ + new DartClassConstructorField("active", "bool"), + new DartClassConstructorField("semanticsLabel", "String"), + ], + null + ) ); }); @@ -473,18 +485,18 @@ describe("parseLinesToConstructor", () => { " })", ]; const classFields = [ - new DartClassConstructorField("active", "bool"), - new DartClassConstructorField("semanticsLabel", "String"), - new DartClassConstructorField("text", "String"), - new DartClassConstructorField("icon", "IconData"), + new DartClassField("active", "bool"), + new DartClassField("semanticsLabel", "String"), + new DartClassField("text", "String"), + new DartClassField("icon", "IconData"), ]; expect(parseLinesToConstructor(lines, "Loader", classFields)).toEqual( new DartClassConstructor( false, [ - classFields[0], - classFields[1], + new DartClassConstructorField("active", "bool"), + new DartClassConstructorField("semanticsLabel", "String"), new DartClassConstructorField("big", "bool"), new DartClassConstructorField("animated", "bool"), ], @@ -504,18 +516,22 @@ describe("parseLinesToConstructor", () => { " })", ]; const classFields = [ - new DartClassConstructorField("active", "bool"), - new DartClassConstructorField("semanticsLabel", "String"), - new DartClassConstructorField("text", "String"), - new DartClassConstructorField("icon", "IconData"), + new DartClassField("active", "bool"), + new DartClassField("semanticsLabel", "String"), + new DartClassField("text", "String"), + new DartClassField("icon", "IconData"), ]; expect(parseLinesToConstructor(lines, "Loader", classFields)).toEqual( new DartClassConstructor( false, [ - classFields[0], - classFields[1], + new DartClassConstructorField( + "active", + "bool", + DartClassConstructorFieldPositionType.positional + ), + new DartClassConstructorField("semanticsLabel", "String"), new DartClassConstructorField("big", "bool"), new DartClassConstructorField("animated", "bool"), ], @@ -549,17 +565,20 @@ describe("parseLinesToConstructor", () => { " }) : big = false,", ]; const classFields = [ - new DartClassConstructorField("active", "bool"), - new DartClassConstructorField("semanticsLabel", "String"), + new DartClassField("active", "bool"), + new DartClassField("semanticsLabel", "String"), // class can have more fields which are not in the main constructor part because they are in its initializer list - new DartClassConstructorField("text", "String"), - new DartClassConstructorField("icon", "IconData"), + new DartClassField("text", "String"), + new DartClassField("icon", "IconData"), ]; expect(parseLinesToConstructor(lines, "Loader", classFields)).toEqual( new DartClassConstructor( true, - [classFields[0], classFields[1]], + [ + new DartClassConstructorField("active", "bool"), + new DartClassConstructorField("semanticsLabel", "String"), + ], "small" ) ); @@ -576,18 +595,18 @@ describe("parseLinesToConstructor", () => { " })", ]; const classFields = [ - new DartClassConstructorField("active", "bool"), - new DartClassConstructorField("semanticsLabel", "String"), - new DartClassConstructorField("text", "String"), - new DartClassConstructorField("icon", "IconData"), + new DartClassField("active", "bool"), + new DartClassField("semanticsLabel", "String"), + new DartClassField("text", "String"), + new DartClassField("icon", "IconData"), ]; expect(parseLinesToConstructor(lines, "Loader", classFields)).toEqual( new DartClassConstructor( true, [ - classFields[0], - classFields[1], + new DartClassConstructorField("active", "bool"), + new DartClassConstructorField("semanticsLabel", "String"), new DartClassConstructorField("big", "bool"), new DartClassConstructorField("animated", "bool"), ], @@ -596,30 +615,37 @@ describe("parseLinesToConstructor", () => { ); }); - test("with positional fields and named fields", () => { const lines = [ " const Loader.small(", - " this.active, {", + " this.active,", + " this.semanticsLabel, {", " super.key,", - " this.semanticsLabel,", " bool big = false,", " required bool animated,", " })", ]; const classFields = [ - new DartClassConstructorField("active", "bool"), - new DartClassConstructorField("semanticsLabel", "String"), - new DartClassConstructorField("text", "String"), - new DartClassConstructorField("icon", "IconData"), + new DartClassField("active", "bool"), + new DartClassField("semanticsLabel", "String"), + new DartClassField("text", "String"), + new DartClassField("icon", "IconData"), ]; expect(parseLinesToConstructor(lines, "Loader", classFields)).toEqual( new DartClassConstructor( true, [ - classFields[0], - classFields[1], + new DartClassConstructorField( + "active", + "bool", + DartClassConstructorFieldPositionType.positional + ), + new DartClassConstructorField( + "semanticsLabel", + "String", + DartClassConstructorFieldPositionType.positional + ), new DartClassConstructorField("big", "bool"), new DartClassConstructorField("animated", "bool"), ], diff --git a/src/util/dart_class_parser.ts b/src/util/dart_class_parser.ts index 79bef32..a5fa339 100644 --- a/src/util/dart_class_parser.ts +++ b/src/util/dart_class_parser.ts @@ -2,6 +2,7 @@ import { DartClass, DartClassConstructor, DartClassConstructorField, + DartClassConstructorFieldPositionType, DartClassField, } from "../data/dart_class"; @@ -134,131 +135,88 @@ function parseLinesToConstructor( const constructorName = isNamed ? nameLine.substring(nameLine.indexOf(".") + 1, nameLine.indexOf("(")) : null; - const namedParametersStartIndex = lines.findIndex((line) => - line.includes("{") - ); const fieldsLines = lines .join("") .split(",") // TODO Modify the line below when adding support for one-line constructors .filter((line) => line !== "" && !line.includes("})")); - - const positionalClassFieldsLines = fieldsLines.filter( - (line) => - line.includes(classFieldReference) && - fieldsLines.indexOf(line) <= namedParametersStartIndex - ); - const namedClassFieldsLines = fieldsLines.filter( - (line) => - line.includes(classFieldReference) && - fieldsLines.indexOf(line) > namedParametersStartIndex - ); - - const classFields = getConstructorClassFields( - positionalClassFieldsLines, - namedClassFieldsLines, - allClassFields - ); - - const customFieldsLines = fieldsLines.filter( - (line) => !line.includes(classFieldReference) && !line.includes("super.") + const namedParametersStartIndex = fieldsLines.findIndex((line) => + line.includes("{") ); - const customFields = customFieldsLines.map((line) => { - const named = lines.indexOf(line) > namedParametersStartIndex; - - const lineParts = line.trim().split(" "); - let name: string; - let type: string; - if (lineParts[0] === "required") { - name = lineParts[2]; - type = lineParts[1]; - } else { - name = lineParts[1]; - type = lineParts[0]; - } - - if (type.endsWith("?")) { - return new DartClassConstructorField( - name, - type.removeTrailing(1), - true, - named - ); - } - - return new DartClassConstructorField(name, type, false, named); - }); + const fields = fieldsLines + .map((line) => { + const positionType = + fieldsLines.indexOf(line) > namedParametersStartIndex + ? DartClassConstructorFieldPositionType.named + : DartClassConstructorFieldPositionType.positional; + const custom = + !line.includes(classFieldReference) && !line.includes("super."); + + if (custom) { + const lineParts = line.trim().split(" "); + let name: string; + let type: string; + if (lineParts[0] === "required") { + name = lineParts[2]; + type = lineParts[1]; + } else { + name = lineParts[1]; + type = lineParts[0]; + } + + if (type.endsWith("?")) { + return new DartClassConstructorField( + name, + type.removeTrailing(1), + positionType, + true + ); + } + + return new DartClassConstructorField(name, type, positionType, false); + } else { + const fieldName = getClassFieldName(line, classFieldReference); + const classField = allClassFields.find( + (field) => field.name === fieldName + ); + + if (classField) { + return classFieldToConstructorField(classField, positionType); + } + + return null; + } + }) + .whereType(); return new DartClassConstructor( constructorName !== null, - [...classFields, ...customFields], + fields, constructorName ); } -function getConstructorClassFields( - positionalClassFieldsLines: Array, - namedClassFieldsLines: Array, - allClassFields: Array -): Array { - const classFieldReference = "this."; - - const positionalClassFieldsNames = getClassFieldNames( - positionalClassFieldsLines, - classFieldReference - ); - const namedClassFieldsNames = getClassFieldNames( - namedClassFieldsLines, - classFieldReference - ); - - const positionalClassFields = positionalClassFieldsNames - .map((fieldName) => - allClassFields.find((field) => field.name === fieldName) - ) - .whereType() - .map((field) => { - return classFieldToConstructorField(field, false); - }); - - const namedClassFields = namedClassFieldsNames - .map((fieldName) => - allClassFields.find((field) => field.name === fieldName) - ) - .whereType() - .map((field) => { - return classFieldToConstructorField(field, true); - }); - - return [...namedClassFields, ...positionalClassFields]; -} - function classFieldToConstructorField( classField: DartClassField, - named: boolean + positionType: DartClassConstructorFieldPositionType ): DartClassConstructorField { return new DartClassConstructorField( classField.name, classField.type, - classField.nullable, - named + positionType, + classField.nullable ); } -function getClassFieldNames( - lines: Array, - classFieldReference: string -): Array { - return lines.map((line) => { - const lineFromFieldName = line.substringAfter(classFieldReference); - const hasDefaultValue = lineFromFieldName.includes(" = "); - if (hasDefaultValue) { - return lineFromFieldName.substring(0, lineFromFieldName.indexOf(" = ")); - } - return lineFromFieldName; - }); +function getClassFieldName(line: string, classFieldReference: string): string { + const lineFromFieldName = line.substringAfter(classFieldReference); + const hasDefaultValue = lineFromFieldName.includes(" = "); + if (hasDefaultValue) { + return lineFromFieldName.substring(0, lineFromFieldName.indexOf(" = ")); + } + return lineFromFieldName; } function doesLookingFurtherMakeSense(line: string): boolean {