Skip to content

Conversation

@flakey5
Copy link
Member

@flakey5 flakey5 commented May 31, 2025

Closes #214

TODO:

@codecov-commenter
Copy link

codecov-commenter commented May 31, 2025

Codecov Report

❌ Patch coverage is 93.86531% with 266 lines in your changes missing coverage. Please review.
✅ Project coverage is 82.42%. Comparing base (9285e74) to head (1cf5261).
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/generators/json/utils/sections/method.mjs 80.96% 67 Missing ⚠️
src/generators/json/utils/sections/event.mjs 76.11% 42 Missing and 1 partial ⚠️
src/utils/generator-error.mjs 21.05% 30 Missing ⚠️
src/generators/json/utils/sections/property.mjs 81.16% 29 Missing ⚠️
src/generators/json/utils/sections/index.mjs 78.94% 23 Missing and 1 partial ⚠️
src/generators/json/index.mjs 80.20% 19 Missing ⚠️
src/utils/assertAstType.mjs 80.00% 11 Missing ⚠️
...generators/json/utils/createParameterGroupings.mjs 91.17% 9 Missing ⚠️
src/generators/json-all/index.mjs 89.74% 8 Missing ⚠️
src/generators/json/utils/sections/base.mjs 95.07% 7 Missing ⚠️
... and 7 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #287      +/-   ##
==========================================
+ Coverage   74.60%   82.42%   +7.81%     
==========================================
  Files         112      140      +28     
  Lines       10700    15034    +4334     
  Branches      722     1027     +305     
==========================================
+ Hits         7983    12392    +4409     
+ Misses       2714     2637      -77     
- Partials        3        5       +2     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@avivkeller avivkeller mentioned this pull request Jun 24, 2025
6 tasks
@avivkeller
Copy link
Member

@flakey5 is this still in progress? Need any help?

@flakey5
Copy link
Member Author

flakey5 commented Sep 30, 2025

Still in progress just haven't been committing, I do wanna get this ready for review within the next coming weeks though

@flakey5 flakey5 force-pushed the flakey5/20240909/new-json-gen branch from a4cd425 to d711fee Compare October 6, 2025 20:16
@vercel
Copy link

vercel bot commented Oct 6, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Updated (UTC)
api-docs-tooling Ready Ready Preview Dec 1, 2025 1:38am

@flakey5
Copy link
Member Author

flakey5 commented Nov 10, 2025

Bulk of this is done, with only two main things left:

  • Couple of failing tests for createParameterGroupings
  • Adding the @constructor property to class sections

I'm leaving this as a draft until those are done, but the rest is ready for review

Copy link
Member

@avivkeller avivkeller left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the rest is ready for review

I've left a first round of reviews. Thank you so much for this effort!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need a seperate generator error?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It exists just to give the general spot in the ast that an error happens to assist with debugging, can definitely be removed if unwanted

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nodejs/web-infra im +1 on removing, WDYT?

'use strict';

// Grabs the default value if present
export const DEFAULT_EXPRESSION = /^(D|d)efault(s|):$/;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's re-use the DEFAULT_EXPRESSION constant from the legacy generator

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't since this is used for matching against the text in the ast which doesn't have the asterisks, while the one in the legacy generator is going off of the stringified version in textRaw which does

Closes #214

Signed-off-by: flakey5 <73616808+flakey5@users.noreply.github.com>
@flakey5
Copy link
Member Author

flakey5 commented Nov 29, 2025

Marking ready for review, should be all there minus #287 (comment)

