provideMarkdown
The provider and the three registration helpers exported alongside it.
provideMarkdown
function provideMarkdown(config?: MarkdownConfig): EnvironmentProvidersConfigures shikidown for the application. Returns EnvironmentProviders, so it belongs in the
providers array of ApplicationConfig (or of a route, if you want a per-feature configuration).
import { provideMarkdown } from 'shikidown';
export const appConfig: ApplicationConfig = {
providers: [provideMarkdown({ theme: 'github-dark' })],
};Every option is described in Configuration. Calling it is optional: the
configuration token is injected with { optional: true } throughout, so the library falls back to
its defaults when the provider is absent.
Beyond storing the configuration, provideMarkdown() schedules an ENVIRONMENT_INITIALIZER that:
- returns immediately when not running in a browser — no Custom Element is ever defined during SSR;
- registers every entry of
componentsthroughregisterAsCustomElement; - registers every entry of
componentModulesthroughregisterComponentModules, without a filter.
Step 3 has no document to inspect, so it registers every component the modules export. The
[componentModules] input on MarkdownComponent does filter —
see Only what the page uses.
The module loaders are not awaited: a lazy module resolves after bootstrap, and registering late is still valid because custom element upgrades are retroactive.
registerAsCustomElement
function registerAsCustomElement(
selector: string,
componentClass: Type<unknown>,
injector: Injector,
): voidWraps an Angular component with createCustomElement() and defines it under selector.
Idempotent — returns immediately if the selector is already defined, so calling it from several places is safe.
import { registerAsCustomElement } from 'shikidown';
registerAsCustomElement('my-widget', WidgetComponent, inject(Injector));The injector you pass becomes the component's environment injector for the rest of the session — there is no way to undefine a custom element. Pass one whose lifetime is at least as long as the page.
registerComponentModules
function registerComponentModules(
sources: ComponentModuleSource[],
injector: Injector,
isUsed?: (selector: string) => boolean,
): Promise<void>Registers every component exported by the given modules, reading each selector from its
@Component decorator. Nothing to declare on the calling side.
A source may be a module object or a () => import('…') loader; loaders are resolved here, which
is what keeps lazily-registered components out of the initial bundle.
An export is registered only if all of the following hold:
- it is a function (this filters out constants, mock data and
undefined); reflectComponentType()recognises it as a component;- its selector is a valid custom element name —
/^[a-z][a-z0-9.\-_]*-[a-z0-9.\-_]*$/, i.e. at least one hyphen and a lowercase first character. This excludes attribute selectors such as[mermaid], multi-selectors such asa, b, and single-word selectors; isUsedreturnstrue, when supplied.
The function is idempotent and a no-op when customElements is undefined, which makes it safe to
call during SSR.
import { registerComponentModules, selectorUsedIn } from 'shikidown';
await registerComponentModules(
[() => import('./demos/button.demos')],
injector,
(selector) => selectorUsedIn(markdown, selector),
);selectorUsedIn
function selectorUsedIn(content: string, selector: string): booleanTrue when content contains an opening tag for selector.
The test runs on the Markdown source, where components appear literally as <demo-foo> or
<demo-foo />. The character following the selector must delimit the tag — >, /, whitespace, or
end of string — so <demo-foo> does not match demo-foo-bar.
selectorUsedIn('<demo-foo />', 'demo-foo'); // true
selectorUsedIn('<demo-foo-bar />', 'demo-foo'); // falseThis is the predicate MarkdownComponent passes to registerComponentModules() for its
[componentModules] input. It is exported so you can apply the same rule elsewhere.