Skip to content
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

feat(compiler): customize readme mermaid diagram colors #5980

Merged
merged 8 commits into from
Sep 11, 2024
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