Skip to content

Commit ea86897

Browse files
committed
refactor: applied bug fixes and code refactor from #55
1 parent 8a9bf15 commit ea86897

File tree

14 files changed

+262
-158
lines changed

14 files changed

+262
-158
lines changed

eslint.config.mjs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,12 @@ import eslintConfigPrettier from 'eslint-config-prettier';
33
import globals from 'globals';
44

55
export default [
6-
{ languageOptions: { globals: globals.node } },
7-
// Visit https://eslint.org/docs/latest/rules to learn more about these rules
6+
// @see https://eslint.org/docs/latest/use/configure/configuration-files#specifying-files-and-ignores
7+
{
8+
files: ['src/**/*.mjs'],
9+
languageOptions: { globals: globals.node },
10+
},
11+
// @see https://eslint.org/docs/latest/rules to learn more about these rules
812
pluginJs.configs.recommended,
913
eslintConfigPrettier,
1014
];

src/constants.mjs

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,6 @@ export const DOC_MDN_BASE_URL_JS_PRIMITIVES = `${DOC_MDN_BASE_URL_JS}Data_struct
1919
// This is the base URL for the MDN JavaScript global objects documentation
2020
export const DOC_MDN_BASE_URL_JS_GLOBALS = `${DOC_MDN_BASE_URL_JS}Reference/Global_Objects/`;
2121

22-
// These are YAML keys from the Markdown YAML Metadata that should always be arrays
23-
export const DOC_API_YAML_KEYS_ARRAYS = [
24-
'added',
25-
'napiVersion',
26-
'deprecated',
27-
'removed',
28-
'introduced_in',
29-
];
30-
3122
// These are YAML keys from the Markdown YAML metadata that should be
3223
// removed and appended to the `update` key
3324
export const DOC_API_YAML_KEYS_UPDATE = [
@@ -73,6 +64,16 @@ export const DOC_API_HEADING_TYPES = [
7364
},
7465
];
7566

67+
// This is a mapping for the `API` updates within the Markdown content and their respective
68+
// content that should be mapping into `changes` property for better mapping on HTML
69+
export const DOC_API_UPDATE_MAPPING = {
70+
added: 'Added in',
71+
removed: 'Removed in',
72+
deprecated: 'Deprecated since',
73+
introduced_in: 'Introduced in',
74+
napiVersion: 'N-API Version',
75+
};
76+
7677
// This is a mapping for types within the Markdown content and their respective
7778
// JavaScript primitive types within the MDN JavaScript docs
7879
// @see DOC_MDN_BASE_URL_JS_PRIMITIVES

src/generators.mjs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import availableGenerators from './generators/index.mjs';
44

55
/**
6-
* @typedef {import('./types.d.ts').ApiDocMetadataEntry} ApiDocMetadataEntry Local type alias for the API doc metadata entry
76
* @typedef {{ ast: import('./generators/types.d.ts').GeneratorMetadata<ApiDocMetadataEntry, ApiDocMetadataEntry>}} AstGenerator The AST "generator" is a facade for the AST tree and it isn't really a generator
87
* @typedef {import('./generators/types.d.ts').AvailableGenerators & AstGenerator} AllGenerators A complete set of the available generators, including the AST one
98
*

src/generators/json-simple/index.mjs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { join } from 'node:path';
1010
* This generator is a top-level generator, and it takes the raw AST tree of the API doc files
1111
* and returns a stringified JSON version of the API docs.
1212
*
13-
* @typedef {import('../../types.d.ts').ApiDocMetadataEntry[]} Input
13+
* @typedef {Array<ApiDocMetadataEntry>} Input
1414
*
1515
* @type {import('../types.d.ts').GeneratorMetadata<Input, string>}
1616
*/
@@ -25,7 +25,6 @@ export default {
2525
dependsOn: 'ast',
2626

2727
async generate(input, options) {
28-
2928
// This simply grabs all the different files and stringifies them
3029
const stringifiedContent = JSON.stringify(input, null, 2);
3130

src/generators/types.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { SemVer } from 'semver';
22
import type availableGenerators from './index.mjs';
3-
import { ApiDocReleaseEntry } from '../types';
3+
import type { ApiDocReleaseEntry } from '../types';
44

55
// All available generators as an inferable type, to allow Generator interfaces
66
// to be type complete and runtime friendly within `runGenerators`

src/loader.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use strict';
22

3-
import { extname } from 'node:path';
43
import { readFile } from 'node:fs/promises';
4+
import { extname } from 'node:path';
55

66
import { globSync } from 'glob';
77
import { VFile } from 'vfile';

src/metadata.mjs

Lines changed: 64 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
'use strict';
22

3+
import { u as createTree } from 'unist-builder';
4+
5+
import { compare } from 'semver';
6+
7+
import { DOC_API_UPDATE_MAPPING } from './constants.mjs';
8+
import { coerceSemVer } from './utils/generators.mjs';
9+
310
/**
4-
* @typedef {import('./types.d.ts').ApiDocMetadataEntry} ApiDocMetadataEntry Local type alias for the API doc metadata entry
5-
* @typedef {import('./types.d.ts').ApiDocRawMetadataEntry} ApiDocRawMetadataEntry Local type alias for the API doc raw metadata entry
6-
* @typedef {import('./types.d.ts').HeadingMetadataEntry} HeadingMetadataEntry Local type alias for the heading metadata entry
7-
*
811
* This method allows us to handle creation of Metadata entries
912
* within the current scope of API docs being parsed
1013
*
@@ -14,6 +17,29 @@
1417
* @param {InstanceType<typeof import('github-slugger').default>} slugger A GitHub Slugger
1518
*/
1619
const createMetadata = slugger => {
20+
/**
21+
* Maps `updates` into `changes` format, merges them and sorts them by version
22+
*
23+
* @param {Array<ApiDocMetadataUpdate>} updates Original updates to be merged
24+
* @param {Array<ApiDocMetadataChange>} changes Changes to be merged into updates
25+
* @returns {Array<ApiDocMetadataChange>} Mapped, merged and sorted changes
26+
*/
27+
const mergeUpdatesIntoChanges = (updates, changes) => {
28+
// Maps the `updates` array into the same format used by the `changes` array
29+
// So that for generators such as HTML, we render all the changes + updates
30+
// into one single list of changes, for example a HTML table
31+
const mappedUpdatesIntoChanges = updates.map(({ version, type }) => ({
32+
version,
33+
'pr-url': undefined,
34+
description: `${DOC_API_UPDATE_MAPPING[type]}: ${version.join(', ')}`,
35+
}));
36+
37+
// Sorts the updates and changes by the first version on a given entry
38+
return [...mappedUpdatesIntoChanges, ...changes].sort((a, b) =>
39+
compare(coerceSemVer(a.version[0]), coerceSemVer(b.version[0]))
40+
);
41+
};
42+
1743
/**
1844
* This holds a temporary buffer of raw metadata before being
1945
* transformed into NavigationEntries and MetadataEntries
@@ -31,8 +57,13 @@ const createMetadata = slugger => {
3157
name: undefined,
3258
depth: -1,
3359
},
34-
properties: {},
35-
stability: undefined,
60+
properties: {
61+
type: undefined,
62+
source_link: undefined,
63+
updates: [],
64+
changes: [],
65+
},
66+
stability: createTree('root', []),
3667
};
3768

3869
return {
@@ -47,10 +78,10 @@ const createMetadata = slugger => {
4778
/**
4879
* Set the Stability Index of a given Metadata
4980
*
50-
* @param {ApiDocMetadataEntry['stability']} stability The new stability metadata
81+
* @param {import('unist').Parent} stability The stability index node to be added
5182
*/
52-
setStability: stability => {
53-
internalMetadata.stability = stability;
83+
addStability: stability => {
84+
internalMetadata.stability.children.push(stability);
5485
},
5586
/**
5687
* Set the Metadata (from YAML if exists) properties to the current Metadata entry
@@ -61,13 +92,27 @@ const createMetadata = slugger => {
6192
* meaning that this method can be called multiple times to update the properties
6293
* and complement each set of data.
6394
*
95+
* Note: This ensures only valid properties get defined and that we don't accidentally override
96+
* values, when we just want to complement them whenever possible.
97+
*
6498
* @param {Partial<ApiDocRawMetadataEntry>} properties Extra Metadata properties to be defined
6599
*/
66100
updateProperties: properties => {
67-
internalMetadata.properties = {
68-
...internalMetadata.properties,
69-
...properties,
70-
};
101+
if (properties.type) {
102+
internalMetadata.properties.type = properties.type;
103+
}
104+
105+
if (properties.source_link) {
106+
internalMetadata.properties.source_link = properties.source_link;
107+
}
108+
109+
if (properties.changes) {
110+
internalMetadata.properties.changes.push(...properties.changes);
111+
}
112+
113+
if (properties.updates) {
114+
internalMetadata.properties.updates.push(...properties.updates);
115+
}
71116
},
72117
/**
73118
* Generates a new Navigation entry and pushes them to the internal collection
@@ -90,7 +135,6 @@ const createMetadata = slugger => {
90135

91136
const {
92137
type: yamlType,
93-
name: yamlName,
94138
source_link: sourceLink,
95139
updates = [],
96140
changes = [],
@@ -99,18 +143,22 @@ const createMetadata = slugger => {
99143
// We override the type of the heading if we have a YAML type
100144
internalMetadata.heading.type = yamlType || internalMetadata.heading.type;
101145

146+
// Maps the Stability Index AST nodes into a JSON objects from their data properties
147+
internalMetadata.stability.toJSON = () =>
148+
internalMetadata.stability.children.map(node => node.data);
149+
102150
// Returns the Metadata entry for the API doc
103151
return {
104152
// The API file basename (without the extension)
105-
api: yamlName || apiDoc.stem,
153+
api: apiDoc.stem,
106154
// The path/slug of the API section
107155
slug: `${apiDoc.stem}.html${slugHash}`,
108156
// The source link of said API section
109157
sourceLink: sourceLink,
110158
// The latest updates to an API section
111159
updates,
112160
// The full-changeset to an API section
113-
changes,
161+
changes: mergeUpdatesIntoChanges(updates, changes),
114162
// The Heading metadata
115163
heading: internalMetadata.heading,
116164
// The Stability Index of the API section

src/parser.mjs

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
11
'use strict';
22

3-
import { remark } from 'remark';
4-
import remarkGfm from 'remark-gfm';
5-
63
import { u as createTree } from 'unist-builder';
4+
import { findAfter } from 'unist-util-find-after';
75
import { remove } from 'unist-util-remove';
86
import { selectAll } from 'unist-util-select';
97
import { SKIP, visit } from 'unist-util-visit';
10-
import { findAfter } from 'unist-util-find-after';
118

129
import createMetadata from './metadata.mjs';
1310
import createQueries from './queries.mjs';
1411

12+
import { transformTypeToReferenceLink } from './utils/parser.mjs';
13+
import { getRemark } from './utils/remark.mjs';
1514
import { createNodeSlugger } from './utils/slugger.mjs';
1615

1716
/**
@@ -20,11 +19,10 @@ import { createNodeSlugger } from './utils/slugger.mjs';
2019
const createParser = () => {
2120
// Creates an instance of the Remark processor with GFM support
2221
// which is used for stringifying the AST tree back to Markdown
23-
const remarkProcessor = remark().use(remarkGfm);
22+
const remarkProcessor = getRemark();
2423

2524
const {
2625
updateLinkReference,
27-
updateTypeToReferenceLink,
2826
updateMarkdownLink,
2927
addYAMLMetadata,
3028
addHeadingMetadata,
@@ -45,14 +43,24 @@ const createParser = () => {
4543
* Then once we have the whole file parsed, we can split the resulting string into sections
4644
* and seal the Metadata Entries (`.create()`) and return the result to the caller of parae.
4745
*
48-
* @type {Array<import('./types.d.ts').ApiDocMetadataEntry>}
46+
* @type {Array<ApiDocMetadataEntry>}
4947
*/
5048
const metadataCollection = [];
5149

5250
// We allow the API doc VFile to be a Promise of a VFile also,
5351
// hence we want to ensure that it first resolves before we pass it to the parser
5452
const resolvedApiDoc = await Promise.resolve(apiDoc);
5553

54+
// Normalizes all the types in the API doc file to be reference links
55+
// which needs to be done before the actual processing is done
56+
// since we're replacing raw text within the Markdown
57+
// @TODO: This could be moved to another place responsible for handling
58+
// text substitutions at the beginning of the parsing process (as dependencies)
59+
resolvedApiDoc.value = String(resolvedApiDoc.value).replaceAll(
60+
createQueries.QUERIES.normalizeTypes,
61+
transformTypeToReferenceLink
62+
);
63+
5664
// Creates a new Slugger instance for the current API doc file
5765
const nodeSlugger = createNodeSlugger();
5866

@@ -73,14 +81,6 @@ const createParser = () => {
7381
// anymore, since all link references got updated to be plain links
7482
remove(apiDocTree, markdownDefinitions);
7583

76-
// Handles API type references transformation into links that point
77-
// to the reference to a said API type
78-
visit(apiDocTree, createQueries.UNIST.isTextWithType, node => {
79-
updateTypeToReferenceLink(node);
80-
81-
return SKIP;
82-
});
83-
8484
// Handles normalisation URLs that reference to API doc files with .md extension
8585
// to replace the .md into .html, since the API doc files get eventually compiled as HTML
8686
visit(apiDocTree, createQueries.UNIST.isMarkdownUrl, node => {
@@ -116,7 +116,7 @@ const createParser = () => {
116116
// the document itself.
117117
const stop =
118118
headingNode === nextHeadingNode
119-
? apiDocTree.children.length - 1
119+
? apiDocTree.children.length
120120
: apiDocTree.children.indexOf(nextHeadingNode);
121121

122122
// Retrieves all the Nodes that should belong to the current API doc section
@@ -130,10 +130,10 @@ const createParser = () => {
130130
// and then apply the Stability Index Metadata to the current Metadata entry
131131
visit(apiSectionTree, createQueries.UNIST.isStabilityIndex, node => {
132132
// Retrieves the subtree of the Stability Index Node
133-
const stabilityNodes = createTree('root', node.children[0].children);
133+
const stabilityNode = createTree('root', node.children);
134134

135135
// Adds the Stability Index Metadata to the current Metadata entry
136-
addStabilityIndexMetadata(stabilityNodes, apiEntryMetadata);
136+
addStabilityIndexMetadata(stabilityNode, apiEntryMetadata);
137137

138138
return SKIP;
139139
});

0 commit comments

Comments
 (0)