rclnodejs 1.8.3 → 1.9.0-alpha.0

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.
Files changed (85) hide show
  1. package/README.md +46 -37
  2. package/index.js +29 -0
  3. package/lib/action/client.js +61 -3
  4. package/lib/message_info.js +94 -0
  5. package/lib/node.js +53 -3
  6. package/lib/parameter_event_handler.js +468 -0
  7. package/lib/parameter_watcher.js +12 -12
  8. package/lib/subscription.js +38 -5
  9. package/lib/timer.js +2 -1
  10. package/lib/wait_for_message.js +111 -0
  11. package/package.json +6 -3
  12. package/prebuilds/linux-arm64/humble-jammy-arm64-rclnodejs.node +0 -0
  13. package/prebuilds/linux-arm64/jazzy-noble-arm64-rclnodejs.node +0 -0
  14. package/prebuilds/linux-arm64/kilted-noble-arm64-rclnodejs.node +0 -0
  15. package/prebuilds/linux-x64/humble-jammy-x64-rclnodejs.node +0 -0
  16. package/prebuilds/linux-x64/jazzy-noble-x64-rclnodejs.node +0 -0
  17. package/prebuilds/linux-x64/kilted-noble-x64-rclnodejs.node +0 -0
  18. package/scripts/run_asan_test.sh +118 -0
  19. package/src/executor.cpp +36 -2
  20. package/src/executor.h +11 -0
  21. package/src/rcl_action_client_bindings.cpp +70 -1
  22. package/src/rcl_context_bindings.cpp +3 -3
  23. package/src/rcl_graph_bindings.cpp +2 -2
  24. package/src/rcl_subscription_bindings.cpp +70 -2
  25. package/src/rcl_utilities.cpp +2 -2
  26. package/tools/jsdoc/Makefile +5 -0
  27. package/tools/jsdoc/README.md +96 -0
  28. package/tools/jsdoc/build-index.js +610 -0
  29. package/tools/jsdoc/publish.js +854 -0
  30. package/tools/jsdoc/regenerate-published-docs.js +605 -0
  31. package/tools/jsdoc/static/fonts/OpenSans-Bold-webfont.eot +0 -0
  32. package/tools/jsdoc/static/fonts/OpenSans-Bold-webfont.svg +1830 -0
  33. package/tools/jsdoc/static/fonts/OpenSans-Bold-webfont.woff +0 -0
  34. package/tools/jsdoc/static/fonts/OpenSans-BoldItalic-webfont.eot +0 -0
  35. package/tools/jsdoc/static/fonts/OpenSans-BoldItalic-webfont.svg +1830 -0
  36. package/tools/jsdoc/static/fonts/OpenSans-BoldItalic-webfont.woff +0 -0
  37. package/tools/jsdoc/static/fonts/OpenSans-Italic-webfont.eot +0 -0
  38. package/tools/jsdoc/static/fonts/OpenSans-Italic-webfont.svg +1830 -0
  39. package/tools/jsdoc/static/fonts/OpenSans-Italic-webfont.woff +0 -0
  40. package/tools/jsdoc/static/fonts/OpenSans-Light-webfont.eot +0 -0
  41. package/tools/jsdoc/static/fonts/OpenSans-Light-webfont.svg +1831 -0
  42. package/tools/jsdoc/static/fonts/OpenSans-Light-webfont.woff +0 -0
  43. package/tools/jsdoc/static/fonts/OpenSans-LightItalic-webfont.eot +0 -0
  44. package/tools/jsdoc/static/fonts/OpenSans-LightItalic-webfont.svg +1835 -0
  45. package/tools/jsdoc/static/fonts/OpenSans-LightItalic-webfont.woff +0 -0
  46. package/tools/jsdoc/static/fonts/OpenSans-Regular-webfont.eot +0 -0
  47. package/tools/jsdoc/static/fonts/OpenSans-Regular-webfont.svg +1831 -0
  48. package/tools/jsdoc/static/fonts/OpenSans-Regular-webfont.woff +0 -0
  49. package/tools/jsdoc/static/scripts/linenumber.js +25 -0
  50. package/tools/jsdoc/static/scripts/prettify/Apache-License-2.0.txt +202 -0
  51. package/tools/jsdoc/static/scripts/prettify/lang-css.js +36 -0
  52. package/tools/jsdoc/static/scripts/prettify/prettify.js +738 -0
  53. package/tools/jsdoc/static/styles/jsdoc-default.css +1012 -0
  54. package/tools/jsdoc/static/styles/prettify-jsdoc.css +111 -0
  55. package/tools/jsdoc/static/styles/prettify-tomorrow.css +132 -0
  56. package/tools/jsdoc/tmpl/augments.tmpl +10 -0
  57. package/tools/jsdoc/tmpl/container.tmpl +193 -0
  58. package/tools/jsdoc/tmpl/details.tmpl +143 -0
  59. package/tools/jsdoc/tmpl/example.tmpl +2 -0
  60. package/tools/jsdoc/tmpl/examples.tmpl +13 -0
  61. package/tools/jsdoc/tmpl/exceptions.tmpl +17 -0
  62. package/tools/jsdoc/tmpl/layout.tmpl +83 -0
  63. package/tools/jsdoc/tmpl/mainpage.tmpl +163 -0
  64. package/tools/jsdoc/tmpl/members.tmpl +43 -0
  65. package/tools/jsdoc/tmpl/method.tmpl +124 -0
  66. package/tools/jsdoc/tmpl/params.tmpl +133 -0
  67. package/tools/jsdoc/tmpl/properties.tmpl +110 -0
  68. package/tools/jsdoc/tmpl/returns.tmpl +12 -0
  69. package/tools/jsdoc/tmpl/source.tmpl +8 -0
  70. package/tools/jsdoc/tmpl/tutorial.tmpl +19 -0
  71. package/tools/jsdoc/tmpl/type.tmpl +7 -0
  72. package/types/action_client.d.ts +8 -0
  73. package/types/index.d.ts +34 -0
  74. package/types/message_info.d.ts +72 -0
  75. package/types/node.d.ts +21 -0
  76. package/types/parameter_event_handler.d.ts +139 -0
  77. package/types/subscription.d.ts +14 -2
  78. package/test_data_integrity.js +0 -108
  79. package/test_repro_exact.js +0 -57
  80. package/test_repro_hz.js +0 -86
  81. package/test_repro_pub.js +0 -36
  82. package/test_repro_stress.js +0 -83
  83. package/test_repro_sub.js +0 -64
  84. package/test_xproc_data.js +0 -64
  85. package/types/interfaces.d.ts +0 -8895
