-
Notifications
You must be signed in to change notification settings - Fork 137
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat(automatin/api): makes obj from components * feat(automation/api): parses single line comments * feat(regex): found it * feat(regex): grouped and closed it * feat(regex): and now it respect white space * feat(automation): working regex-to-obj * feat(commentMerger): removes whitespace and * This is achieved by yet another regex™. It fully respects any intentional (or not) whitespace or line breaks after the fact. * feat(automation/regex): adds type extraction to regex * feat(automation api): fix type regex to correctly read from file * refactor(automation): splits file reading from comment regex * refactor(extractType): splits regex from file handle * refactor(automatic api): better file name and ouput * fix(automatic api): removes camel case * refactor(api automation): better fn name * fix(automatic api): prevents deleting comments * fix(automatic api): rm console log * feat(custom api): parses comments from single file * fix(custom api): type extentions correctly handles the second way a type could end * feat(auto api):group comments under type label Adds label prop to comment obj. Label prop is derived from the type that defines the comments but is not changed to match current API naming * feat(auto api): components use comment labels * feat(auto api): dynamically gets component name * feat(auto api): adds fn that returns output path * refactor(auto api): renames and shifts now the plugin is in one file, decluttering the vite.config file, and gives it a better name * refactor(custom api): removes old code new code is placed under a single file, can now be used in vite * refactor(auto api): move fns around Mostly default export being at the top. * fix(auto api): adds proper types * feat(auto-api): write api obj to file * fix(auto-api): removes semicolon from type * feat(auto-api): adds docs component * fix(auto api): removes unused import * feat(auto api): adds periods to sub component names * feat(auto api): adds fn for removing question marks * refactor(auto api): betters namings * fix(auto api): workaround error message Essentially, you can't import across package boundaries. Hacky fix is to duplicate types. * fix(auto api): removes unused import * fix(auto api): removes test code Accidentally commited code used to test the auto API.
- Loading branch information
1 parent
af6aff9
commit 27474ea
Showing
7 changed files
with
265 additions
and
8 deletions.
There are no files selected for viewing
This file contains 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,118 @@ | ||
import * as fs from 'fs'; | ||
import { resolve } from 'path'; | ||
import { inspect } from 'util'; | ||
import { ViteDevServer } from 'vite'; | ||
export default function autoAPI() { | ||
return { | ||
name: 'watch-monorepo-changes', | ||
configureServer(server: ViteDevServer) { | ||
const watchPath = resolve(__dirname, '../../packages/kit-headless'); | ||
server.watcher.on('change', (file: string) => { | ||
if (file.startsWith(watchPath)) { | ||
loopOnAllChildFiles(file); | ||
} | ||
}); | ||
}, | ||
}; | ||
} | ||
// the object should have this general structure, arranged from parent to child | ||
// componentName:[subComponent,subcomponent,...] | ||
// subComponentName:[publicType,publicType,...] | ||
// publicType:[{ comment,prop,type },{ comment,prop,type },...] | ||
// THEY UPPER-MOST KEY IS ALWAYS USED AS A HEADING | ||
export type ComponentParts = Record<string, SubComponents>; | ||
type SubComponents = SubComponent[]; | ||
export type SubComponent = Record<string, PublicType[]>; | ||
export type PublicType = Record<string, ParsedProps[]>; | ||
type ParsedProps = { | ||
comment: string; | ||
prop: string; | ||
type: string; | ||
}; | ||
function parseSingleComponentFromDir(path: string, ref: SubComponents) { | ||
const component_name = /\/([\w-]*).tsx/.exec(path); | ||
if (component_name === null || component_name[1] === null) { | ||
// may need better behavior | ||
return; | ||
} | ||
const sourceCode = fs.readFileSync(path, 'utf-8'); | ||
const comments = extractPublicTypes(sourceCode); | ||
const parsed: PublicType[] = []; | ||
for (const comment of comments) { | ||
const api = extractComments(comment.string); | ||
const pair: PublicType = { [comment.label]: api }; | ||
parsed.push(pair); | ||
} | ||
const completeSubComponent: SubComponent = { [component_name[1]]: parsed }; | ||
ref.push(completeSubComponent); | ||
return ref; | ||
} | ||
|
||
function extractPublicTypes(strg: string) { | ||
const getPublicTypes = /type Public([A-Z][\w]*)*[\w\W]*?{([\w|\W]*?)}(;| &)/gm; | ||
const cms = []; | ||
let groups; | ||
while ((groups = getPublicTypes.exec(strg)) !== null) { | ||
const string = groups[2]; | ||
cms.push({ label: groups[1], string }); | ||
} | ||
return cms; | ||
} | ||
function extractComments(strg: string): ParsedProps[] { | ||
const magical_regex = | ||
/^\s*?\/[*]{2}\n?([\w|\W|]*?)\s*[*]{1,2}[/]\n[ ]*([\w|\W]*?): ([\w|\W]*?);?$/gm; | ||
|
||
const cms = []; | ||
let groups; | ||
|
||
while ((groups = magical_regex.exec(strg)) !== null) { | ||
const trimStart = /^ *|(\* *)/g; | ||
const comment = groups[1].replaceAll(trimStart, ''); | ||
const prop = groups[2]; | ||
const type = groups[3]; | ||
cms.push({ comment, prop, type }); | ||
} | ||
return cms; | ||
} | ||
function writeToDocs(fullPath: string, componentName: string, api: ComponentParts) { | ||
if (fullPath.includes('kit-headless')) { | ||
const relDocPath = `../website/src/routes//docs/headless/${componentName}`; | ||
const fullDocPath = resolve(__dirname, relDocPath); | ||
const dirPath = fullDocPath.concat('/auto-api'); | ||
|
||
if (!fs.existsSync(dirPath)) { | ||
fs.mkdirSync(dirPath); | ||
} | ||
const json = JSON.stringify(api, null, 2); | ||
const hacky = `export const api=${json}`; | ||
|
||
try { | ||
fs.writeFileSync(dirPath.concat('/api.ts'), hacky); | ||
console.log('auto-api: succesfully genereated new json!!! :)'); | ||
} catch (err) { | ||
return; | ||
} | ||
} | ||
} | ||
function loopOnAllChildFiles(filePath: string) { | ||
const childComponentRegex = /\/([\w-]*).tsx$/.exec(filePath); | ||
if (childComponentRegex === null) { | ||
return; | ||
} | ||
const parentDir = filePath.replace(childComponentRegex[0], ''); | ||
const componentRegex = /\/(\w*)$/.exec(parentDir); | ||
if (!fs.existsSync(parentDir) || componentRegex == null) { | ||
return; | ||
} | ||
const componentName = componentRegex[1]; | ||
const allParts: SubComponents = []; | ||
const store: ComponentParts = { [componentName]: allParts }; | ||
fs.readdirSync(parentDir).forEach((fileName) => { | ||
if (/tsx$/.test(fileName)) { | ||
const fullPath = parentDir + '/' + fileName; | ||
parseSingleComponentFromDir(fullPath, store[componentName]); | ||
} | ||
}); | ||
|
||
writeToDocs(filePath, componentName, store); | ||
} |
This file contains 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 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,134 @@ | ||
import { JSXOutput, component$, $, QRL, useTask$, useSignal } from '@builder.io/qwik'; | ||
import { APITable, type APITableProps } from './api-table'; | ||
import { packages } from '../install-snippet/install-snippet'; | ||
|
||
//This is a workaround for not being able to export across packages due to nx rule: | ||
// https://nx.dev/features/enforce-module-boundaries#enforce-module-boundaries | ||
type ComponentParts = Record<string, SubComponents>; | ||
type SubComponents = SubComponent[]; | ||
type SubComponent = Record<string, PublicType[]>; | ||
type PublicType = Record<string, ParsedProps[]>; | ||
type ParsedProps = { | ||
comment: string; | ||
prop: string; | ||
type: string; | ||
}; | ||
type AutoAPIConfig = { | ||
topHeader?: QRL<(text: string) => JSXOutput>; | ||
subHeader?: QRL<(text: string) => JSXOutput>; | ||
props?: QRL<(text: string) => string>; | ||
}; | ||
|
||
type AnatomyTableProps = { | ||
api?: ComponentParts; | ||
config: AutoAPIConfig; | ||
}; | ||
|
||
type SubComponentProps = { | ||
subComponent: SubComponent; | ||
config: AutoAPIConfig; | ||
}; | ||
type ParsedCommentsProps = { | ||
parsedProps: PublicType; | ||
config: AutoAPIConfig; | ||
}; | ||
const currentHeader = $((_: string) => { | ||
//cannot send h2 from here because current TOC can only read md | ||
return null; | ||
}); | ||
|
||
const currentSubHeader = $((text: string) => { | ||
let subHeader = text.replace(/(p|P)rops/, ''); | ||
const hasCapital = /[a-z][A-Z]/.exec(subHeader)?.index; | ||
if (hasCapital != undefined) { | ||
subHeader = | ||
subHeader.slice(0, hasCapital + 1) + '.' + subHeader.slice(hasCapital + 1); | ||
} | ||
return ( | ||
<> | ||
<h3 class="mb-6 mt-8 scroll-mt-20 text-xl font-semibold">{subHeader}</h3> | ||
</> | ||
); | ||
}); | ||
|
||
const removeQuestionMarkFromProp = $((text: string) => { | ||
return text.replace('?', ''); | ||
}); | ||
const defaultConfig: AutoAPIConfig = { | ||
topHeader: currentHeader, | ||
subHeader: currentSubHeader, | ||
props: removeQuestionMarkFromProp, | ||
}; | ||
export const AutoAPI = component$<AnatomyTableProps>( | ||
({ api, config = defaultConfig }) => { | ||
if (api === undefined) { | ||
return null; | ||
} | ||
const key = Object.keys(api)[0]; | ||
const topHeaderSig = useSignal<string | JSXOutput>(key); | ||
const subComponents = api[key].filter((e) => e[Object.keys(e)[0]].length > 0); | ||
useTask$(async () => { | ||
if (config.topHeader) { | ||
topHeaderSig.value = await config.topHeader(key as string); | ||
} | ||
}); | ||
return ( | ||
<> | ||
{topHeaderSig.value} | ||
{subComponents.map((e) => ( | ||
<SubComponent subComponent={e} config={config} /> | ||
))} | ||
</> | ||
); | ||
}, | ||
); | ||
|
||
const SubComponent = component$<SubComponentProps>(({ subComponent, config }) => { | ||
const subComponentKey = Object.keys(subComponent)[0]; | ||
const comments = subComponent[subComponentKey]; | ||
return ( | ||
<> | ||
{comments.map((e) => ( | ||
<> | ||
<ParsedComments parsedProps={e} config={config} /> | ||
</> | ||
))} | ||
</> | ||
); | ||
}); | ||
|
||
const ParsedComments = component$<ParsedCommentsProps>(({ parsedProps, config }) => { | ||
const key = Object.keys(parsedProps)[0]; | ||
const subHeaderSig = useSignal<string | JSXOutput>(key); | ||
useTask$(async () => { | ||
if (config.subHeader) { | ||
subHeaderSig.value = await config.subHeader(key as string); | ||
} | ||
}); | ||
const appliedPropsSig = useSignal<null | APITableProps>(null); | ||
useTask$(async () => { | ||
const translation: APITableProps = { | ||
propDescriptors: parsedProps[key].map((e) => { | ||
return { | ||
name: e.prop, | ||
type: e.type, | ||
description: e.comment, | ||
}; | ||
}), | ||
}; | ||
if (config.props) { | ||
for (const props of translation.propDescriptors) { | ||
props.name = await config.props(props.name); | ||
} | ||
} | ||
appliedPropsSig.value = translation; | ||
}); | ||
return ( | ||
<> | ||
{subHeaderSig.value} | ||
{appliedPropsSig.value?.propDescriptors && ( | ||
<APITable propDescriptors={appliedPropsSig.value?.propDescriptors} /> | ||
)} | ||
</> | ||
); | ||
}); |
This file contains 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 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 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 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