Skip to content

Commit

Permalink
feat(compiler): copy dock block from component to generated types
Browse files Browse the repository at this point in the history
The dock block from the component itself is now copied to the generated components.d.ts file. This
makes it possible for other tools to get the documentation and use it, e.g. an IDE can display the
documentation for a component when it's being used
  • Loading branch information
jgroth committed Aug 17, 2022
1 parent f5b2b69 commit 5b67de2
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 9 deletions.
15 changes: 10 additions & 5 deletions src/compiler/types/generate-app-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { generateComponentTypes } from './generate-component-types';
import { generateEventDetailTypes } from './generate-event-detail-types';
import { GENERATED_DTS, getComponentsDtsSrcFilePath } from '../output-targets/output-utils';
import { isAbsolute, relative, resolve } from 'path';
import { normalizePath } from '@utils';
import { addDockBlock, normalizePath } from '@utils';
import { updateReferenceTypeImports } from './update-import-refs';
import { updateStencilTypesImports } from './stencil-types';

Expand Down Expand Up @@ -146,10 +146,15 @@ const generateComponentTypesFile = (config: d.Config, buildCtx: d.BuildCtx, areT
c.push(` export namespace JSX {`);
c.push(` interface IntrinsicElements {`);
c.push(
...modules.map(
(m) =>
` "${m.tagName}": LocalJSX.${m.tagNameAsPascal} & JSXBase.HTMLAttributes<${m.htmlElementName}>;`
)
...modules.map((m) => {
const docs = components.find((c) => c.tagName === m.tagName).docs;

return addDockBlock(
` "${m.tagName}": LocalJSX.${m.tagNameAsPascal} & JSXBase.HTMLAttributes<${m.htmlElementName}>;`,
docs,
12
);
})
);
c.push(` }`);
c.push(` }`);
Expand Down
12 changes: 8 additions & 4 deletions src/compiler/types/generate-component-types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type * as d from '../../declarations';
import { dashToPascalCase, sortBy } from '@utils';
import { addDockBlock, dashToPascalCase, sortBy } from '@utils';
import { generateEventTypes } from './generate-event-types';
import { generateMethodTypes } from './generate-method-types';
import { generatePropTypes } from './generate-prop-types';
Expand Down Expand Up @@ -33,7 +33,11 @@ export const generateComponentTypes = (
const jsxAttributes = attributesToMultiLineString([...propAttributes, ...eventAttributes], true, areTypesInternal);

const element = [
` interface ${htmlElementName} extends Components.${tagNameAsPascal}, HTMLStencilElement {`,
addDockBlock(
` interface ${htmlElementName} extends Components.${tagNameAsPascal}, HTMLStencilElement {`,
cmp.docs,
4
),
` }`,
` var ${htmlElementName}: {`,
` prototype: ${htmlElementName};`,
Expand All @@ -45,8 +49,8 @@ export const generateComponentTypes = (
tagName,
tagNameAsPascal,
htmlElementName,
component: ` interface ${tagNameAsPascal} {\n${componentAttributes} }`,
jsx: ` interface ${tagNameAsPascal} {\n${jsxAttributes} }`,
component: addDockBlock(` interface ${tagNameAsPascal} {\n${componentAttributes} }`, cmp.docs, 4),
jsx: addDockBlock(` interface ${tagNameAsPascal} {\n${jsxAttributes} }`, cmp.docs, 2),
element: element.join(`\n`),
};
};
Expand Down
42 changes: 42 additions & 0 deletions src/utils/test/util.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,4 +199,46 @@ describe('util', () => {
});
});
});

describe('addDockBlock', () => {
let str: string;
let docs: d.CompilerJsDoc;

beforeEach(() => {
str = 'interface Foo extends Components.Foo, HTMLStencilElement {';
docs = {
tags: [{ name: 'deprecated', text: 'only for testing' }],
text: 'Lorem ipsum',
};
});

it('adds a doc block to the string', () => {
expect(util.addDockBlock(str, docs)).toEqual(`/**
* Lorem ipsum
* @deprecated only for testing
*/
interface Foo extends Components.Foo, HTMLStencilElement {`);
});

it('indents the doc block correctly', () => {
str = ' ' + str;
expect(util.addDockBlock(str, docs, 4)).toEqual(` /**
* Lorem ipsum
* @deprecated only for testing
*/
interface Foo extends Components.Foo, HTMLStencilElement {`);
});

it('excludes the @internal tag', () => {
docs.tags.push({ name: 'internal' });
expect(util.addDockBlock(str, docs).includes('@internal')).toBeFalsy();
});

it.each([[null], [undefined], [{ tags: [], text: '' }]])(
'does not add a dock block when docs are empty (%j)',
(docs) => {
expect(util.addDockBlock(str, docs)).toEqual(str);
}
);
});
});
35 changes: 35 additions & 0 deletions src/utils/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,41 @@ ${docs.tags
.join('\n')}`.trim();
}

/**
* Adds a doc block to a string
* @param str the string to add a doc block to
* @param docs the compiled JS docs
* @param indentation number of spaces to indent the block with
* @returns the doc block
*/
export function addDockBlock(str: string, docs?: d.CompilerJsDoc, indentation: number = 0): string {
if (!docs) {
return str;
}

return [getDocBlock(docs, indentation), str].filter(Boolean).join(`\n`);
}

function getDocBlock(docs: d.CompilerJsDoc, indentation: number = 0): string {
const textDocs = getDocBlockLines(docs);
if (!textDocs.filter(Boolean).length) {
return '';
}

const spaces = new Array(indentation + 1).join(' ');

return [spaces + '/**', ...textDocs.map((line) => spaces + ` * ${line}`), spaces + ' */'].join(`\n`);
}

function getDocBlockLines(docs: d.CompilerJsDoc | undefined | null): string[] {
return [
...docs.text.split(lineBreakRegex),
...docs.tags
.filter((tag) => tag.name !== 'internal')
.map((tag) => `@${tag.name} ${tag.text || ''}`.split(lineBreakRegex)),
].flat();
}

/**
* Retrieve a project's dependencies from the current build context
* @param buildCtx the current build context to query for a specific package
Expand Down

0 comments on commit 5b67de2

Please sign in to comment.