Parse and validate AT Protocol Lexicons with DTO generation for Laravel
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Generate conversion methods for data classes

+633 -64
+17 -62
src/Generator/ClassGenerator.php
··· 23 23 protected StubRenderer $renderer; 24 24 25 25 /** 26 + * Method generator instance. 27 + */ 28 + protected MethodGenerator $methodGenerator; 29 + 30 + /** 26 31 * Create a new ClassGenerator. 27 32 */ 28 33 public function __construct( 29 34 ?NamingConverter $naming = null, 30 35 ?TypeMapper $typeMapper = null, 31 - ?StubRenderer $renderer = null 36 + ?StubRenderer $renderer = null, 37 + ?MethodGenerator $methodGenerator = null 32 38 ) { 33 39 $this->naming = $naming ?? new NamingConverter; 34 40 $this->typeMapper = $typeMapper ?? new TypeMapper($this->naming); 35 41 $this->renderer = $renderer ?? new StubRenderer; 42 + $this->methodGenerator = $methodGenerator ?? new MethodGenerator($this->naming, $this->typeMapper, $this->renderer); 36 43 } 37 44 38 45 /** ··· 155 162 */ 156 163 protected function generateMethods(LexiconDocument $document): string 157 164 { 158 - $methods = []; 159 - 160 - // Generate getLexicon method 161 - $methods[] = $this->generateGetLexiconMethod($document); 162 - 163 - // Generate fromArray method 164 - $methods[] = $this->generateFromArrayMethod($document); 165 + $methods = $this->methodGenerator->generateAll($document); 165 166 166 167 return implode("\n\n", $methods); 167 168 } 168 169 169 170 /** 170 - * Generate getLexicon method. 171 - */ 172 - protected function generateGetLexiconMethod(LexiconDocument $document): string 173 - { 174 - $nsid = $document->getNsid(); 175 - 176 - return " public static function getLexicon(): string\n". 177 - " {\n". 178 - " return '{$nsid}';\n". 179 - " }"; 180 - } 181 - 182 - /** 183 - * Generate fromArray method. 184 - */ 185 - protected function generateFromArrayMethod(LexiconDocument $document): string 186 - { 187 - $mainDef = $document->getMainDefinition(); 188 - $properties = $mainDef['properties'] ?? []; 189 - 190 - if (empty($properties)) { 191 - return " public static function fromArray(array \$data): static\n". 192 - " {\n". 193 - " return new static();\n". 194 - " }"; 195 - } 196 - 197 - $assignments = []; 198 - foreach ($properties as $name => $propDef) { 199 - $type = $propDef['type'] ?? 'unknown'; 200 - 201 - // Handle nested objects/refs 202 - if ($type === 'ref' && isset($propDef['ref'])) { 203 - $refClass = $this->naming->nsidToClassName($propDef['ref']); 204 - $refClassName = basename(str_replace('\\', '/', $refClass)); 205 - $assignments[] = " {$name}: isset(\$data['{$name}']) ? {$refClassName}::fromArray(\$data['{$name}']) : null,"; 206 - } elseif ($type === 'array' && isset($propDef['items']['type']) && $propDef['items']['type'] === 'ref') { 207 - $refClass = $this->naming->nsidToClassName($propDef['items']['ref']); 208 - $refClassName = basename(str_replace('\\', '/', $refClass)); 209 - $assignments[] = " {$name}: isset(\$data['{$name}']) ? array_map(fn (\$item) => {$refClassName}::fromArray(\$item), \$data['{$name}']) : [],"; 210 - } else { 211 - $assignments[] = " {$name}: \$data['{$name}'] ?? null,"; 212 - } 213 - } 214 - 215 - return " public static function fromArray(array \$data): static\n". 216 - " {\n". 217 - " return new static(\n". 218 - implode("\n", $assignments)."\n". 219 - " );\n". 220 - " }"; 221 - } 222 - 223 - /** 224 171 * Generate class-level documentation block. 225 172 * 226 173 * @param array<string, mixed> $definition ··· 315 262 public function getRenderer(): StubRenderer 316 263 { 317 264 return $this->renderer; 265 + } 266 + 267 + /** 268 + * Get the method generator. 269 + */ 270 + public function getMethodGenerator(): MethodGenerator 271 + { 272 + return $this->methodGenerator; 318 273 } 319 274 }
+272
src/Generator/MethodGenerator.php
··· 1 + <?php 2 + 3 + namespace SocialDept\Schema\Generator; 4 + 5 + use SocialDept\Schema\Data\LexiconDocument; 6 + 7 + class MethodGenerator 8 + { 9 + /** 10 + * Naming converter instance. 11 + */ 12 + protected NamingConverter $naming; 13 + 14 + /** 15 + * Type mapper instance. 16 + */ 17 + protected TypeMapper $typeMapper; 18 + 19 + /** 20 + * Stub renderer instance. 21 + */ 22 + protected StubRenderer $renderer; 23 + 24 + /** 25 + * Create a new MethodGenerator. 26 + */ 27 + public function __construct( 28 + ?NamingConverter $naming = null, 29 + ?TypeMapper $typeMapper = null, 30 + ?StubRenderer $renderer = null 31 + ) { 32 + $this->naming = $naming ?? new NamingConverter; 33 + $this->typeMapper = $typeMapper ?? new TypeMapper($this->naming); 34 + $this->renderer = $renderer ?? new StubRenderer; 35 + } 36 + 37 + /** 38 + * Generate all standard methods for a data class. 39 + * 40 + * @return array<string> 41 + */ 42 + public function generateAll(LexiconDocument $document): array 43 + { 44 + return [ 45 + $this->generateGetLexicon($document), 46 + $this->generateFromArray($document), 47 + ]; 48 + } 49 + 50 + /** 51 + * Generate getLexicon method. 52 + */ 53 + public function generateGetLexicon(LexiconDocument $document): string 54 + { 55 + $nsid = $document->getNsid(); 56 + 57 + return $this->renderer->render('method', [ 58 + 'docBlock' => $this->generateDocBlock('Get the lexicon NSID for this data type.', 'string'), 59 + 'visibility' => 'public ', 60 + 'static' => 'static ', 61 + 'name' => 'getLexicon', 62 + 'parameters' => '', 63 + 'returnType' => ': string', 64 + 'body' => " return '{$nsid}';", 65 + ]); 66 + } 67 + 68 + /** 69 + * Generate fromArray method. 70 + */ 71 + public function generateFromArray(LexiconDocument $document): string 72 + { 73 + $mainDef = $document->getMainDefinition(); 74 + $properties = $mainDef['properties'] ?? []; 75 + $required = $mainDef['required'] ?? []; 76 + 77 + if (empty($properties)) { 78 + return $this->generateEmptyFromArray(); 79 + } 80 + 81 + $assignments = $this->generateFromArrayAssignments($properties, $required); 82 + $body = " return new static(\n".$assignments."\n );"; 83 + 84 + return $this->renderer->render('method', [ 85 + 'docBlock' => $this->generateDocBlock('Create an instance from an array.', 'static', [ 86 + ['name' => 'data', 'type' => 'array', 'description' => 'The data array'], 87 + ]), 88 + 'visibility' => 'public ', 89 + 'static' => 'static ', 90 + 'name' => 'fromArray', 91 + 'parameters' => 'array $data', 92 + 'returnType' => ': static', 93 + 'body' => $body, 94 + ]); 95 + } 96 + 97 + /** 98 + * Generate fromArray for empty properties. 99 + */ 100 + protected function generateEmptyFromArray(): string 101 + { 102 + return $this->renderer->render('method', [ 103 + 'docBlock' => $this->generateDocBlock('Create an instance from an array.', 'static', [ 104 + ['name' => 'data', 'type' => 'array', 'description' => 'The data array'], 105 + ]), 106 + 'visibility' => 'public ', 107 + 'static' => 'static ', 108 + 'name' => 'fromArray', 109 + 'parameters' => 'array $data', 110 + 'returnType' => ': static', 111 + 'body' => ' return new static();', 112 + ]); 113 + } 114 + 115 + /** 116 + * Generate assignments for fromArray method. 117 + * 118 + * @param array<string, array<string, mixed>> $properties 119 + * @param array<string> $required 120 + */ 121 + protected function generateFromArrayAssignments(array $properties, array $required): string 122 + { 123 + $lines = []; 124 + 125 + foreach ($properties as $name => $definition) { 126 + $type = $definition['type'] ?? 'unknown'; 127 + $assignment = $this->generatePropertyAssignment($name, $definition, $type, $required); 128 + $lines[] = ' '.$name.': '.$assignment.','; 129 + } 130 + 131 + // Remove trailing comma from last line 132 + if (! empty($lines)) { 133 + $lines[count($lines) - 1] = rtrim($lines[count($lines) - 1], ','); 134 + } 135 + 136 + return implode("\n", $lines); 137 + } 138 + 139 + /** 140 + * Generate assignment for a property in fromArray. 141 + * 142 + * @param array<string, mixed> $definition 143 + * @param array<string> $required 144 + */ 145 + protected function generatePropertyAssignment(string $name, array $definition, string $type, array $required): string 146 + { 147 + $isRequired = in_array($name, $required); 148 + 149 + // Handle reference types 150 + if ($type === 'ref' && isset($definition['ref'])) { 151 + $refClass = $this->naming->nsidToClassName($definition['ref']); 152 + $className = basename(str_replace('\\', '/', $refClass)); 153 + 154 + if ($isRequired) { 155 + return "{$className}::fromArray(\$data['{$name}'])"; 156 + } 157 + 158 + return "isset(\$data['{$name}']) ? {$className}::fromArray(\$data['{$name}']) : null"; 159 + } 160 + 161 + // Handle arrays of references 162 + if ($type === 'array' && isset($definition['items']['type']) && $definition['items']['type'] === 'ref') { 163 + $refClass = $this->naming->nsidToClassName($definition['items']['ref']); 164 + $className = basename(str_replace('\\', '/', $refClass)); 165 + 166 + return "isset(\$data['{$name}']) ? array_map(fn (\$item) => {$className}::fromArray(\$item), \$data['{$name}']) : []"; 167 + } 168 + 169 + // Handle arrays of objects 170 + if ($type === 'array' && isset($definition['items']['type']) && $definition['items']['type'] === 'object') { 171 + return "\$data['{$name}'] ?? []"; 172 + } 173 + 174 + // Handle DateTime types (if string format matches ISO8601) 175 + if ($type === 'string' && isset($definition['format']) && $definition['format'] === 'datetime') { 176 + if ($isRequired) { 177 + return "new \\DateTime(\$data['{$name}'])"; 178 + } 179 + 180 + return "isset(\$data['{$name}']) ? new \\DateTime(\$data['{$name}']) : null"; 181 + } 182 + 183 + // Default: simple property access 184 + if ($isRequired) { 185 + return "\$data['{$name}']"; 186 + } 187 + 188 + return "\$data['{$name}'] ?? null"; 189 + } 190 + 191 + /** 192 + * Generate a generic method. 193 + * 194 + * @param array<array{name: string, type: string, description?: string}> $params 195 + */ 196 + public function generate( 197 + string $name, 198 + string $returnType, 199 + string $body, 200 + ?string $description = null, 201 + array $params = [], 202 + bool $isStatic = false 203 + ): string { 204 + $parameters = $this->formatParameters($params); 205 + 206 + return $this->renderer->render('method', [ 207 + 'docBlock' => $this->generateDocBlock($description, $returnType, $params), 208 + 'visibility' => 'public ', 209 + 'static' => $isStatic ? 'static ' : '', 210 + 'name' => $name, 211 + 'parameters' => $parameters, 212 + 'returnType' => $returnType ? ': '.$returnType : '', 213 + 'body' => $body, 214 + ]); 215 + } 216 + 217 + /** 218 + * Generate method documentation block. 219 + * 220 + * @param array<array{name: string, type: string, description?: string}> $params 221 + */ 222 + protected function generateDocBlock(?string $description, ?string $returnType, array $params = []): string 223 + { 224 + $lines = [' /**']; 225 + 226 + if ($description) { 227 + $lines[] = ' * '.$description; 228 + 229 + if (! empty($params) || $returnType) { 230 + $lines[] = ' *'; 231 + } 232 + } 233 + 234 + // Add parameter docs 235 + foreach ($params as $param) { 236 + $desc = $param['description'] ?? ''; 237 + if ($desc) { 238 + $lines[] = ' * @param '.$param['type'].' $'.$param['name'].' '.$desc; 239 + } else { 240 + $lines[] = ' * @param '.$param['type'].' $'.$param['name']; 241 + } 242 + } 243 + 244 + // Add return type 245 + if ($returnType && $returnType !== 'void') { 246 + $lines[] = ' * @return '.$returnType; 247 + } 248 + 249 + $lines[] = ' */'; 250 + 251 + return implode("\n", $lines); 252 + } 253 + 254 + /** 255 + * Format method parameters. 256 + * 257 + * @param array<array{name: string, type: string, description?: string}> $params 258 + */ 259 + protected function formatParameters(array $params): string 260 + { 261 + if (empty($params)) { 262 + return ''; 263 + } 264 + 265 + $formatted = []; 266 + foreach ($params as $param) { 267 + $formatted[] = $param['type'].' $'.$param['name']; 268 + } 269 + 270 + return implode(', ', $formatted); 271 + } 272 + }
+1 -1
stubs/method.stub
··· 1 1 {{ docBlock }} 2 - {{ visibility }}{{ static }} function {{ name }}({{ parameters }}){{ returnType }} 2 + {{ visibility }}{{ static }}function {{ name }}({{ parameters }}){{ returnType }} 3 3 { 4 4 {{ body }} 5 5 }
+1 -1
tests/Unit/Generator/ClassGeneratorTest.php
··· 89 89 90 90 $this->assertStringContainsString('public static function fromArray(array $data): static', $code); 91 91 $this->assertStringContainsString('return new static(', $code); 92 - $this->assertStringContainsString("text: \$data['text'] ?? null", $code); 92 + $this->assertStringContainsString("text: \$data['text']", $code); 93 93 } 94 94 95 95 public function test_it_includes_use_statements(): void
+342
tests/Unit/Generator/MethodGeneratorTest.php
··· 1 + <?php 2 + 3 + namespace SocialDept\Schema\Tests\Unit\Generator; 4 + 5 + use Orchestra\Testbench\TestCase; 6 + use SocialDept\Schema\Data\LexiconDocument; 7 + use SocialDept\Schema\Generator\MethodGenerator; 8 + use SocialDept\Schema\Parser\Nsid; 9 + 10 + class MethodGeneratorTest extends TestCase 11 + { 12 + protected MethodGenerator $generator; 13 + 14 + protected function setUp(): void 15 + { 16 + parent::setUp(); 17 + 18 + $this->generator = new MethodGenerator; 19 + } 20 + 21 + public function test_it_generates_get_lexicon_method(): void 22 + { 23 + $document = $this->createDocument('app.test.post', [ 24 + 'type' => 'record', 25 + 'properties' => [], 26 + ]); 27 + 28 + $method = $this->generator->generateGetLexicon($document); 29 + 30 + $this->assertStringContainsString('public static function getLexicon(): string', $method); 31 + $this->assertStringContainsString("return 'app.test.post';", $method); 32 + } 33 + 34 + public function test_it_generates_from_array_with_properties(): void 35 + { 36 + $document = $this->createDocument('app.test.user', [ 37 + 'type' => 'record', 38 + 'properties' => [ 39 + 'name' => ['type' => 'string'], 40 + 'age' => ['type' => 'integer'], 41 + ], 42 + 'required' => ['name', 'age'], 43 + ]); 44 + 45 + $method = $this->generator->generateFromArray($document); 46 + 47 + $this->assertStringContainsString('public static function fromArray(array $data): static', $method); 48 + $this->assertStringContainsString('return new static(', $method); 49 + $this->assertStringContainsString('name: $data[\'name\']', $method); 50 + $this->assertStringContainsString('age: $data[\'age\']', $method); 51 + } 52 + 53 + public function test_it_generates_from_array_with_optional_properties(): void 54 + { 55 + $document = $this->createDocument('app.test.user', [ 56 + 'type' => 'record', 57 + 'properties' => [ 58 + 'name' => ['type' => 'string'], 59 + 'nickname' => ['type' => 'string'], 60 + ], 61 + 'required' => ['name'], 62 + ]); 63 + 64 + $method = $this->generator->generateFromArray($document); 65 + 66 + $this->assertStringContainsString('name: $data[\'name\']', $method); 67 + $this->assertStringContainsString('nickname: $data[\'nickname\'] ?? null', $method); 68 + } 69 + 70 + public function test_it_handles_ref_types_in_from_array(): void 71 + { 72 + $document = $this->createDocument('app.test.post', [ 73 + 'type' => 'record', 74 + 'properties' => [ 75 + 'author' => [ 76 + 'type' => 'ref', 77 + 'ref' => 'app.test.author', 78 + ], 79 + ], 80 + 'required' => ['author'], 81 + ]); 82 + 83 + $method = $this->generator->generateFromArray($document); 84 + 85 + $this->assertStringContainsString('Author::fromArray($data[\'author\'])', $method); 86 + } 87 + 88 + public function test_it_handles_optional_ref_types(): void 89 + { 90 + $document = $this->createDocument('app.test.post', [ 91 + 'type' => 'record', 92 + 'properties' => [ 93 + 'author' => [ 94 + 'type' => 'ref', 95 + 'ref' => 'app.test.author', 96 + ], 97 + ], 98 + 'required' => [], 99 + ]); 100 + 101 + $method = $this->generator->generateFromArray($document); 102 + 103 + $this->assertStringContainsString('isset($data[\'author\']) ? Author::fromArray($data[\'author\']) : null', $method); 104 + } 105 + 106 + public function test_it_handles_array_of_refs(): void 107 + { 108 + $document = $this->createDocument('app.test.feed', [ 109 + 'type' => 'record', 110 + 'properties' => [ 111 + 'posts' => [ 112 + 'type' => 'array', 113 + 'items' => [ 114 + 'type' => 'ref', 115 + 'ref' => 'app.test.post', 116 + ], 117 + ], 118 + ], 119 + 'required' => ['posts'], 120 + ]); 121 + 122 + $method = $this->generator->generateFromArray($document); 123 + 124 + $this->assertStringContainsString('array_map(fn ($item) => Post::fromArray($item)', $method); 125 + } 126 + 127 + public function test_it_generates_empty_from_array_for_no_properties(): void 128 + { 129 + $document = $this->createDocument('app.test.empty', [ 130 + 'type' => 'record', 131 + 'properties' => [], 132 + 'required' => [], 133 + ]); 134 + 135 + $method = $this->generator->generateFromArray($document); 136 + 137 + $this->assertStringContainsString('return new static();', $method); 138 + } 139 + 140 + public function test_it_generates_all_methods(): void 141 + { 142 + $document = $this->createDocument('app.test.post', [ 143 + 'type' => 'record', 144 + 'properties' => [ 145 + 'text' => ['type' => 'string'], 146 + ], 147 + 'required' => ['text'], 148 + ]); 149 + 150 + $methods = $this->generator->generateAll($document); 151 + 152 + $this->assertCount(2, $methods); 153 + $this->assertStringContainsString('getLexicon', $methods[0]); 154 + $this->assertStringContainsString('fromArray', $methods[1]); 155 + } 156 + 157 + public function test_it_generates_generic_method(): void 158 + { 159 + $method = $this->generator->generate( 160 + name: 'customMethod', 161 + returnType: 'string', 162 + body: ' return "hello";', 163 + description: 'A custom method', 164 + params: [ 165 + ['name' => 'input', 'type' => 'string', 'description' => 'The input value'], 166 + ], 167 + isStatic: false 168 + ); 169 + 170 + $this->assertStringContainsString('public function customMethod(string $input): string', $method); 171 + $this->assertStringContainsString('* A custom method', $method); 172 + $this->assertStringContainsString('* @param string $input The input value', $method); 173 + $this->assertStringContainsString('* @return string', $method); 174 + $this->assertStringContainsString('return "hello";', $method); 175 + } 176 + 177 + public function test_it_generates_static_method(): void 178 + { 179 + $method = $this->generator->generate( 180 + name: 'create', 181 + returnType: 'static', 182 + body: ' return new static();', 183 + isStatic: true 184 + ); 185 + 186 + $this->assertStringContainsString('public static function create(): static', $method); 187 + } 188 + 189 + public function test_it_generates_method_with_multiple_parameters(): void 190 + { 191 + $method = $this->generator->generate( 192 + name: 'calculate', 193 + returnType: 'int', 194 + body: ' return $a + $b;', 195 + params: [ 196 + ['name' => 'a', 'type' => 'int'], 197 + ['name' => 'b', 'type' => 'int'], 198 + ] 199 + ); 200 + 201 + $this->assertStringContainsString('function calculate(int $a, int $b): int', $method); 202 + $this->assertStringContainsString('@param int $a', $method); 203 + $this->assertStringContainsString('@param int $b', $method); 204 + } 205 + 206 + public function test_it_generates_method_without_return_type(): void 207 + { 208 + $method = $this->generator->generate( 209 + name: 'doSomething', 210 + returnType: '', 211 + body: ' // do something', 212 + ); 213 + 214 + $this->assertStringContainsString('function doSomething()', $method); 215 + $this->assertStringNotContainsString('@return', $method); 216 + } 217 + 218 + public function test_it_generates_method_with_void_return(): void 219 + { 220 + $method = $this->generator->generate( 221 + name: 'process', 222 + returnType: 'void', 223 + body: ' // process', 224 + ); 225 + 226 + $this->assertStringContainsString('function process(): void', $method); 227 + $this->assertStringNotContainsString('@return', $method); 228 + } 229 + 230 + public function test_it_handles_datetime_format(): void 231 + { 232 + $document = $this->createDocument('app.test.event', [ 233 + 'type' => 'record', 234 + 'properties' => [ 235 + 'createdAt' => [ 236 + 'type' => 'string', 237 + 'format' => 'datetime', 238 + ], 239 + ], 240 + 'required' => ['createdAt'], 241 + ]); 242 + 243 + $method = $this->generator->generateFromArray($document); 244 + 245 + $this->assertStringContainsString('new \\DateTime($data[\'createdAt\'])', $method); 246 + } 247 + 248 + public function test_it_handles_optional_datetime(): void 249 + { 250 + $document = $this->createDocument('app.test.event', [ 251 + 'type' => 'record', 252 + 'properties' => [ 253 + 'updatedAt' => [ 254 + 'type' => 'string', 255 + 'format' => 'datetime', 256 + ], 257 + ], 258 + 'required' => [], 259 + ]); 260 + 261 + $method = $this->generator->generateFromArray($document); 262 + 263 + $this->assertStringContainsString('isset($data[\'updatedAt\']) ? new \\DateTime($data[\'updatedAt\']) : null', $method); 264 + } 265 + 266 + public function test_it_handles_array_of_objects(): void 267 + { 268 + $document = $this->createDocument('app.test.config', [ 269 + 'type' => 'record', 270 + 'properties' => [ 271 + 'settings' => [ 272 + 'type' => 'array', 273 + 'items' => [ 274 + 'type' => 'object', 275 + ], 276 + ], 277 + ], 278 + 'required' => [], 279 + ]); 280 + 281 + $method = $this->generator->generateFromArray($document); 282 + 283 + $this->assertStringContainsString('$data[\'settings\'] ?? []', $method); 284 + } 285 + 286 + public function test_it_does_not_add_trailing_comma_to_last_assignment(): void 287 + { 288 + $document = $this->createDocument('app.test.user', [ 289 + 'type' => 'record', 290 + 'properties' => [ 291 + 'first' => ['type' => 'string'], 292 + 'last' => ['type' => 'string'], 293 + ], 294 + 'required' => ['first', 'last'], 295 + ]); 296 + 297 + $method = $this->generator->generateFromArray($document); 298 + 299 + // Should have comma after first 300 + $this->assertMatchesRegularExpression('/last: \$data\[\'last\'\][^,]/', $method); 301 + } 302 + 303 + public function test_it_includes_method_docblocks(): void 304 + { 305 + $document = $this->createDocument('app.test.post', [ 306 + 'type' => 'record', 307 + 'properties' => [ 308 + 'text' => ['type' => 'string'], 309 + ], 310 + 'required' => ['text'], 311 + ]); 312 + 313 + $method = $this->generator->generateFromArray($document); 314 + 315 + $this->assertStringContainsString('/**', $method); 316 + $this->assertStringContainsString('* Create an instance from an array.', $method); 317 + $this->assertStringContainsString('* @param array $data', $method); 318 + $this->assertStringContainsString('* @return static', $method); 319 + $this->assertStringContainsString('*/', $method); 320 + } 321 + 322 + /** 323 + * Helper to create a test document. 324 + * 325 + * @param array<string, mixed> $mainDef 326 + */ 327 + protected function createDocument(string $nsid, array $mainDef): LexiconDocument 328 + { 329 + return new LexiconDocument( 330 + lexicon: 1, 331 + id: Nsid::parse($nsid), 332 + defs: ['main' => $mainDef], 333 + description: null, 334 + source: null, 335 + raw: [ 336 + 'lexicon' => 1, 337 + 'id' => $nsid, 338 + 'defs' => ['main' => $mainDef], 339 + ] 340 + ); 341 + } 342 + }