Skip to content

Commit

Permalink
feat(compiler): customize readme mermaid diagram colors (#5980)
Browse files Browse the repository at this point in the history
* feat(compiler): add docs option to the config

Apply colors from the `docs.markdown.targetComponent` to
the my-component node in the mermaid diagram.

fixes: #2876

* feat(compiler): add docs option to the config

Apply colors from the `docs.markdown.targetComponent` to
the my-component node in the mermaid diagram.

fixes: #2876

* format(): prettier

* refactor: refactor after code review, add tests for depsToMarkdown func

* feat: move validation logic for config.docs to validateConfig

* fix: deps

* fix(compiler): build error & logger message

---------

Co-authored-by: Tanner Reits <[email protected]>
  • Loading branch information
Tardigrada777 and tanner-reits committed Sep 11, 2024
1 parent 2569abd commit 9ca8951
Show file tree
Hide file tree
Showing 11 changed files with 1,964 additions and 73 deletions.
1,711 changes: 1,643 additions & 68 deletions package-lock.json

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions src/compiler/config/constants.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
import type * as d from '../../declarations';

type DefaultTargetComponentConfig = d.Config['docs']['markdown']['targetComponent'];

export const DEFAULT_DEV_MODE = false;
export const DEFAULT_HASHED_FILENAME_LENGTH = 8;
export const MIN_HASHED_FILENAME_LENGTH = 4;
export const MAX_HASHED_FILENAME_LENGTH = 32;
export const DEFAULT_NAMESPACE = 'App';
export const DEFAULT_TARGET_COMPONENT_STYLES: DefaultTargetComponentConfig = {
background: '#f9f',
textColor: '#333',
};
34 changes: 34 additions & 0 deletions src/compiler/config/test/validate-docs.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type * as d from '@stencil/core/declarations';
import { mockConfig, mockLoadConfigInit } from '@stencil/core/testing';

import { DEFAULT_TARGET_COMPONENT_STYLES } from '../constants';
import { validateConfig } from '../validate-config';

describe('validateDocs', () => {
Expand Down Expand Up @@ -35,4 +36,37 @@ describe('validateDocs', () => {
const { config } = validateConfig(userConfig, mockLoadConfigInit());
expect(config.outputTargets.some((o) => o.type === 'docs-readme')).toBe(false);
});

it('should use default values for docs.markdown.targetComponent', () => {
const { config } = validateConfig(userConfig, mockLoadConfigInit());
expect(config.docs.markdown.targetComponent.background).toBe(DEFAULT_TARGET_COMPONENT_STYLES.background);
});

it('should use user values for docs.markdown.targetComponent.background', () => {
userConfig = mockConfig({
docs: {
markdown: {
targetComponent: {
background: '#123',
},
},
},
});
const { config } = validateConfig(userConfig, mockLoadConfigInit());
expect(config.docs.markdown.targetComponent.background).toBe(userConfig.docs.markdown.targetComponent.background);
});

it('should use user values for docs.markdown.targetComponent.textColor', () => {
userConfig = mockConfig({
docs: {
markdown: {
targetComponent: {
textColor: '#123',
},
},
},
});
const { config } = validateConfig(userConfig, mockLoadConfigInit());
expect(config.docs.markdown.targetComponent.textColor).toBe(userConfig.docs.markdown.targetComponent.textColor);
});
});
2 changes: 2 additions & 0 deletions src/compiler/config/validate-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
} from './constants';
import { validateOutputTargets } from './outputs';
import { validateDevServer } from './validate-dev-server';
import { validateDocs } from './validate-docs';
import { validateHydrated } from './validate-hydrated';
import { validateDistNamespace } from './validate-namespace';
import { validateNamespace } from './validate-namespace';
Expand Down Expand Up @@ -136,6 +137,7 @@ export const validateConfig = (
rollupConfig: validateRollupConfig(config),
sys: config.sys ?? bootstrapConfig.sys ?? createNodeSys({ logger }),
testing: config.testing ?? {},
docs: validateDocs(config, logger),
transformAliasedImportPaths: isBoolean(userConfig.transformAliasedImportPaths)
? userConfig.transformAliasedImportPaths
: true,
Expand Down
42 changes: 42 additions & 0 deletions src/compiler/config/validate-docs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import * as d from '../../declarations';
import { UnvalidatedConfig } from '../../declarations';
import { isHexColor } from '../docs/readme/docs-util';
import { DEFAULT_TARGET_COMPONENT_STYLES } from './constants';

/**
* Validate the `.docs` property on the supplied config object and
* return a properly-validated value.
*
* @param config the configuration we're examining
* @param logger the logger that will be set on the config
* @returns a suitable/default value for the docs property
*/
export const validateDocs = (config: UnvalidatedConfig, logger: d.Logger): d.ValidatedConfig['docs'] => {
const { background: defaultBackground, textColor: defaultTextColor } = DEFAULT_TARGET_COMPONENT_STYLES;

let { background = defaultBackground, textColor = defaultTextColor } =
config.docs?.markdown?.targetComponent ?? DEFAULT_TARGET_COMPONENT_STYLES;

if (!isHexColor(background)) {
logger.warn(
`'${background}' is not a valid hex color. The default value for diagram backgrounds ('${defaultBackground}') will be used.`,
);
background = defaultBackground;
}

if (!isHexColor(textColor)) {
logger.warn(
`'${textColor}' is not a valid hex color. The default value for diagram text ('${defaultTextColor}') will be used.`,
);
textColor = defaultTextColor;
}

return {
markdown: {
targetComponent: {
background,
textColor,
},
},
};
};
17 changes: 17 additions & 0 deletions src/compiler/docs/readme/docs-util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,23 @@ const normalizeColumnWidth = (rows: RowData[]) => {
}
};

