extract-output-schemas.js 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. #!/usr/bin/env node
  2. // Emits, as JSON on stdout, one entry per node module in nodes/ describing what
  3. // its outputSchema currently declares at the top level: the plain property
  4. // names, any patternProperties regexes (for dynamic-named branches such as
  5. // switch's case0/case1), and whether additionalProperties allows anything
  6. // else through. check-output-schema-drift.py compares this against what a
  7. // real execution actually returned.
  8. //
  9. // Requiring each module is safe without the runtime: outputSchema is plain
  10. // data assembled at module load time, and nothing in it touches the
  11. // `smartbotic` global - that only happens inside execute(), which this never
  12. // calls.
  13. 'use strict';
  14. const fs = require('fs');
  15. const path = require('path');
  16. const ROOT = path.resolve(__dirname, '..', 'nodes');
  17. function walk(dir, out) {
  18. for (const entry of fs.readdirSync(dir)) {
  19. const full = path.join(dir, entry);
  20. const stat = fs.statSync(full);
  21. if (stat.isDirectory()) {
  22. walk(full, out);
  23. } else if (entry.endsWith('.js')) {
  24. out.push(full);
  25. }
  26. }
  27. }
  28. function nodeTypeOf(source) {
  29. const m = source.match(/@node\s+([a-zA-Z0-9_-]+)/);
  30. return m ? m[1] : null;
  31. }
  32. const files = [];
  33. walk(ROOT, files);
  34. const result = {};
  35. for (const file of files) {
  36. const source = fs.readFileSync(file, 'utf8');
  37. const nodeType = nodeTypeOf(source);
  38. if (!nodeType) {
  39. process.stderr.write('no @node tag found in ' + file + ', skipping\n');
  40. continue;
  41. }
  42. let mod;
  43. try {
  44. delete require.cache[require.resolve(file)];
  45. mod = require(file);
  46. } catch (e) {
  47. process.stderr.write('could not load ' + file + ': ' + e.message + '\n');
  48. continue;
  49. }
  50. const schema = mod.outputSchema;
  51. if (!schema || typeof schema !== 'object') {
  52. process.stderr.write(nodeType + ' (' + file + ') has no outputSchema\n');
  53. continue;
  54. }
  55. const properties = Object.keys(schema.properties || {});
  56. const patternProps = Object.keys(schema.patternProperties || {});
  57. const additionalProperties = schema.additionalProperties === true;
  58. result[nodeType] = {
  59. file: path.relative(path.resolve(__dirname, '..'), file),
  60. properties: properties,
  61. patternProperties: patternProps,
  62. additionalProperties: additionalProperties
  63. };
  64. }
  65. process.stdout.write(JSON.stringify(result, null, 2) + '\n');