Skip to content

Commit

Permalink
chore(compiler): jsdoc dependency generation (#4674)
Browse files Browse the repository at this point in the history
* chore(compiler): jsdoc dependency generation

this commit adds documentation by the way of jsdoc to the type
declarations and functions for generating a list of direct/indirect
dependencies a component has, as well as a list of components that are
direct/indirect dependent on a component

* review(ap): add note on entries being populated
  • Loading branch information
rwaskiewicz committed Aug 16, 2023
1 parent b217b96 commit 91bb7bc
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 4 deletions.
79 changes: 75 additions & 4 deletions src/compiler/entries/resolve-component-dependencies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,38 +2,93 @@ import { flatOne, unique } from '@utils';

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

export function resolveComponentDependencies(cmps: d.ComponentCompilerMeta[]) {
/**
* For each entry in the provided collection of compiler metadata, generate several lists:
* - dependencies that the component has (both directly and indirectly/transitively)
* - dependencies that the component has (only directly)
* - components that are dependent on a particular component (both directly and indirectly/transitively)
* - components that are dependent on a particular component (only directly)
*
* This information is stored directly on each entry in the provided collection
*
* @param cmps the compiler metadata of the components whose dependencies and dependents ought to be calculated
*/
export function resolveComponentDependencies(cmps: d.ComponentCompilerMeta[]): void {
computeDependencies(cmps);
computeDependents(cmps);
}

function computeDependencies(cmps: d.ComponentCompilerMeta[]) {
/**
* Compute the direct and transitive dependencies for each entry in the provided collection of component metadata.
*
* This function mutates each entry in the provided collection.
*
* @param cmps the metadata for the components whose dependencies ought to be calculated.
*/
function computeDependencies(cmps: d.ComponentCompilerMeta[]): void {
const visited = new Set<d.ComponentCompilerMeta>();
cmps.forEach((cmp) => {
resolveTransitiveDependencies(cmp, cmps, visited);
cmp.dependencies = unique(cmp.dependencies).sort();
});
}

function computeDependents(cmps: d.ComponentCompilerMeta[]) {
/**
* Compute the direct and transitive dependents for each entry in the provided collection of component metadata.
*
* @param cmps the component metadata whose entries will have their dependents calculated
*/
function computeDependents(cmps: d.ComponentCompilerMeta[]): void {
cmps.forEach((cmp) => {
resolveTransitiveDependents(cmp, cmps);
});
}

/**
* Calculate the direct and transitive dependencies of a particular component.
*
* For example, given a component `foo-bar` whose `render` function references another web component `baz-buzz`:
* ```tsx
* // foo-bar.ts
* render() {
* return <baz-buzz></baz-buzz>;
* }
* ```
* where `baz-buzz` references `my-component`:
* ```tsx
* // baz-buzz.ts
* render() {
* return <my-component></my-component>;
* }
* ```
* this function will return ['baz-buzz', 'my-component'] when inspecting 'foo-bar', as 'baz-buzz' is directly used by
* 'foo-bar', and 'my-component' is used by a component ('baz-buzz') that is being used by 'foo-bar'.
*
* This function mutates each entry in the provided collection.
*
* @param cmp the metadata for the component whose dependencies are being calculated
* @param cmps the metadata for all components that participate in the current build
* @param visited a collection of component metadata that has already been inspected
* @returns a list of direct and transitive dependencies for the component being inspected
*/
function resolveTransitiveDependencies(
cmp: d.ComponentCompilerMeta,
cmps: d.ComponentCompilerMeta[],
visited: Set<d.ComponentCompilerMeta>,
): string[] {
if (visited.has(cmp)) {
// we've already inspected this component, return its dependency list
return cmp.dependencies;
}
// otherwise, add the component to our collection to mark it as 'visited'
visited.add(cmp);

// create a collection of dependencies of web components that the build knows about
const dependencies = unique(cmp.potentialCmpRefs.filter((tagName) => cmps.some((c) => c.tagName === tagName)));

cmp.dependencies = cmp.directDependencies = dependencies;

// get a list of dependencies of the current component's dependencies
const transitiveDeps = flatOne(
dependencies
.map((tagName) => cmps.find((c) => c.tagName === tagName))
Expand All @@ -42,12 +97,28 @@ function resolveTransitiveDependencies(
return (cmp.dependencies = [...dependencies, ...transitiveDeps]);
}

function resolveTransitiveDependents(cmp: d.ComponentCompilerMeta, cmps: d.ComponentCompilerMeta[]) {
/**
* Generate and set the lists of components that are:
* 1. directly _and_ indirectly (transitively) dependent on the component being inspected
* 2. only directly dependent on the component being inspected
*
* This function assumes that the {@link d.ComponentCompilerMeta#dependencies} and
* {@link d.ComponentCompilerMeta#directDependencies} properties are pre-populated for `cmp` and all entries in `cmps`.
*
* This function mutates the `dependents` and `directDependents` field on the provided `cmp` argument for both lists,
* respectively.
*
* @param cmp the metadata for the component whose dependents are being calculated
* @param cmps the metadata for all components that participate in the current build
*/
function resolveTransitiveDependents(cmp: d.ComponentCompilerMeta, cmps: d.ComponentCompilerMeta[]): void {
// the dependents of a component are any other components that list it as a direct or transitive dependency
cmp.dependents = cmps
.filter((c) => c.dependencies.includes(cmp.tagName))
.map((c) => c.tagName)
.sort();

// the dependents of a component are any other components that list it as a direct dependency
cmp.directDependents = cmps
.filter((c) => c.directDependencies.includes(cmp.tagName))
.map((c) => c.tagName)
Expand Down
19 changes: 19 additions & 0 deletions src/declarations/stencil-private.ts
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,9 @@ export interface ComponentCompilerFeatures {
* - any lifecycle methods, including `render()`
*/
isPlain: boolean;
/**
* A collection of tag names of web components that a component references in its JSX/h() function
*/
potentialCmpRefs: string[];
}

Expand Down Expand Up @@ -611,9 +614,25 @@ export interface ComponentCompilerMeta extends ComponentCompilerFeatures {
styles: StyleCompiler[];
tagName: string;
internal: boolean;
/**
* A list of web component tag names that are either:
* - directly referenced in a Stencil component's JSX/h() function
* - are referenced by a web component that is directly referenced in a Stencil component's JSX/h() function
*/
dependencies?: string[];
/**
* A list of web component tag names that either:
* - directly reference the current component directly in their JSX/h() function
* - indirectly/transitively reference the current component directly in their JSX/h() function
*/
dependents?: string[];
/**
* A list of web component tag names that are directly referenced in a Stencil component's JSX/h() function
*/
directDependencies?: string[];
/**
* A list of web component tag names that the current component directly in their JSX/h() function
*/
directDependents?: string[];
}

Expand Down

0 comments on commit 91bb7bc

Please sign in to comment.