@openwebf/webf 0.22.5 → 0.22.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/analyzer.js CHANGED
@@ -37,12 +37,15 @@ exports.analyzer = analyzer;
37
37
  exports.buildClassRelationship = buildClassRelationship;
38
38
  exports.clearCaches = clearCaches;
39
39
  const typescript_1 = __importStar(require("typescript"));
40
+ const tsdoc_1 = require("@microsoft/tsdoc");
40
41
  const declaration_1 = require("./declaration");
41
42
  const utils_1 = require("./utils");
42
43
  // Cache for parsed source files to avoid re-parsing
43
44
  const sourceFileCache = new Map();
44
45
  // Cache for type conversions to avoid redundant processing
45
46
  const typeConversionCache = new Map();
47
+ // TSDoc parser instance
48
+ const tsdocParser = new tsdoc_1.TSDocParser();
46
49
  // Type mapping constants for better performance
47
50
  const BASIC_TYPE_MAP = {
48
51
  [typescript_1.default.SyntaxKind.StringKeyword]: declaration_1.FunctionArgumentType.dom_string,
@@ -72,7 +75,7 @@ function analyzer(blob, definedPropertyCollector, unionTypeCollector) {
72
75
  blob.objects = sourceFile.statements
73
76
  .map(statement => {
74
77
  try {
75
- return walkProgram(blob, statement, definedPropertyCollector, unionTypeCollector);
78
+ return walkProgram(blob, statement, sourceFile, definedPropertyCollector, unionTypeCollector);
76
79
  }
77
80
  catch (error) {
78
81
  console.error(`Error processing statement in ${blob.source}:`, error);
@@ -106,6 +109,93 @@ function getInterfaceName(statement) {
106
109
  }
107
110
  return statement.name.escapedText;
108
111
  }
112
+ function getJSDocComment(node, sourceFile) {
113
+ const sourceText = sourceFile.getFullText();
114
+ const nodeStart = node.getFullStart();
115
+ const nodePos = node.getStart(sourceFile);
116
+ // Get the text between full start and actual start (includes leading trivia)
117
+ const leadingText = sourceText.substring(nodeStart, nodePos);
118
+ // Find JSDoc comment in the leading text
119
+ const jsDocMatch = leadingText.match(/\/\*\*([\s\S]*?)\*\/\s*$/);
120
+ if (!jsDocMatch)
121
+ return undefined;
122
+ // Extract the full JSDoc comment including delimiters
123
+ const commentText = jsDocMatch[0];
124
+ const commentStartPos = nodeStart + leadingText.lastIndexOf(commentText);
125
+ // Create a TextRange for the comment
126
+ const textRange = tsdoc_1.TextRange.fromStringRange(sourceText, commentStartPos, commentStartPos + commentText.length);
127
+ // Parse the JSDoc comment using TSDoc
128
+ const parserContext = tsdocParser.parseRange(textRange);
129
+ const docComment = parserContext.docComment;
130
+ // For now, always use the raw comment to preserve all tags including @default
131
+ // TSDoc parser doesn't handle @default tags properly out of the box
132
+ // Fallback to raw comment if TSDoc parsing fails
133
+ const comment = jsDocMatch[1]
134
+ .split('\n')
135
+ .map(line => line.replace(/^\s*\*\s?/, ''))
136
+ .join('\n')
137
+ .trim();
138
+ return comment || undefined;
139
+ }
140
+ // Helper function to render TSDoc nodes to string
141
+ function renderDocNodes(nodes) {
142
+ return nodes.map(node => {
143
+ if (node.kind === 'PlainText') {
144
+ return node.text;
145
+ }
146
+ else if (node.kind === 'SoftBreak') {
147
+ return '\n';
148
+ }
149
+ else if (node.kind === 'Paragraph') {
150
+ return renderDocNodes(node.nodes);
151
+ }
152
+ return '';
153
+ }).join('').trim();
154
+ }
155
+ // Special function to get the first JSDoc comment in a file for the first interface
156
+ function getFirstInterfaceJSDoc(statement, sourceFile) {
157
+ // Find all interfaces in the file
158
+ const interfaces = [];
159
+ typescript_1.default.forEachChild(sourceFile, child => {
160
+ if (typescript_1.default.isInterfaceDeclaration(child)) {
161
+ interfaces.push(child);
162
+ }
163
+ });
164
+ // If this is the first interface, check for a file-level JSDoc
165
+ if (interfaces.length > 0 && interfaces[0] === statement) {
166
+ const sourceText = sourceFile.getFullText();
167
+ const firstInterfacePos = statement.getFullStart();
168
+ // Get all text before the first interface
169
+ const textBeforeInterface = sourceText.substring(0, firstInterfacePos);
170
+ // Find the last JSDoc comment before the interface
171
+ const jsDocMatches = textBeforeInterface.match(/\/\*\*([\s\S]*?)\*\//g);
172
+ if (jsDocMatches && jsDocMatches.length > 0) {
173
+ const lastJsDoc = jsDocMatches[jsDocMatches.length - 1];
174
+ const commentStartPos = textBeforeInterface.lastIndexOf(lastJsDoc);
175
+ // Create a TextRange for the comment
176
+ const textRange = tsdoc_1.TextRange.fromStringRange(sourceText, commentStartPos, commentStartPos + lastJsDoc.length);
177
+ // Parse the JSDoc comment using TSDoc
178
+ const parserContext = tsdocParser.parseRange(textRange);
179
+ const docComment = parserContext.docComment;
180
+ // Extract the parsed content
181
+ if (docComment.summarySection) {
182
+ const summary = renderDocNodes(docComment.summarySection.nodes);
183
+ if (summary)
184
+ return summary;
185
+ }
186
+ // Fallback to raw comment
187
+ const comment = lastJsDoc
188
+ .replace(/^\/\*\*/, '')
189
+ .replace(/\*\/$/, '')
190
+ .split('\n')
191
+ .map(line => line.replace(/^\s*\*\s?/, ''))
192
+ .join('\n')
193
+ .trim();
194
+ return comment || undefined;
195
+ }
196
+ }
197
+ return undefined;
198
+ }
109
199
  function getHeritageType(heritage) {
110
200
  if (!heritage.types.length)
111
201
  return null;
@@ -210,11 +300,15 @@ function getParameterBaseType(type, mode) {
210
300
  }
211
301
  }
212
302
  if (type.kind === typescript_1.default.SyntaxKind.LiteralType) {
213
- // Handle literal types - check if it's a null literal
303
+ // Handle literal types
214
304
  const literalType = type;
215
305
  if (literalType.literal.kind === typescript_1.default.SyntaxKind.NullKeyword) {
216
306
  return declaration_1.FunctionArgumentType.null;
217
307
  }
308
+ if (literalType.literal.kind === typescript_1.default.SyntaxKind.StringLiteral) {
309
+ // Return the string literal value itself
310
+ return literalType.literal.text;
311
+ }
218
312
  return declaration_1.FunctionArgumentType.any;
219
313
  }
220
314
  return declaration_1.FunctionArgumentType.any;
@@ -379,10 +473,10 @@ function isParamsReadOnly(m) {
379
473
  return false;
380
474
  return m.modifiers.some(k => k.kind === typescript_1.default.SyntaxKind.ReadonlyKeyword);
381
475
  }
382
- function walkProgram(blob, statement, definedPropertyCollector, unionTypeCollector) {
476
+ function walkProgram(blob, statement, sourceFile, definedPropertyCollector, unionTypeCollector) {
383
477
  switch (statement.kind) {
384
478
  case typescript_1.default.SyntaxKind.InterfaceDeclaration:
385
- return processInterfaceDeclaration(statement, blob, definedPropertyCollector, unionTypeCollector);
479
+ return processInterfaceDeclaration(statement, blob, sourceFile, definedPropertyCollector, unionTypeCollector);
386
480
  case typescript_1.default.SyntaxKind.VariableStatement:
387
481
  return processVariableStatement(statement, unionTypeCollector);
388
482
  case typescript_1.default.SyntaxKind.TypeAliasDeclaration:
@@ -399,9 +493,13 @@ function processTypeAliasDeclaration(statement, blob) {
399
493
  typeAlias.type = printer.printNode(typescript_1.default.EmitHint.Unspecified, statement.type, statement.getSourceFile());
400
494
  return typeAlias;
401
495
  }
402
- function processInterfaceDeclaration(statement, blob, definedPropertyCollector, unionTypeCollector) {
496
+ function processInterfaceDeclaration(statement, blob, sourceFile, definedPropertyCollector, unionTypeCollector) {
403
497
  const interfaceName = statement.name.escapedText.toString();
404
498
  const obj = new declaration_1.ClassObject();
499
+ // Capture JSDoc comment for the interface
500
+ const directComment = getJSDocComment(statement, sourceFile);
501
+ const fileComment = getFirstInterfaceJSDoc(statement, sourceFile);
502
+ obj.documentation = directComment || fileComment;
405
503
  // Process heritage clauses
406
504
  if (statement.heritageClauses) {
407
505
  const heritage = statement.heritageClauses[0];
@@ -420,40 +518,41 @@ function processInterfaceDeclaration(statement, blob, definedPropertyCollector,
420
518
  }
421
519
  // Process members in batches for better performance
422
520
  const members = Array.from(statement.members);
423
- processMembersBatch(members, obj, definedPropertyCollector, unionTypeCollector);
521
+ processMembersBatch(members, obj, sourceFile, definedPropertyCollector, unionTypeCollector);
424
522
  declaration_1.ClassObject.globalClassMap[interfaceName] = obj;
425
523
  return obj;
426
524
  }
427
- function processMembersBatch(members, obj, definedPropertyCollector, unionTypeCollector) {
525
+ function processMembersBatch(members, obj, sourceFile, definedPropertyCollector, unionTypeCollector) {
428
526
  for (const member of members) {
429
527
  try {
430
- processMember(member, obj, definedPropertyCollector, unionTypeCollector);
528
+ processMember(member, obj, sourceFile, definedPropertyCollector, unionTypeCollector);
431
529
  }
432
530
  catch (error) {
433
531
  console.error(`Error processing member:`, error);
434
532
  }
435
533
  }
436
534
  }
437
- function processMember(member, obj, definedPropertyCollector, unionTypeCollector) {
535
+ function processMember(member, obj, sourceFile, definedPropertyCollector, unionTypeCollector) {
438
536
  switch (member.kind) {
439
537
  case typescript_1.default.SyntaxKind.PropertySignature:
440
- processPropertySignature(member, obj, definedPropertyCollector, unionTypeCollector);
538
+ processPropertySignature(member, obj, sourceFile, definedPropertyCollector, unionTypeCollector);
441
539
  break;
442
540
  case typescript_1.default.SyntaxKind.MethodSignature:
443
- processMethodSignature(member, obj, unionTypeCollector);
541
+ processMethodSignature(member, obj, sourceFile, unionTypeCollector);
444
542
  break;
445
543
  case typescript_1.default.SyntaxKind.IndexSignature:
446
- processIndexSignature(member, obj, unionTypeCollector);
544
+ processIndexSignature(member, obj, sourceFile, unionTypeCollector);
447
545
  break;
448
546
  case typescript_1.default.SyntaxKind.ConstructSignature:
449
- processConstructSignature(member, obj, unionTypeCollector);
547
+ processConstructSignature(member, obj, sourceFile, unionTypeCollector);
450
548
  break;
451
549
  }
452
550
  }
453
- function processPropertySignature(member, obj, definedPropertyCollector, unionTypeCollector) {
551
+ function processPropertySignature(member, obj, sourceFile, definedPropertyCollector, unionTypeCollector) {
454
552
  const prop = new declaration_1.PropsDeclaration();
455
553
  prop.name = getPropName(member.name, prop);
456
554
  prop.readonly = isParamsReadOnly(member);
555
+ prop.documentation = getJSDocComment(member, sourceFile);
457
556
  definedPropertyCollector.properties.add(prop.name);
458
557
  if (!member.type) {
459
558
  console.warn(`Property ${prop.name} has no type annotation`);
@@ -491,10 +590,11 @@ function createAsyncProperty(prop, mode) {
491
590
  };
492
591
  return asyncProp;
493
592
  }
494
- function processMethodSignature(member, obj, unionTypeCollector) {
593
+ function processMethodSignature(member, obj, sourceFile, unionTypeCollector) {
495
594
  var _a;
496
595
  const f = new declaration_1.FunctionDeclaration();
497
596
  f.name = getPropName(member.name);
597
+ f.documentation = getJSDocComment(member, sourceFile);
498
598
  f.args = member.parameters.map(params => paramsNodeToArguments(params, unionTypeCollector));
499
599
  if (member.type) {
500
600
  const mode = new declaration_1.ParameterMode();
@@ -531,7 +631,7 @@ function createAsyncMethod(member, originalFunc, unionTypeCollector) {
531
631
  };
532
632
  return asyncFunc;
533
633
  }
534
- function processIndexSignature(member, obj, unionTypeCollector) {
634
+ function processIndexSignature(member, obj, sourceFile, unionTypeCollector) {
535
635
  const prop = new declaration_1.IndexedPropertyDeclaration();
536
636
  const modifier = member.modifiers;
537
637
  prop.readonly = !!(modifier && modifier[0].kind === typescript_1.default.SyntaxKind.ReadonlyKeyword);
@@ -544,7 +644,7 @@ function processIndexSignature(member, obj, unionTypeCollector) {
544
644
  prop.typeMode = mode;
545
645
  obj.indexedProp = prop;
546
646
  }
547
- function processConstructSignature(member, obj, unionTypeCollector) {
647
+ function processConstructSignature(member, obj, sourceFile, unionTypeCollector) {
548
648
  const c = new declaration_1.FunctionDeclaration();
549
649
  c.name = 'constructor';
550
650
  c.args = member.parameters.map(params => paramsNodeToArguments(params, unionTypeCollector));
package/dist/commands.js CHANGED
@@ -317,6 +317,8 @@ function generateCommand(distPath, options) {
317
317
  const hasTsConfig = fs_1.default.existsSync(tsConfigPath);
318
318
  // Determine if we need to create a new project
319
319
  const needsProjectCreation = !hasPackageJson || !hasGlobalDts || !hasTsConfig;
320
+ // Track if this is an existing project (has all required files)
321
+ const isExistingProject = hasPackageJson && hasGlobalDts && hasTsConfig;
320
322
  let framework = options.framework;
321
323
  let packageName = options.packageName;
322
324
  // Validate and sanitize package name if provided
@@ -526,7 +528,7 @@ function generateCommand(distPath, options) {
526
528
  // Handle npm publishing if requested via command line option
527
529
  if (options.publishToNpm && framework) {
528
530
  try {
529
- yield buildAndPublishPackage(resolvedDistPath, options.npmRegistry);
531
+ yield buildAndPublishPackage(resolvedDistPath, options.npmRegistry, isExistingProject);
530
532
  }
531
533
  catch (error) {
532
534
  console.error('\nError during npm publish:', error);
@@ -561,7 +563,7 @@ function generateCommand(distPath, options) {
561
563
  }
562
564
  }]);
563
565
  try {
564
- yield buildAndPublishPackage(resolvedDistPath, registryAnswer.registry || undefined);
566
+ yield buildAndPublishPackage(resolvedDistPath, registryAnswer.registry || undefined, isExistingProject);
565
567
  }
566
568
  catch (error) {
567
569
  console.error('\nError during npm publish:', error);
@@ -652,17 +654,34 @@ function buildPackage(packagePath) {
652
654
  }
653
655
  });
654
656
  }
655
- function buildAndPublishPackage(packagePath, registry) {
656
- return __awaiter(this, void 0, void 0, function* () {
657
+ function buildAndPublishPackage(packagePath_1, registry_1) {
658
+ return __awaiter(this, arguments, void 0, function* (packagePath, registry, isExistingProject = false) {
657
659
  const packageJsonPath = path_1.default.join(packagePath, 'package.json');
658
660
  if (!fs_1.default.existsSync(packageJsonPath)) {
659
661
  throw new Error(`No package.json found in ${packagePath}`);
660
662
  }
661
- const packageJson = JSON.parse(fs_1.default.readFileSync(packageJsonPath, 'utf-8'));
663
+ let packageJson = JSON.parse(fs_1.default.readFileSync(packageJsonPath, 'utf-8'));
662
664
  const packageName = packageJson.name;
663
- const packageVersion = packageJson.version;
665
+ let packageVersion = packageJson.version;
664
666
  // First, ensure dependencies are installed and build the package
665
667
  yield buildPackage(packagePath);
668
+ // If this is an existing project, increment the patch version before publishing
669
+ if (isExistingProject) {
670
+ console.log(`\nIncrementing version for existing project...`);
671
+ const versionResult = (0, child_process_1.spawnSync)(NPM, ['version', 'patch', '--no-git-tag-version'], {
672
+ cwd: packagePath,
673
+ encoding: 'utf-8',
674
+ stdio: 'pipe'
675
+ });
676
+ if (versionResult.status !== 0) {
677
+ console.error('Failed to increment version:', versionResult.stderr);
678
+ throw new Error('Failed to increment version');
679
+ }
680
+ // Re-read package.json to get the new version
681
+ packageJson = JSON.parse(fs_1.default.readFileSync(packageJsonPath, 'utf-8'));
682
+ packageVersion = packageJson.version;
683
+ console.log(`Version updated to ${packageVersion}`);
684
+ }
666
685
  // Set registry if provided
667
686
  if (registry) {
668
687
  console.log(`\nUsing npm registry: ${registry}`);
package/dist/react.js CHANGED
@@ -15,6 +15,9 @@ function readTemplate(name) {
15
15
  return fs_1.default.readFileSync(path_1.default.join(__dirname, '../templates/' + name + '.tpl'), { encoding: 'utf-8' });
16
16
  }
17
17
  function generateReturnType(type) {
18
+ if ((0, utils_1.isUnionType)(type)) {
19
+ return type.value.map(v => `'${v.value}'`).join(' | ');
20
+ }
18
21
  if ((0, utils_1.isPointerType)(type)) {
19
22
  const pointerType = (0, utils_1.getPointerType)(type);
20
23
  return pointerType;
@@ -79,6 +82,19 @@ function generateMethodDeclaration(method) {
79
82
  var returnType = generateReturnType(method.returnType);
80
83
  return `${methodName}(${args}): ${returnType};`;
81
84
  }
85
+ function generateMethodDeclarationWithDocs(method, indent = '') {
86
+ let result = '';
87
+ if (method.documentation) {
88
+ result += `${indent}/**\n`;
89
+ const docLines = method.documentation.split('\n');
90
+ docLines.forEach(line => {
91
+ result += `${indent} * ${line}\n`;
92
+ });
93
+ result += `${indent} */\n`;
94
+ }
95
+ result += `${indent}${generateMethodDeclaration(method)}`;
96
+ return result;
97
+ }
82
98
  function toReactEventName(name) {
83
99
  const eventName = 'on-' + name;
84
100
  return lodash_1.default.camelCase(eventName);
@@ -110,9 +126,13 @@ function generateReactComponent(blob, packageName, relativeDir) {
110
126
  const events = classObjects.filter(object => {
111
127
  return object.name.endsWith('Events');
112
128
  });
129
+ const methods = classObjects.filter(object => {
130
+ return object.name.endsWith('Methods');
131
+ });
113
132
  const others = classObjects.filter(object => {
114
133
  return !object.name.endsWith('Properties')
115
- && !object.name.endsWith('Events');
134
+ && !object.name.endsWith('Events')
135
+ && !object.name.endsWith('Methods');
116
136
  });
117
137
  // Include type aliases
118
138
  const typeAliasDeclarations = typeAliases.map(typeAlias => {
@@ -120,6 +140,19 @@ function generateReactComponent(blob, packageName, relativeDir) {
120
140
  }).join('\n');
121
141
  const dependencies = [
122
142
  typeAliasDeclarations,
143
+ // Include Methods interfaces as dependencies
144
+ methods.map(object => {
145
+ const methodDeclarations = object.methods.map(method => {
146
+ return generateMethodDeclarationWithDocs(method, ' ');
147
+ }).join('\n');
148
+ let interfaceDoc = '';
149
+ if (object.documentation) {
150
+ interfaceDoc = `/**\n${object.documentation.split('\n').map(line => ` * ${line}`).join('\n')}\n */\n`;
151
+ }
152
+ return `${interfaceDoc}interface ${object.name} {
153
+ ${methodDeclarations}
154
+ }`;
155
+ }).join('\n\n'),
123
156
  others.map(object => {
124
157
  const props = object.props.map(prop => {
125
158
  if (prop.optional) {
@@ -135,7 +168,7 @@ interface ${object.name} {
135
168
  ].filter(Boolean).join('\n\n');
136
169
  // Generate all components from this file
137
170
  const components = [];
138
- // Create a map of component names to their properties and events
171
+ // Create a map of component names to their properties, events, and methods
139
172
  const componentMap = new Map();
140
173
  // Process all Properties interfaces
141
174
  properties.forEach(prop => {
@@ -153,6 +186,14 @@ interface ${object.name} {
153
186
  }
154
187
  componentMap.get(componentName).events = event;
155
188
  });
189
+ // Process all Methods interfaces
190
+ methods.forEach(method => {
191
+ const componentName = method.name.replace(/Methods$/, '');
192
+ if (!componentMap.has(componentName)) {
193
+ componentMap.set(componentName, {});
194
+ }
195
+ componentMap.get(componentName).methods = method;
196
+ });
156
197
  // If we have multiple components, we need to generate a combined file
157
198
  const componentEntries = Array.from(componentMap.entries());
158
199
  if (componentEntries.length === 0) {
@@ -180,6 +221,7 @@ interface ${object.name} {
180
221
  className: className,
181
222
  properties: component.properties,
182
223
  events: component.events,
224
+ methods: component.methods,
183
225
  classObjectDictionary,
184
226
  dependencies,
185
227
  blob,
@@ -187,6 +229,7 @@ interface ${object.name} {
187
229
  toWebFTagName,
188
230
  generateReturnType,
189
231
  generateMethodDeclaration,
232
+ generateMethodDeclarationWithDocs,
190
233
  generateEventHandlerType,
191
234
  getEventType,
192
235
  });
@@ -214,6 +257,7 @@ interface ${object.name} {
214
257
  className: className,
215
258
  properties: component.properties,
216
259
  events: component.events,
260
+ methods: component.methods,
217
261
  classObjectDictionary,
218
262
  dependencies: '', // Dependencies will be at the top
219
263
  blob,
@@ -221,6 +265,7 @@ interface ${object.name} {
221
265
  toWebFTagName,
222
266
  generateReturnType,
223
267
  generateMethodDeclaration,
268
+ generateMethodDeclarationWithDocs,
224
269
  generateEventHandlerType,
225
270
  getEventType,
226
271
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openwebf/webf",
3
- "version": "0.22.5",
3
+ "version": "0.22.8",
4
4
  "description": "Command line tools for WebF",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -36,6 +36,8 @@
36
36
  "ts-jest": "^29.1.2"
37
37
  },
38
38
  "dependencies": {
39
+ "@microsoft/tsdoc": "^0.15.1",
40
+ "@microsoft/tsdoc-config": "^0.17.1",
39
41
  "commander": "^14.0.0",
40
42
  "glob": "^10.3.10",
41
43
  "inquirer": "^8.2.6",