/**
* Checks if a given string is a valid hexadecimal color representation.
*
* @param str - The string to be checked.
* @returns `true` if the string is a valid hex color (e.g., '#FF00AA', '#f0f'), `false` otherwise.
*
* @example
* isHexColor('#FF00AA'); // true
* isHexColor('#f0f'); // true
* isHexColor('#abcde'); // false (too many characters)
* isHexColor('FF00AA'); // false (missing #)
*/
export const isHexColor = (str: string): boolean => {
const hexColorRegex = /^#([0-9A-Fa-f]{3}){1,2}$/;
return hexColorRegex.test(str);
};

interface ColumnData {
text: string;
width: number;
Expand Down
9 changes: 7 additions & 2 deletions src/compiler/docs/readme/markdown-dependencies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ import { normalizePath, relative } from '@utils';

import type * as d from '../../../declarations';

export const depsToMarkdown = (cmp: d.JsonDocsComponent, cmps: d.JsonDocsComponent[]) => {
export const depsToMarkdown = (cmp: d.JsonDocsComponent, cmps: d.JsonDocsComponent[], config: d.ValidatedConfig) => {
const content: string[] = [];

const deps = Object.entries(cmp.dependencyGraph);

if (deps.length === 0) {
return content;
}
Expand All @@ -20,6 +22,7 @@ export const depsToMarkdown = (cmp: d.JsonDocsComponent, cmps: d.JsonDocsCompone
content.push(...usedBy);
content.push(``);
}

if (cmp.dependencies.length > 0) {
const dependsOn = cmp.dependencies.map((tag) => '- ' + getCmpLink(cmp, tag, cmps));

Expand All @@ -38,7 +41,9 @@ export const depsToMarkdown = (cmp: d.JsonDocsComponent, cmps: d.JsonDocsCompone
});
});

content.push(` style ${cmp.tag} fill:#f9f,stroke:#333,stroke-width:4px`);
const { background, textColor } = config.docs.markdown.targetComponent;

content.push(` style ${cmp.tag} fill:${background},stroke:${textColor},stroke-width:4px`);

content.push('```');

Expand Down
6 changes: 4 additions & 2 deletions src/compiler/docs/readme/output-docs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export const generateReadme = async (
await getUserReadmeContent(compilerCtx, readmeOutputPath)
: userContent;

const readmeContent = generateMarkdown(currentReadmeContent, docsData, cmps, readmeOutput);
const readmeContent = generateMarkdown(currentReadmeContent, docsData, cmps, readmeOutput, config);

const results = await compilerCtx.fs.writeFile(readmeOutputPath, readmeContent);
if (results.changedContent) {
Expand All @@ -74,9 +74,11 @@ export const generateMarkdown = (
cmp: d.JsonDocsComponent,
cmps: d.JsonDocsComponent[],
readmeOutput: d.OutputTargetDocsReadme,
config?: d.ValidatedConfig,
) => {
//If the readmeOutput.dependencies is true or undefined the dependencies will be generated.
const dependencies = readmeOutput.dependencies !== false ? depsToMarkdown(cmp, cmps) : [];
const dependencies = readmeOutput.dependencies !== false ? depsToMarkdown(cmp, cmps, config) : [];

return [
userContent || '',
AUTO_GENERATE_COMMENT,
Expand Down
27 changes: 26 additions & 1 deletion src/compiler/docs/test/docs-util.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { MarkdownTable } from '../../docs/readme/docs-util';
import { isHexColor, MarkdownTable } from '../../docs/readme/docs-util';

describe('markdown-table', () => {
it('header', () => {
Expand Down Expand Up @@ -46,3 +46,28 @@ describe('markdown-table', () => {
expect(o).toEqual([]);
});
});

describe('isHexColor', () => {
it('should return true for valid hex colors', () => {
expect(isHexColor('#FFF')).toBe(true);
expect(isHexColor('#FFFFFF')).toBe(true);
expect(isHexColor('#000000')).toBe(true);
expect(isHexColor('#f0f0f0')).toBe(true);
expect(isHexColor('#aBcDeF')).toBe(true);
});

it('should return false for invalid hex colors', () => {
expect(isHexColor('FFF')).toBe(false);
expect(isHexColor('#GGGGGG')).toBe(false);
expect(isHexColor('#FF')).toBe(false);
expect(isHexColor('#FFFFFFF')).toBe(false);
expect(isHexColor('#FF0000FF')).toBe(false);
});

it('should return false for non-string inputs', () => {
expect(isHexColor('123')).toBe(false);
expect(isHexColor('true')).toBe(false);
expect(isHexColor('{}')).toBe(false);
expect(isHexColor('[]')).toBe(false);
});
});
150 changes: 150 additions & 0 deletions src/compiler/docs/test/markdown-dependencies.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import type * as d from '../../../declarations';
import { DEFAULT_TARGET_COMPONENT_STYLES } from '../../config/constants';
import { depsToMarkdown } from '../readme/markdown-dependencies';

describe('depsToMarkdown()', () => {
it('should use default settings if docs.markdown configuration was not provided', () => {
const mockConfig = {
docs: {
markdown: {
targetComponent: {
...DEFAULT_TARGET_COMPONENT_STYLES,
},
},
},
} as d.ValidatedConfig;
const md = depsToMarkdown(
{
dependencies: [],
dependencyGraph: {
's-test': ['s-test-dep1'],
},
dependents: [],
docs: '',
docsTags: [],
encapsulation: undefined,
events: [],
listeners: [],
methods: [],
parts: [],
props: [],
readme: '',
slots: [],
styles: [],
tag: '',
usage: undefined,
},
[],
mockConfig,
);
expect(md).toEqual([
'## Dependencies',
'',
'### Graph',
'```mermaid',
'graph TD;',
' s-test --> s-test-dep1',
` style fill:${DEFAULT_TARGET_COMPONENT_STYLES.background},stroke:${DEFAULT_TARGET_COMPONENT_STYLES.textColor},stroke-width:4px`,
'```',
'',
]);
});

it('should use provided background settings for generated dependencies graph', () => {
const mockColor = '#445334';
const mockConfig = {
docs: {
markdown: {
targetComponent: {
background: mockColor,
textColor: DEFAULT_TARGET_COMPONENT_STYLES.textColor,
},
},
},
} as d.ValidatedConfig;
const md = depsToMarkdown(
{
dependencies: [],
dependencyGraph: {
's-test': ['s-test-dep1'],
},
dependents: [],
docs: '',
docsTags: [],
encapsulation: undefined,
events: [],
listeners: [],
methods: [],
parts: [],
props: [],
readme: '',
slots: [],
styles: [],
tag: '',
usage: undefined,
},
[],
mockConfig,
);
expect(md).toEqual([
'## Dependencies',
'',
'### Graph',
'```mermaid',
'graph TD;',
' s-test --> s-test-dep1',
` style fill:${mockColor},stroke:${DEFAULT_TARGET_COMPONENT_STYLES.textColor},stroke-width:4px`,
'```',
'',
]);
});

it('should use provided text color settings for generated dependencies graph', () => {
const mockColor = '#445334';
const mockConfig = {
docs: {
markdown: {
targetComponent: {
background: DEFAULT_TARGET_COMPONENT_STYLES.background,
textColor: mockColor,
},
},
},
} as d.ValidatedConfig;
const md = depsToMarkdown(
{
dependencies: [],
dependencyGraph: {
's-test': ['s-test-dep1'],
},
dependents: [],
docs: '',
docsTags: [],
encapsulation: undefined,
events: [],
listeners: [],
methods: [],
parts: [],
props: [],
readme: '',
slots: [],
styles: [],
tag: '',
usage: undefined,
},
[],
mockConfig,
);
expect(md).toEqual([
'## Dependencies',
'',
'### Graph',
'```mermaid',
'graph TD;',
' s-test --> s-test-dep1',
` style fill:${DEFAULT_TARGET_COMPONENT_STYLES.background},stroke:${mockColor},stroke-width:4px`,
'```',
'',
]);
});
});
Loading

0 comments on commit 9ca8951

Please sign in to comment.