@flakey5 flakey5 marked this pull request as ready for review November 29, 2025 02:49
@flakey5 flakey5 requested a review from a team as a code owner November 29, 2025 02:49
Comment on lines +46 to +53
input.forEach(section => {
const copiedSection = {};

Object.keys(section).forEach(key => {
if (!propertiesToIgnore.includes(key)) {
copiedSection[key] = section[key];
}
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
input.forEach(section => {
const copiedSection = {};
Object.keys(section).forEach(key => {
if (!propertiesToIgnore.includes(key)) {
copiedSection[key] = section[key];
}
});
input.forEach({ ignoreThisKey, ...section } => {

WDYT?

Comment on lines +55 to +64
switch (section.type) {
case 'module':
generatedValue.modules.push(copiedSection);
break;
case 'text':
generatedValue.text.push(copiedSection);
break;
default:
throw new TypeError(`unsupported root section type ${section.type}`);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
switch (section.type) {
case 'module':
generatedValue.modules.push(copiedSection);
break;
case 'text':
generatedValue.text.push(copiedSection);
break;
default:
throw new TypeError(`unsupported root section type ${section.type}`);
}
generatedValue[type === 'module' ? 'modules' : type].push(copiedSection)

nit

*/
async generate(input, { version, output }) {
const generatedValue = {
$schema: `${BASE_URL}/docs/${DOC_NODE_VERSION}/api/node-doc-all-schema.jsonc`,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make a SCHEMA_URL constant?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really since it intakes the version given by the generator options, same for the rest of the schema url instances

* @param {string} version
*/
export function generateJsonSchema(version) {
const jsonSchemaUrl = `${BASE_URL}/docs/${version}/api/node-doc-schema.json`;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto on the constant

// Returns: {string}
// Returns {string}
// Returns: {string} bla bla bla
export const METHOD_RETURN_TYPE_EXTRACTOR = /^(?:R|r)eturns(:?) {(.*)}( .*)?$/;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather do a case insensitive search

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't just checking if it starts with Returns: though, it's extracting the type information and possible description from it as well

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we change it to /^Returns(:?) {(.*)}( .*)?$/i?

}

return {
$schema: `${BASE_URL}docs/${version}/api/node-doc-schema.json`,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto on the constant

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nodejs/web-infra im +1 on removing, WDYT?

Co-authored-by: Aviv Keller <me@aviv.sh>
Signed-off-by: flakey5 <73616808+flakey5@users.noreply.github.com>
Signed-off-by: flakey5 <73616808+flakey5@users.noreply.github.com>
);
}

if (rest[0]?.type === 'link') {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we have these specific extractors on dedicated function for ... extracting these? Also are the types here only link and "else"?


parameter['@name'] = 'value';

const { types, endingIndex } = parseTypeList(parameterAst.children);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feels like we could dedup this


break;
}
case 'text': {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like more duped code

// it
const { types, endingIndex } = parseTypeList(paragraph.children, 2);

if (types.length === 0) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like more duped code

}
case 'link': {
const { types, endingIndex } = parseTypeList(paragraph.children, 1);
if (types.length === 0) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like more duped code

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we mhave this file output be in a different folder? Like a generated folder?

Copy link
Member

@ovflowd ovflowd Nov 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be automatically generated?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The schema should be maintained by hand. It doesn't need to be in a standalone json file though, we could have it be a function like with the json-all's schema. Only reason it is standalone currently is to make it so we can have better intellisense/autocomplete in editors when modifying the schema

Copy link
Member

@ovflowd ovflowd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly LGTM, I think that we can dedup lots of the code, extract many of the utility functions within parsing things (such as event, property) into dedicated tiny functions that do one thing and then unit test them.

Makes the parent functions simpler, easier to read the code and understand what pieces does what, reusability and unit testing.

Signed-off-by: flakey5 <73616808+flakey5@users.noreply.github.com>
@avivkeller
Copy link
Member

@flakey5 Is there a reason why many properties start with @? I feel like that makes it harder for consumers, since they now need to <>.["@prop"] instead of just <>.prop

@avivkeller avivkeller requested a review from Copilot December 3, 2025 22:52
Copilot finished reviewing on behalf of avivkeller December 3, 2025 22:54
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces new JSON generators (json and json-all) for the Node.js documentation toolkit, providing machine-readable JSON representations of API documentation.

Key Changes:

  • New json generator that outputs individual JSON files for each API module with comprehensive type information, method signatures, properties, and events
  • New json-all generator that combines all JSON outputs into a single file
  • Comprehensive JSON schema definition with TypeScript type generation support

Reviewed changes

Copilot reviewed 39 out of 44 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
src/generators/json/index.mjs Main JSON generator implementation
src/generators/json-all/index.mjs Aggregator generator for combined JSON output
src/generators/json/schema.jsonc JSON schema definition for documentation structure
src/generators/json/utils/sections/*.mjs Section parsers for modules, classes, methods, properties, and events
src/generators/json/utils/parseTypeList.mjs Type list parsing utility
src/generators/json/utils/createParameterGroupings.mjs Method signature parameter grouping logic
src/utils/buildHierarchy.mjs Shared utility for building hierarchical entry structures
src/utils/assertAstType.mjs AST node type assertion utilities
src/utils/generator-error.mjs Custom error class for generator errors
src/utils/unist.mjs Enhanced node transformation with support for break, delete, and link nodes
scripts/generate-json-types.mjs Script to generate TypeScript definitions from JSON schema
package.json New dependencies for JSON parsing and schema validation
.github/workflows/generate.yml CI workflow integration for JSON generator

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

// Parse the JSON schema into an object
const schema = await parse(schemaString);

// Compile the the JSON schema into TypeScript typedefs
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment "the the" contains a duplicate word. It should be "the JSON schema".

Suggested change
// Compile the the JSON schema into TypeScript typedefs
// Compile the JSON schema into TypeScript typedefs

Copilot uses AI. Check for mistakes.
// TODO: if the type of the parameter is `object`, sometimes it's followed
// by a `list` node that contains the properties expected on that object.
// I'd really like those to be included in the json output, but for now
// they're not going to be for simplicitly sakes (they still should be in
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment "for simplicitly sakes" contains a spelling error. It should be "for simplicity's sake" or "for simplicity sake".

Copilot uses AI. Check for mistakes.

/**
* Given a list of parameter names in the order that they should appear and
* a map of paramter names to their type info, let's create the signature
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment contains a grammatical error. "a map of paramter names" should be "a map of parameter names".

Copilot uses AI. Check for mistakes.
Comment on lines +491 to +495
assert.equal(base.changes, undefined);
assert.equal(base['@since'], undefined);
assert.equal(base.napiVersion, undefined);
assert.equal(base.removedIn, undefined);
assert.equal(base['@deprecated'], undefined);
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The assertion uses assert.equal() which performs loose equality. For more explicit comparison, consider using assert.strictEqual() instead.

Suggested change
assert.equal(base.changes, undefined);
assert.equal(base['@since'], undefined);
assert.equal(base.napiVersion, undefined);
assert.equal(base.removedIn, undefined);
assert.equal(base['@deprecated'], undefined);
assert.strictEqual(base.changes, undefined);
assert.strictEqual(base['@since'], undefined);
assert.strictEqual(base.napiVersion, undefined);
assert.strictEqual(base.removedIn, undefined);
assert.strictEqual(base['@deprecated'], undefined);

Copilot uses AI. Check for mistakes.
Comment on lines +156 to +362
"additionalProperties": false
},
],
},

"Class": {
"allOf": [
{ "$ref": "#/definitions/SectionBase" },
{
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": ["class"],
},
"@constructor": {
"type": "array",
"items": { "$ref": "#/definitions/MethodSignature" },
},
"methods": {
"type": "array",
"items": { "$ref": "#/definitions/Method" },
},
"staticMethods": {
"type": "array",
"items": { "$ref": "#/definitions/Method" },
},
"properties": {
"type": "array",
"items": { "$ref": "#/definitions/Property" },
},
"events": {
"type": "array",
"items": { "$ref": "#/definitions/Event" },
},
},
"required": [
"type",
],
"additionalProperties": false
},
],
},

"Method": {
"description": "A JavaScript function.",
"allOf": [
{ "$ref": "#/definitions/SectionBase" },
{
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": ["method"],
},
"signatures": {
"type": "array",
"items": { "$ref": "#/definitions/MethodSignature" },
},
},
"required": ["type", "signatures"],
"additionalProperties": false
},
],
},

"MethodSignature": {
"type": "object",
"properties": {
"parameters": {
"type": "array",
"items": { "$ref": "#/definitions/MethodParameter" },
},
"@returns": {
"$ref": "#/definitions/MethodReturnType",
},
},
"required": ["parameters", "@returns"],
"additionalProperties": false
},

"MethodParameter": {
"type": "object",
"properties": {
"@name": {
"type": "string",
"description": "Name of the parameter.",
},
"@type": {
"description": "Type of the parameter",
"oneOf": [
{
"type": "string",
"examples": ["string"],
},
{
"type": "array",
"items": {
"type": "string",
},
"minItems": 1,
},
],
},
"description": {
"type": "string",
},
"@default": {
"type": "string",
"description": "The parameter's default value",
"examples": ["foo"],
},
},
"required": ["@name", "@type"],
"additionalProperties": false
},

"MethodReturnType": {
"type": "object",
"description": "A method signature's return type.",
"properties": {
"description": {
"type": "string",
},
"@type": {
"description": "The method signature's return type.",
"oneOf": [
{
"type": "string",
"examples": ["string"],
},
{
"type": "array",
"items": { "type": "string" },
"examples": [["string", "Promise<string>"]],
"minItems": 1,
},
],
},
},
"required": ["@type"],
"additionalProperties": false
},

"Global": {
"type": "object",
"properties": {},
"required": [],
},

"Property": {
"description": "A property on a JavaScript object or class.",
"allOf": [
{ "$ref": "#/definitions/SectionBase" },
{
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": ["property"],
},
"@type": {
"description": "JavaScript type of the property.",
"oneOf": [
{
"type": "string",
"examples": ["string"],
},
{
"type": "array",
"items": {
"type": "string",
},
"minItems": 1,
},
],
},
"mutable": {
"type": "boolean",
"description": "Is this property modifiable by user code?",
"default": false,
},
},
"required": ["type"],
"additionalProperties": false
},
],
},

"Event": {
"description": "An event that can be emitted by the parent object or class.",
"allOf": [
{ "$ref": "#/definitions/SectionBase" },
{
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": ["event"],
},
"parameters": {
"type": "array",
"items": { "$ref": "#/definitions/MethodParameter" },
},
},
"required": ["type", "parameters"],
"additionalProperties": false
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Inconsistent indentation: the additionalProperties: false statements on lines 156, 195, 217, 234, 270, 297, 340, 362, and 399 are not indented consistently with the surrounding JSON structure. They should be indented at the same level as the other properties in their respective objects.

Copilot uses AI. Check for mistakes.
"$ref": "#/definitions/MethodReturnType",
},
},
"required": ["parameters", "@returns"],
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The MethodSignature.parameters property is defined as required (line 233), but in several places in the code, signatures are created with an empty parameters array instead of omitting it when there are no parameters. Consider making this property optional or consistently handling empty arrays.

Suggested change
"required": ["parameters", "@returns"],
"required": ["@returns"],

Copilot uses AI. Check for mistakes.
export function createSection(head, entries, version) {
const entryHierarchy = buildHierarchy(entries);

if (entryHierarchy.length != 1) {
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Inconsistent comparison operator: The code uses != (loose inequality) instead of !== (strict inequality). For consistency with modern JavaScript best practices, use strict equality operators.

Copilot uses AI. Check for mistakes.
*/
let section;
try {
section = createSectionBase(entry, parent?.type);
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Superfluous argument passed to function createSectionBase.

Copilot uses AI. Check for mistakes.
* - Run `scripts/generate-json-types.mjs` and ensure there aren't type errors
*/

"$schema": "http://json-schema.org/draft-07/schema#",
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The $schema reference uses an http URL, which enables man-in-the-middle tampering when tools fetch the JSON Schema for validation. An attacker intercepting the connection could serve a modified schema to alter validation behavior or inject malicious constraints. Fix by switching to https:

"$schema": "https://json-schema.org/draft-07/schema#"
Suggested change
"$schema": "http://json-schema.org/draft-07/schema#",
"$schema": "https://json-schema.org/draft-07/schema#",

Copilot uses AI. Check for mistakes.
@avivkeller avivkeller added this to the Web Generator Migration milestone Dec 3, 2025
Copy link
Member

@avivkeller avivkeller left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More comments :-)


// Not code, let's stringify it and add it to the description.
section.description ??= '';
section.description += `${transformNodeToString(node)}${node.type === 'paragraph' ? '\n' : ' '}`;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this added newline needed?


if (section.description) {
section.description = section.description.trim();
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
}
section.description &&= section.description.trim();

} else {
section['@example'] = node.value;
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const examples = [...enforceArray(section['@example']), node.value];
section['@example'] = examples.length === 1 ? examples[0] : examples;

Comment on lines +73 to +79
value = Number(value);

if (isNaN(value)) {
throw new GeneratorError(`Stability index ${stability.index} NaN`);
}
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
value = Number(value);
if (isNaN(value)) {
throw new GeneratorError(`Stability index ${stability.index} NaN`);
}
}
const value = parseFloat(stability.index);

Do we really need to throw an error if it's NaN? I'd rather produce JSON with the NaN (even though this should never happen) than error the entire generator?

Comment on lines +80 to +84
section.stability = {
value,
text: stability.description,
};
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we just do section.stability = stability? Like keep the { index, description } format?

Comment on lines +92 to +95
if (entryHierarchy.length != 1) {
throw new TypeError(`${head.api_doc_source} has multiple root elements`);
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. I don't think there will ever be more than one root, right?
  2. Let's just ignore the extra data, not error on it

Comment on lines +102 to +108
if (section.type !== 'module' && section.type !== 'text') {
throw new GeneratorError(
`expected root section to be a module or text, got ${section.type}`,
{ entry: head }
);
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these if checks are too strict, IMO. I feel like there's nothing wrong with it having the wrong type (like, the generator should still work)

// Returns: {string}
// Returns {string}
// Returns: {string} bla bla bla
export const METHOD_RETURN_TYPE_EXTRACTOR = /^(?:R|r)eturns(:?) {(.*)}( .*)?$/;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we change it to /^Returns(:?) {(.*)}( .*)?$/i?

* @returns {Promise<object>}
*/
async generate(input, { version, output }) {
const versionString = `v${version.toString()}`;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit

Suggested change
const versionString = `v${version.toString()}`;
const versionString = `v${version.raw}`;

Comment on lines +11 to +25
export function findParentSection(section, type) {
type = enforceArray(type);

let parent = section.parent;

while (parent) {
if (type.includes(parent.type)) {
return parent;
}

parent = parent.parent;
}

return undefined;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
export function findParentSection(section, type) {
type = enforceArray(type);
let parent = section.parent;
while (parent) {
if (type.includes(parent.type)) {
return parent;
}
parent = parent.parent;
}
return undefined;
}
export function findParentSection({ parent }, type) {
type = enforceArray(type);
while (parent) {
if (type.includes(parent.type)) {
return parent;
}
parent = parent.parent;
}
return undefined;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

New JSON generator schema

5 participants