@@ -0,0 +1,854 @@
1
+ // Copyright (c) 2026 The Robot Web Tools Contributors. All rights reserved.
2
+ //
3
+ // Licensed under the Apache License, Version 2.0 (the "License");
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+
15
+ 'use strict';
16
+
17
+ var doop = require('jsdoc/util/doop');
18
+ var env = require('jsdoc/env');
19
+ var fs = require('jsdoc/fs');
20
+ var helper = require('jsdoc/util/templateHelper');
21
+ var logger = require('jsdoc/util/logger');
22
+ var path = require('jsdoc/path');
23
+ var taffy = require('@jsdoc/salty').taffy;
24
+ var template = require('jsdoc/template');
25
+ var util = require('util');
26
+
27
+ var htmlsafe = helper.htmlsafe;
28
+ var linkto = helper.linkto;
29
+ var resolveAuthorLinks = helper.resolveAuthorLinks;
30
+ var hasOwnProp = Object.prototype.hasOwnProperty;
31
+
32
+ var data;
33
+ var view;
34
+
35
+ var outdir = path.normalize(env.opts.destination);
36
+
37
+ function find(spec) {
38
+ return helper.find(data, spec);
39
+ }
40
+
41
+ function tutoriallink(tutorial) {
42
+ return helper.toTutorial(tutorial, null, {
43
+ tag: 'em',
44
+ classname: 'disabled',
45
+ prefix: 'Tutorial: ',
46
+ });
47
+ }
48
+
49
+ function getAncestorLinks(doclet) {
50
+ return helper.getAncestorLinks(data, doclet);
51
+ }
52
+
53
+ function hashToLink(doclet, hash) {
54
+ var url;
55
+
56
+ if (!/^(#.+)/.test(hash)) {
57
+ return hash;
58
+ }
59
+
60
+ url = helper.createLink(doclet);
61
+ url = url.replace(/(#.+|$)/, hash);
62
+
63
+ return '<a href="' + url + '">' + hash + '</a>';
64
+ }
65
+
66
+ function needsSignature(doclet) {
67
+ var needsSig = false;
68
+
69
+ // function and class definitions always get a signature
70
+ if (doclet.kind === 'function' || doclet.kind === 'class') {
71
+ needsSig = true;
72
+ }
73
+ // typedefs that contain functions get a signature, too
74
+ else if (
75
+ doclet.kind === 'typedef' &&
76
+ doclet.type &&
77
+ doclet.type.names &&
78
+ doclet.type.names.length
79
+ ) {
80
+ for (var i = 0, l = doclet.type.names.length; i < l; i++) {
81
+ if (doclet.type.names[i].toLowerCase() === 'function') {
82
+ needsSig = true;
83
+ break;
84
+ }
85
+ }
86
+ }
87
+ // and namespaces that are functions get a signature (but finding them is a
88
+ // bit messy)
89
+ else if (
90
+ doclet.kind === 'namespace' &&
91
+ doclet.meta &&
92
+ doclet.meta.code &&
93
+ doclet.meta.code.type &&
94
+ doclet.meta.code.type.match(/[Ff]unction/)
95
+ ) {
96
+ needsSig = true;
97
+ }
98
+
99
+ return needsSig;
100
+ }
101
+
102
+ function getSignatureAttributes(item) {
103
+ var attributes = [];
104
+
105
+ if (item.optional) {
106
+ attributes.push('opt');
107
+ }
108
+
109
+ if (item.nullable === true) {
110
+ attributes.push('nullable');
111
+ } else if (item.nullable === false) {
112
+ attributes.push('non-null');
113
+ }
114
+
115
+ return attributes;
116
+ }
117
+
118
+ function updateItemName(item) {
119
+ var attributes = getSignatureAttributes(item);
120
+ var itemName = item.name || '';
121
+
122
+ if (item.variable) {
123
+ itemName = '&hellip;' + itemName;
124
+ }
125
+
126
+ if (attributes && attributes.length) {
127
+ itemName = util.format(
128
+ '%s<span class="signature-attributes">%s</span>',
129
+ itemName,
130
+ attributes.join(', ')
131
+ );
132
+ }
133
+
134
+ return itemName;
135
+ }
136
+
137
+ function addParamAttributes(params) {
138
+ return params
139
+ .filter(function (param) {
140
+ return param.name && param.name.indexOf('.') === -1;
141
+ })
142
+ .map(updateItemName);
143
+ }
144
+
145
+ function buildItemTypeStrings(item) {
146
+ var types = [];
147
+
148
+ if (item && item.type && item.type.names) {
149
+ item.type.names.forEach(function (name) {
150
+ types.push(linkto(name, htmlsafe(name)));
151
+ });
152
+ }
153
+
154
+ return types;
155
+ }
156
+
157
+ function buildAttribsString(attribs) {
158
+ var attribsString = '';
159
+
160
+ if (attribs && attribs.length) {
161
+ attribsString = htmlsafe(util.format('(%s) ', attribs.join(', ')));
162
+ }
163
+
164
+ return attribsString;
165
+ }
166
+
167
+ function addNonParamAttributes(items) {
168
+ var types = [];
169
+
170
+ items.forEach(function (item) {
171
+ types = types.concat(buildItemTypeStrings(item));
172
+ });
173
+
174
+ return types;
175
+ }
176
+
177
+ function addSignatureParams(f) {
178
+ var params = f.params ? addParamAttributes(f.params) : [];
179
+
180
+ f.signature = util.format('%s(%s)', f.signature || '', params.join(', '));
181
+ }
182
+
183
+ function addSignatureReturns(f) {
184
+ var attribs = [];
185
+ var attribsString = '';
186
+ var returnTypes = [];
187
+ var returnTypesString = '';
188
+ var source = f.yields || f.returns;
189
+
190
+ // jam all the return-type attributes into an array. this could create odd results (for example,
191
+ // if there are both nullable and non-nullable return types), but let's assume that most people
192
+ // who use multiple @return tags aren't using Closure Compiler type annotations, and vice-versa.
193
+ if (source) {
194
+ source.forEach(function (item) {
195
+ helper.getAttribs(item).forEach(function (attrib) {
196
+ if (attribs.indexOf(attrib) === -1) {
197
+ attribs.push(attrib);
198
+ }
199
+ });
200
+ });
201
+
202
+ attribsString = buildAttribsString(attribs);
203
+ }
204
+
205
+ if (source) {
206
+ returnTypes = addNonParamAttributes(source);
207
+ }
208
+ if (returnTypes.length) {
209
+ returnTypesString = util.format(
210
+ ' &rarr; %s{%s}',
211
+ attribsString,
212
+ returnTypes.join('|')
213
+ );
214
+ }
215
+
216
+ f.signature =
217
+ '<span class="signature">' +
218
+ (f.signature || '') +
219
+ '</span>' +
220
+ '<span class="type-signature">' +
221
+ returnTypesString +
222
+ '</span>';
223
+ }
224
+
225
+ function addSignatureTypes(f) {
226
+ var types = f.type ? buildItemTypeStrings(f) : [];
227
+
228
+ f.signature =
229
+ (f.signature || '') +
230
+ '<span class="type-signature">' +
231
+ (types.length ? ' :' + types.join('|') : '') +
232
+ '</span>';
233
+ }
234
+
235
+ function addAttribs(f) {
236
+ var attribs = helper.getAttribs(f);
237
+ var attribsString = buildAttribsString(attribs);
238
+
239
+ f.attribs = util.format(
240
+ '<span class="type-signature">%s</span>',
241
+ attribsString
242
+ );
243
+ }
244
+
245
+ function shortenPaths(files, commonPrefix) {
246
+ Object.keys(files).forEach(function (file) {
247
+ files[file].shortened = files[file].resolved
248
+ .replace(commonPrefix, '')
249
+ // always use forward slashes
250
+ .replace(/\\/g, '/');
251
+ });
252
+
253
+ return files;
254
+ }
255
+
256
+ function getPathFromDoclet(doclet) {
257
+ if (!doclet.meta) {
258
+ return null;
259
+ }
260
+
261
+ return doclet.meta.path && doclet.meta.path !== 'null'
262
+ ? path.join(doclet.meta.path, doclet.meta.filename)
263
+ : doclet.meta.filename;
264
+ }
265
+
266
+ function generate(title, docs, filename, resolveLinks) {
267
+ var docData;
268
+ var html;
269
+ var outpath;
270
+
271
+ resolveLinks = resolveLinks !== false;
272
+
273
+ docData = {
274
+ env: env,
275
+ title: title,
276
+ docs: docs,
277
+ };
278
+
279
+ outpath = path.join(outdir, filename);
280
+ html = view.render('container.tmpl', docData);
281
+
282
+ if (resolveLinks) {
283
+ html = helper.resolveLinks(html); // turn {@link foo} into <a href="foodoc.html">foo</a>
284
+ }
285
+
286
+ fs.writeFileSync(outpath, html, 'utf8');
287
+ }
288
+
289
+ function generateSourceFiles(sourceFiles, encoding) {
290
+ encoding = encoding || 'utf8';
291
+ Object.keys(sourceFiles).forEach(function (file) {
292
+ var source;
293
+ // links are keyed to the shortened path in each doclet's `meta.shortpath` property
294
+ var sourceOutfile = helper.getUniqueFilename(sourceFiles[file].shortened);
295
+
296
+ helper.registerLink(sourceFiles[file].shortened, sourceOutfile);
297
+
298
+ try {
299
+ source = {
300
+ kind: 'source',
301
+ code: helper.htmlsafe(
302
+ fs.readFileSync(sourceFiles[file].resolved, encoding)
303
+ ),
304
+ };
305
+ } catch (e) {
306
+ logger.error(
307
+ 'Error while generating source file %s: %s',
308
+ file,
309
+ e.message
310
+ );
311
+ }
312
+
313
+ generate(
314
+ 'Source: ' + sourceFiles[file].shortened,
315
+ [source],
316
+ sourceOutfile,
317
+ false
318
+ );
319
+ });
320
+ }
321
+
322
+ function getHomepageData(members, globalUrl) {
323
+ return {
324
+ coreModuleUrl:
325
+ helper.longnameToUrl['module:rclnodejs'] || 'module-rclnodejs.html',
326
+ validatorModuleUrl:
327
+ helper.longnameToUrl['module:validator'] || 'module-validator.html',
328
+ globalUrl: globalUrl,
329
+ installUrl: '#guide-installation',
330
+ typescriptUrl: '#guide-typescript',
331
+ generationUrl: '#guide-message-generation',
332
+ docsUrl:
333
+ helper.longnameToUrl['module:rclnodejs'] || 'module-rclnodejs.html',
334
+ stats: [
335
+ {
336
+ value: members.modules.length,
337
+ label: 'Modules',
338
+ detail: 'Primary entry points and supporting package surfaces',
339
+ },
340
+ {
341
+ value: members.classes.length,
342
+ label: 'Classes',
343
+ detail:
344
+ 'Runtime building blocks for nodes, services, actions, and time',
345
+ },
346
+ {
347
+ value: members.globals.length,
348
+ label: 'Globals',
349
+ detail: 'Enums, constants, policies, and top-level helpers',
350
+ },
351
+ ],
352
+ };
353
+ }
354
+
355
+ /**
356
+ * Look for classes or functions with the same name as modules (which indicates that the module
357
+ * exports only that class or function), then attach the classes or functions to the `module`
358
+ * property of the appropriate module doclets. The name of each class or function is also updated
359
+ * for display purposes. This function mutates the original arrays.
360
+ *
361
+ * @private
362
+ * @param {Array.<module:jsdoc/doclet.Doclet>} doclets - The array of classes and functions to
363
+ * check.
364
+ * @param {Array.<module:jsdoc/doclet.Doclet>} modules - The array of module doclets to search.
365
+ */
366
+ function attachModuleSymbols(doclets, modules) {
367
+ var symbols = {};
368
+
369
+ // build a lookup table
370
+ doclets.forEach(function (symbol) {
371
+ symbols[symbol.longname] = symbols[symbol.longname] || [];
372
+ symbols[symbol.longname].push(symbol);
373
+ });
374
+
375
+ modules.forEach(function (module) {
376
+ if (symbols[module.longname]) {
377
+ module.modules = symbols[module.longname]
378
+ // Only show symbols that have a description. Make an exception for classes, because
379
+ // we want to show the constructor-signature heading no matter what.
380
+ .filter(function (symbol) {
381
+ return symbol.description || symbol.kind === 'class';
382
+ })
383
+ .map(function (symbol) {
384
+ symbol = doop(symbol);
385
+
386
+ if (symbol.kind === 'class' || symbol.kind === 'function') {
387
+ symbol.name = symbol.name.replace('module:', '(require("') + '"))';
388
+ }
389
+
390
+ return symbol;
391
+ });
392
+ }
393
+ });
394
+ }
395
+
396
+ function buildMemberNav(items, itemHeading, itemsSeen, linktoFn) {
397
+ var nav = '';
398
+
399
+ if (items.length) {
400
+ var itemsNav = '';
401
+
402
+ items.forEach(function (item) {
403
+ var displayName;
404
+
405
+ if (!hasOwnProp.call(item, 'longname')) {
406
+ itemsNav += '<li>' + linktoFn('', item.name) + '</li>';
407
+ } else if (!hasOwnProp.call(itemsSeen, item.longname)) {
408
+ if (env.conf.templates.default.useLongnameInNav) {
409
+ displayName = item.longname;
410
+ } else {
411
+ displayName = item.name;
412
+ }
413
+ itemsNav +=
414
+ '<li>' +
415
+ linktoFn(
416
+ item.longname,
417
+ displayName.replace(/\b(module|event):/g, '')
418
+ ) +
419
+ '</li>';
420
+
421
+ itemsSeen[item.longname] = true;
422
+ }
423
+ });
424
+
425
+ if (itemsNav !== '') {
426
+ nav +=
427
+ '<section class="nav-section"><h3>' +
428
+ itemHeading +
429
+ '</h3><ul class="nav-list">' +
430
+ itemsNav +
431
+ '</ul></section>';
432
+ }
433
+ }
434
+
435
+ return nav;
436
+ }
437
+
438
+ function linktoTutorial(longName, name) {
439
+ return tutoriallink(name);
440
+ }
441
+
442
+ function linktoExternal(longName, name) {
443
+ return linkto(longName, name.replace(/(^"|"$)/g, ''));
444
+ }
445
+
446
+ /**
447
+ * Create the navigation sidebar.
448
+ * @param {object} members The members that will be used to create the sidebar.
449
+ * @param {array<object>} members.classes
450
+ * @param {array<object>} members.externals
451
+ * @param {array<object>} members.globals
452
+ * @param {array<object>} members.mixins
453
+ * @param {array<object>} members.modules
454
+ * @param {array<object>} members.namespaces
455
+ * @param {array<object>} members.tutorials
456
+ * @param {array<object>} members.events
457
+ * @param {array<object>} members.interfaces
458
+ * @return {string} The HTML for the navigation sidebar.
459
+ */
460
+ function buildNav(members) {
461
+ var globalNav;
462
+ var nav = '<div class="nav-home"><a href="index.html">Overview</a></div>';
463
+ var seen = {};
464
+ var seenTutorials = {};
465
+
466
+ nav += buildMemberNav(members.modules, 'Modules', {}, linkto);
467
+ nav += buildMemberNav(members.externals, 'Externals', seen, linktoExternal);
468
+ nav += buildMemberNav(members.classes, 'Classes', seen, linkto);
469
+ nav += buildMemberNav(members.events, 'Events', seen, linkto);
470
+ nav += buildMemberNav(members.namespaces, 'Namespaces', seen, linkto);
471
+ nav += buildMemberNav(members.mixins, 'Mixins', seen, linkto);
472
+ nav += buildMemberNav(
473
+ members.tutorials,
474
+ 'Tutorials',
475
+ seenTutorials,
476
+ linktoTutorial
477
+ );
478
+ nav += buildMemberNav(members.interfaces, 'Interfaces', seen, linkto);
479
+
480
+ if (members.globals.length) {
481
+ globalNav = '';
482
+
483
+ members.globals.forEach(function (g) {
484
+ if (g.kind !== 'typedef' && !hasOwnProp.call(seen, g.longname)) {
485
+ globalNav += '<li>' + linkto(g.longname, g.name) + '</li>';
486
+ }
487
+ seen[g.longname] = true;
488
+ });
489
+
490
+ if (!globalNav) {
491
+ // turn the heading into a link so you can actually get to the global page
492
+ nav +=
493
+ '<section class="nav-section"><h3>' +
494
+ linkto('global', 'Global') +
495
+ '</h3></section>';
496
+ } else {
497
+ nav +=
498
+ '<section class="nav-section"><h3>Global</h3><ul class="nav-list">' +
499
+ globalNav +
500
+ '</ul></section>';
501
+ }
502
+ }
503
+
504
+ return nav;
505
+ }
506
+
507
+ /**
508
+ @param {TAFFY} taffyData See <http://taffydb.com/>.
509
+ @param {object} opts
510
+ @param {Tutorial} tutorials
511
+ */
512
+ exports.publish = function (taffyData, opts, tutorials) {
513
+ var classes;
514
+ var conf;
515
+ var externals;
516
+ var files;
517
+ var fromDir;
518
+ var globalUrl;
519
+ var indexUrl;
520
+ var interfaces;
521
+ var members;
522
+ var mixins;
523
+ var modules;
524
+ var namespaces;
525
+ var outputSourceFiles;
526
+ var packageInfo;
527
+ var packages;
528
+ var sourceFilePaths = [];
529
+ var sourceFiles = {};
530
+ var staticFileFilter;
531
+ var staticFilePaths;
532
+ var staticFiles;
533
+ var staticFileScanner;
534
+ var templatePath;
535
+
536
+ data = taffyData;
537
+
538
+ conf = env.conf.templates || {};
539
+ conf.default = conf.default || {};
540
+
541
+ templatePath = path.normalize(opts.template);
542
+ view = new template.Template(path.join(templatePath, 'tmpl'));
543
+
544
+ // claim some special filenames in advance, so the All-Powerful Overseer of Filename Uniqueness
545
+ // doesn't try to hand them out later
546
+ indexUrl = helper.getUniqueFilename('index');
547
+ // don't call registerLink() on this one! 'index' is also a valid longname
548
+
549
+ globalUrl = helper.getUniqueFilename('global');
550
+ helper.registerLink('global', globalUrl);
551
+
552
+ // set up templating
553
+ view.layout = conf.default.layoutFile
554
+ ? path.getResourcePath(
555
+ path.dirname(conf.default.layoutFile),
556
+ path.basename(conf.default.layoutFile)
557
+ )
558
+ : 'layout.tmpl';
559
+
560
+ // set up tutorials for helper
561
+ helper.setTutorials(tutorials);
562
+
563
+ data = helper.prune(data);
564
+ data.sort('longname, version, since');
565
+ helper.addEventListeners(data);
566
+
567
+ data().each(function (doclet) {
568
+ var sourcePath;
569
+
570
+ doclet.attribs = '';
571
+
572
+ if (doclet.examples) {
573
+ doclet.examples = doclet.examples.map(function (example) {
574
+ var caption;
575
+ var code;
576
+
577
+ if (
578
+ example.match(
579
+ /^\s*<caption>([\s\S]+?)<\/caption>(\s*[\n\r])([\s\S]+)$/i
580
+ )
581
+ ) {
582
+ caption = RegExp.$1;
583
+ code = RegExp.$3;
584
+ }
585
+
586
+ return {
587
+ caption: caption || '',
588
+ code: code || example,
589
+ };
590
+ });
591
+ }
592
+ if (doclet.see) {
593
+ doclet.see.forEach(function (seeItem, i) {
594
+ doclet.see[i] = hashToLink(doclet, seeItem);
595
+ });
596
+ }
597
+
598
+ // build a list of source files
599
+ if (doclet.meta) {
600
+ sourcePath = getPathFromDoclet(doclet);
601
+
602
+ sourceFiles[sourcePath] = {
603
+ resolved: sourcePath,
604
+ shortened: null,
605
+ };
606
+ if (sourceFilePaths.indexOf(sourcePath) === -1) {
607
+ sourceFilePaths.push(sourcePath);
608
+ }
609
+ }
610
+ });
611
+
612
+ // update outdir if necessary, then create outdir
613
+ packageInfo = (find({ kind: 'package' }) || [])[0];
614
+ if (packageInfo && packageInfo.version) {
615
+ outdir = path.join(outdir, packageInfo.version);
616
+ }
617
+ fs.mkPath(outdir);
618
+
619
+ // copy the template's static files to outdir
620
+ fromDir = path.join(templatePath, 'static');
621
+ staticFiles = fs.ls(fromDir, 3);
622
+
623
+ staticFiles.forEach(function (fileName) {
624
+ var toDir = fs.toDir(fileName.replace(fromDir, outdir));
625
+
626
+ fs.mkPath(toDir);
627
+ fs.copyFileSync(fileName, toDir);
628
+ });
629
+
630
+ // copy user-specified static files to outdir
631
+ if (conf.default.staticFiles) {
632
+ // The canonical property name is `include`. We accept `paths` for backwards compatibility
633
+ // with a bug in JSDoc 3.2.x.
634
+ staticFilePaths =
635
+ conf.default.staticFiles.include || conf.default.staticFiles.paths || [];
636
+ staticFileFilter = new (require('jsdoc/src/filter').Filter)(
637
+ conf.default.staticFiles
638
+ );
639
+ staticFileScanner = new (require('jsdoc/src/scanner').Scanner)();
640
+
641
+ staticFilePaths.forEach(function (filePath) {
642
+ var extraStaticFiles;
643
+
644
+ filePath = path.resolve(env.pwd, filePath);
645
+ extraStaticFiles = staticFileScanner.scan(
646
+ [filePath],
647
+ 10,
648
+ staticFileFilter
649
+ );
650
+
651
+ extraStaticFiles.forEach(function (fileName) {
652
+ var sourcePath = fs.toDir(filePath);
653
+ var toDir = fs.toDir(fileName.replace(sourcePath, outdir));
654
+
655
+ fs.mkPath(toDir);
656
+ fs.copyFileSync(fileName, toDir);
657
+ });
658
+ });
659
+ }
660
+
661
+ if (sourceFilePaths.length) {
662
+ sourceFiles = shortenPaths(sourceFiles, path.commonPrefix(sourceFilePaths));
663
+ }
664
+ data().each(function (doclet) {
665
+ var docletPath;
666
+ var url = helper.createLink(doclet);
667
+
668
+ helper.registerLink(doclet.longname, url);
669
+
670
+ // add a shortened version of the full path
671
+ if (doclet.meta) {
672
+ docletPath = getPathFromDoclet(doclet);
673
+ docletPath = sourceFiles[docletPath].shortened;
674
+ if (docletPath) {
675
+ doclet.meta.shortpath = docletPath;
676
+ }
677
+ }
678
+ });
679
+
680
+ data().each(function (doclet) {
681
+ var url = helper.longnameToUrl[doclet.longname];
682
+
683
+ if (url.indexOf('#') > -1) {
684
+ doclet.id = helper.longnameToUrl[doclet.longname].split(/#/).pop();
685
+ } else {
686
+ doclet.id = doclet.name;
687
+ }
688
+
689
+ if (needsSignature(doclet)) {
690
+ addSignatureParams(doclet);
691
+ addSignatureReturns(doclet);
692
+ addAttribs(doclet);
693
+ }
694
+ });
695
+
696
+ // do this after the urls have all been generated
697
+ data().each(function (doclet) {
698
+ doclet.ancestors = getAncestorLinks(doclet);
699
+
700
+ if (doclet.kind === 'member') {
701
+ addSignatureTypes(doclet);
702
+ addAttribs(doclet);
703
+ }
704
+
705
+ if (doclet.kind === 'constant') {
706
+ addSignatureTypes(doclet);
707
+ addAttribs(doclet);
708
+ doclet.kind = 'member';
709
+ }
710
+ });
711
+
712
+ members = helper.getMembers(data);
713
+ members.tutorials = tutorials.children;
714
+
715
+ // output pretty-printed source files by default
716
+ outputSourceFiles = conf.default && conf.default.outputSourceFiles !== false;
717
+
718
+ // add template helpers
719
+ view.find = find;
720
+ view.linkto = linkto;
721
+ view.resolveAuthorLinks = resolveAuthorLinks;
722
+ view.tutoriallink = tutoriallink;
723
+ view.htmlsafe = htmlsafe;
724
+ view.outputSourceFiles = outputSourceFiles;
725
+ view.packageInfo = packageInfo || null;
726
+ view.homepage = getHomepageData(members, globalUrl);
727
+
728
+ // once for all
729
+ view.nav = buildNav(members);
730
+ attachModuleSymbols(find({ longname: { left: 'module:' } }), members.modules);
731
+
732
+ // generate the pretty-printed source files first so other pages can link to them
733
+ if (outputSourceFiles) {
734
+ generateSourceFiles(sourceFiles, opts.encoding);
735
+ }
736
+
737
+ if (members.globals.length) {
738
+ generate('Global', [{ kind: 'globalobj' }], globalUrl);
739
+ }
740
+
741
+ // index page displays information from package.json and lists files
742
+ files = find({ kind: 'file' });
743
+ packages = find({ kind: 'package' });
744
+
745
+ generate(
746
+ 'Home',
747
+ packages
748
+ .concat([
749
+ {
750
+ kind: 'mainpage',
751
+ readme: opts.readme,
752
+ longname: opts.mainpagetitle ? opts.mainpagetitle : 'Main Page',
753
+ },
754
+ ])
755
+ .concat(files),
756
+ indexUrl
757
+ );
758
+
759
+ // set up the lists that we'll use to generate pages
760
+ classes = taffy(members.classes);
761
+ modules = taffy(members.modules);
762
+ namespaces = taffy(members.namespaces);
763
+ mixins = taffy(members.mixins);
764
+ externals = taffy(members.externals);
765
+ interfaces = taffy(members.interfaces);
766
+
767
+ Object.keys(helper.longnameToUrl).forEach(function (longname) {
768
+ var myClasses = helper.find(classes, { longname: longname });
769
+ var myExternals = helper.find(externals, { longname: longname });
770
+ var myInterfaces = helper.find(interfaces, { longname: longname });
771
+ var myMixins = helper.find(mixins, { longname: longname });
772
+ var myModules = helper.find(modules, { longname: longname });
773
+ var myNamespaces = helper.find(namespaces, { longname: longname });
774
+
775
+ if (myModules.length) {
776
+ generate(
777
+ 'Module: ' + myModules[0].name,
778
+ myModules,
779
+ helper.longnameToUrl[longname]
780
+ );
781
+ }
782
+
783
+ if (myClasses.length) {
784
+ generate(
785
+ 'Class: ' + myClasses[0].name,
786
+ myClasses,
787
+ helper.longnameToUrl[longname]
788
+ );
789
+ }
790
+
791
+ if (myNamespaces.length) {
792
+ generate(
793
+ 'Namespace: ' + myNamespaces[0].name,
794
+ myNamespaces,
795
+ helper.longnameToUrl[longname]
796
+ );
797
+ }
798
+
799
+ if (myMixins.length) {
800
+ generate(
801
+ 'Mixin: ' + myMixins[0].name,
802
+ myMixins,
803
+ helper.longnameToUrl[longname]
804
+ );
805
+ }
806
+
807
+ if (myExternals.length) {
808
+ generate(
809
+ 'External: ' + myExternals[0].name,
810
+ myExternals,
811
+ helper.longnameToUrl[longname]
812
+ );
813
+ }
814
+
815
+ if (myInterfaces.length) {
816
+ generate(
817
+ 'Interface: ' + myInterfaces[0].name,
818
+ myInterfaces,
819
+ helper.longnameToUrl[longname]
820
+ );
821
+ }
822
+ });
823
+
824
+ // TODO: move the tutorial functions to templateHelper.js
825
+ function generateTutorial(title, tutorial, filename) {
826
+ var tutorialData = {
827
+ title: title,
828
+ header: tutorial.title,
829
+ content: tutorial.parse(),
830
+ children: tutorial.children,
831
+ };
832
+ var tutorialPath = path.join(outdir, filename);
833
+ var html = view.render('tutorial.tmpl', tutorialData);
834
+
835
+ // yes, you can use {@link} in tutorials too!
836
+ html = helper.resolveLinks(html); // turn {@link foo} into <a href="foodoc.html">foo</a>
837
+
838
+ fs.writeFileSync(tutorialPath, html, 'utf8');
839
+ }
840
+
841
+ // tutorials can have only one parent so there is no risk for loops
842
+ function saveChildren(node) {
843
+ node.children.forEach(function (child) {
844
+ generateTutorial(
845
+ 'Tutorial: ' + child.title,
846
+ child,
847
+ helper.tutorialToUrl(child.name)
848
+ );
849
+ saveChildren(child);
850
+ });
851
+ }
852
+
853
+ saveChildren(tutorials);
854
+ };