-
Notifications
You must be signed in to change notification settings - Fork 9
feat: introduce based generator logic #42
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 11 commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
f1baf7d
feat: introduce based generator logic
ovflowd a0fe767
chore: actually write the file
ovflowd 9912500
chore: fixed typos
ovflowd ee1e39e
Update src/generators.mjs
ovflowd 11a3d48
chore: explicitly import type
ovflowd af26deb
chore: better types for the generators
ovflowd 0e1bbab
chore: more type optimisations
ovflowd 80be9a0
chore: minor typo
ovflowd 7b66d02
chore: even more type coersion and use plain object instead of Map
ovflowd 7e2a27b
chore: type directly on the generator
ovflowd 6d8dadd
chore: fix typo
ovflowd cce4d52
Apply suggestions from code review
ovflowd f113eaa
chore: code review
ovflowd File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
'use strict'; | ||
|
||
import availableGenerators from './generators/index.mjs'; | ||
|
||
/** | ||
* @typedef {import('./types.d.ts').ApiDocMetadataEntry} ApiDocMetadataEntry Local type alias for the API doc metadata entry | ||
* @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 | ||
* @typedef {import('./generators/types.d.ts').AvailableGenerators & AstGenerator} AllGenerators A complete set of the available generators, including the AST one | ||
* | ||
* This method creates a system that allows you to register generators | ||
* and then execute them in a specific order, keeping track of the | ||
* generation process, and handling errors that may occur from the | ||
* execution of generating content. | ||
* | ||
* When the final generator is reached, the system will return the | ||
* final generated content. | ||
* | ||
* Generators can output content that can be consumed by other generators; | ||
* Generators can also write to files. These would usually be considered | ||
* the final generators in the chain. | ||
* | ||
* @param {ApiDocMetadataEntry} input The parsed API doc metadata entries | ||
*/ | ||
const createGenerator = input => { | ||
/** | ||
* We store all the registered generators to be processed | ||
* within a Record, so we can access their results at any time whenever needed | ||
* (we store the Promises of the generator outputs) | ||
* | ||
* @type {{ [K in keyof AllGenerators]: ReturnType<AllGenerators[K]['generate']> }} | ||
*/ | ||
const cachedGenerators = { ast: Promise.resolve(input) }; | ||
|
||
/** | ||
* Runs the Generator engine with the provided top-level input and the given generator options | ||
* | ||
* @param {import('./generators/types.d.ts').GeneratorOptions} options The options for the generator runtime | ||
*/ | ||
const runGenerators = async ({ output, generators }) => { | ||
// Note that this method is blocking, and will only execute one generator per-time | ||
// but it ensures all dependencies are resolved, and that multiple bottom-level generators | ||
// can reuse the already parsed content from the top-level/dependency generators | ||
for (const generatorName of generators) { | ||
const { dependsOn, generate } = availableGenerators[generatorName]; | ||
|
||
// If the generator dependency has not yet been resolved, we resolve | ||
// the dependency first before running the current generator | ||
if (dependsOn && dependsOn in cachedGenerators === false) { | ||
await runGenerators({ output, generators: [dependsOn] }); | ||
} | ||
|
||
// Ensures that the dependency output gets resolved before we run the current | ||
// generator with its dependency output as the input | ||
const dependencyOutput = await cachedGenerators[dependsOn]; | ||
|
||
// Adds the current generator execution Promise to the cache | ||
cachedGenerators[generatorName] = generate(dependencyOutput, { output }); | ||
} | ||
|
||
// Returns the value of the last generator of the current pipeline | ||
// Note that dependencies will be awaited (as shown on line 48) | ||
return cachedGenerators[generators[generators.length - 1]]; | ||
}; | ||
|
||
return { runGenerators }; | ||
}; | ||
|
||
export default createGenerator; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
'use strict'; | ||
|
||
import jsonSimple from './json-simple/index.mjs'; | ||
import legacyHtml from './legacy-html/index.mjs'; | ||
|
||
export default { jsonSimple, legacyHtml }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
'use strict'; | ||
|
||
import { writeFile } from 'fs/promises'; | ||
import { join } from 'node:path'; | ||
|
||
/** | ||
* This generator generates a simplified JSON version of the API docs and returns it as a string | ||
* this is not meant to be used for the final API docs, but for debugging and testing purposes | ||
* | ||
* This generator is a top-level generator, and it takes the raw AST tree of the API doc files | ||
* and returns a stringified JSON version of the API docs. | ||
* | ||
* @typedef {import('../../types.d.ts').ApiDocMetadataEntry[]} Input | ||
* | ||
* @type {import('../types.d.ts').GeneratorMetadata<Input, string>} | ||
*/ | ||
export default { | ||
name: 'jsonSimple', | ||
|
||
version: '1.0.0', | ||
|
||
description: | ||
'Generates the simple JSON version of the API docs, and returns it as a string', | ||
|
||
dependsOn: 'ast', | ||
|
||
async generate(input, options) { | ||
// This simply grabs all the different files and stringifies them | ||
const stringifiedContent = JSON.stringify(input, null, 2); | ||
|
||
// Writes all the API docs stringified content into one file | ||
// Note: The full JSON generator in the future will create one JSON file per top-level API doc file | ||
await writeFile( | ||
join(options.output, 'api-docs.json'), | ||
stringifiedContent, | ||
'utf-8' | ||
); | ||
}, | ||
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
!.gitignore |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
'use strict'; | ||
|
||
/** | ||
* This generator generates the legacy HTML pages of the legacy API docs | ||
* for retro-compatibility and while we are implementing the new 'react' and 'html' generators. | ||
* | ||
* This generator is a top-level generator, and it takes the raw AST tree of the API doc files | ||
* and generates the HTML files to the specified output directory from the configuration settings | ||
* | ||
* @typedef {import('../../types.d.ts').ApiDocMetadataEntry[]} Input | ||
* | ||
* @type {import('../types.d.ts').GeneratorMetadata<Input, void>} | ||
*/ | ||
export default { | ||
name: 'legacyHtml', | ||
|
||
version: '1.0.0', | ||
|
||
description: | ||
'Generates the legacy version of the API docs in HTML, with the assets and styles included as files', | ||
|
||
dependsOn: 'ast', | ||
|
||
async generate() { | ||
throw new Error('Not yet implemented'); | ||
}, | ||
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import type availableGenerators from './index.mjs'; | ||
|
||
// All available generators as an inferable type, to allow Generator interfaces | ||
// to be type complete and runtime friendly within `runGenerators` | ||
export type AvailableGenerators = typeof availableGenerators; | ||
|
||
// This is the runtime config passed to the API doc genberators | ||
ovflowd marked this conversation as resolved.
Show resolved
Hide resolved
|
||
export interface GeneratorOptions { | ||
// The path used to output generated files, this is to be considered | ||
// the base path that any generator will use for generating files | ||
// This parameter accepts globs but when passed to generators will contain | ||
// the already resolved absolute path to the output folder | ||
output: string; | ||
// A list of generators to be used in the API doc generation process; | ||
// This is considered a "sorted" list of generators, in the sense that | ||
// if the last entry of this list contains a generated value, we will return | ||
// the value of the last generator in the list, if any. | ||
generators: (keyof AvailableGenerators)[]; | ||
} | ||
|
||
export interface GeneratorMetadata<I extends any, O extends any> { | ||
// The name of the Generator. Must match the Key in the AvailableGenerators | ||
name: keyof AvailableGenerators; | ||
|
||
version: string; | ||
|
||
description: string; | ||
|
||
/** | ||
* The immediate generator that this generator depends on. | ||
* For example, the `html` generator depends on the `react` generator. | ||
* | ||
* If a given generator has no "before" generator, it will be considered a top-level | ||
* generator, and run in parallel. | ||
* | ||
* Assume you pass to the `createGenerator`: ['json', 'html'] as the generators, | ||
* this means both the 'json' and the 'html' generators will be executed and generate their | ||
* own outputs in parallel. If the 'html' generator depends on the 'react' generator, then | ||
* the 'react' generator will be executed first, then the 'html' generator. | ||
* | ||
* But both 'json' and 'htnl' generators will be executed in parallel. | ||
ovflowd marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* | ||
* If you pass `createGenerator` with ['react', 'html'], the 'react' generator will be executed first, | ||
* as it is a top level generator and then the 'html' generator would be executed after the 'react' generator. | ||
* | ||
* The 'ast' generator is the top-level parser, and if 'ast' is passed to `dependsOn`, then the generator | ||
* will be marked as a top-level generator. | ||
*/ | ||
dependsOn: keyof AvailableGenerators | 'ast'; | ||
|
||
/** | ||
* Generators are abstract and the different generators have different sort of inputs and outputs. | ||
* For example, a MDX generator would take the raw AST and output MDX with React Components; | ||
* Whereas a JSON generator would take the raw AST and output JSON; | ||
* Then a React generator could receive either the raw AST or the MDX output and output React Components. | ||
* (depending if they support such I/O) | ||
* | ||
* Hence you can combine different generators to achieve different outputs. | ||
*/ | ||
generate: (input: I, options: Partial<GeneratorOptions>) => Promise<O>; | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.