Lumaktaw sa nilalaman

CEM Analyzer Plugin

Ang CEM (custom-elements.json) ay isang standard na paglalarawan ng mga element na tinutukoy ng isang package (ang kanilang mga tag, attribute, property, event, at slot) para mabasa ng tooling ang iyong mga component nang hindi ito pinapatakbo. Magbasa pa sa custom-elements-manifest.open-wc.org para sa analyzer at plugin API nito, o ang schema at specification para sa file format mismo.

Ang CEM Analyzer (@custom-elements-manifest/analyzer), bilang default, ay babasahin ang static props bilang isang static field lamang at walang ilalabas na attribute para dito. Dahil dito, walang mababasa ang tooling tulad ng Storybook at ang autocomplete ng code editor.

Sa paggamit ng aming web-component-base/cem-plugin, palalawakin ng CEM Analyzer ang bawat prop tungo sa isang typed manifest attribute at katugmang public field. Gagabayan ka ng mga hakbang sa ibaba sa pag-install ng CEM Analyzer at pag-configure nito para makapagtrabaho ang tooling kasama ng iyong wcb custom elements.

Terminal window
npm i -D @custom-elements-manifest/analyzer
custom-elements-manifest.config.mjs
import { wcbStaticProps } from 'web-component-base/cem-plugin'
export default {
globs: ['src/**/*.{js,ts}'],
outdir: '.',
plugins: [wcbStaticProps()],
}

Pagkatapos, patakbuhin ang analyzer:

Terminal window
npx cem analyze

Kung bibigyan ng component:

src/cozy-button.ts
import { WebComponent, html } from 'web-component-base'
type CozyButtonProps = {
variant: 'primary' | 'ghost'
disabled: boolean
maxCount: number
}
export class CozyButton extends WebComponent<CozyButtonProps> {
static props: CozyButtonProps = {
variant: 'primary',
disabled: false,
maxCount: 3,
}
static shadowRootInit = { mode: 'open' }
static styles = ':host { display: inline-block }'
get template() {
return html`<button>${this.props.variant}</button>`
}
}
customElements.define('cozy-button', CozyButton)

Nakakakuha ang custom-elements.json ng typed attribute at katugmang public field kada prop:

attributetypefielddefault
variantstringvariant'primary'
disabledbooleandisabledfalse
max-countnumbermaxCount3

…at ang props, shadowRootInit, styles, strictProps, observedAttributes at template ay tinatanggal mula sa public surface.

May dalawang detalye na dapat malaman:

  • Ang mga type ay nagmumula sa default literal: true/falseboolean, numeric → number, object/array → object, ang lahat ng iba pa → string. Hindi kinokonsulta ang TypeScript annotation, kaya ang union ni variant ay bumabagsak pa rin sa manifest bilang string.
  • Ang mga attribute name ay nagmumula sa getKebabCase mismo ng wcb, ang parehong function na ginagamit ng observedAttributes, kaya hindi maaaring mailihis ang mga manifest name mula sa aktwal na obserbado ng component.

Ang web-components renderer ng Storybook ay bumubuo ng autodocs at controls mula sa Custom Elements Manifest.

.storybook/preview.js
import { setCustomElementsManifest } from '@storybook/web-components-vite'
import manifest from '../custom-elements.json'
setCustomElementsManifest(manifest)
export default { tags: ['autodocs'] }

I-bind ang isang story sa tag name at ide-derive na ng Storybook ang natitira, na nagbibigay ng text field para sa variant, isang toggle para sa disabled, isang number input para sa maxCount:

cozy-button.stories.js
import { html } from 'lit'
import '../src/cozy-button.ts'
export default {
title: 'Cozy/Button',
component: 'cozy-button', // ← no argTypes needed
render: ({ variant, disabled, maxCount }) => html`
<cozy-button
variant=${variant}
?disabled=${disabled}
max-count=${maxCount}
></cozy-button>
`,
}
export const Default = {
args: { variant: 'primary', disabled: false, maxCount: 3 },
}

Para sa kumpletong gumaganang setup, tingnan ang storybook/ sa wcb repo, na nagpapatakbo ng configuration na ito laban sa mga demo component.

Kapag umiiral na ang custom-elements.json, maaaring mag-alok ang mga editor ng tag-name at attribute autocomplete para sa iyong mga component, na hinihimok ng parehong static props na binabasa ng plugin, kaya hindi maililihis ang mga hint mula sa code.

Una, i-declare ang manifest file sa iyong package.json. Natutuklasan ito ng language server ng Route 2 sa pamamagitan ng field na ito, at ito ang ecosystem convention na hahanapin ng ibang manifest-driven na tooling:

{
"customElements": "custom-elements.json"
}

Binabasa ng built-in na HTML language service ng VS Code ang sariling custom data format nito. Kino-convert ito ng ikalawang analyzer plugin, kaya lumalabas ang parehong file mula sa iisang cem analyze run:

Terminal window
npm i -D cem-plugin-vs-code-custom-data-generator
custom-elements-manifest.config.mjs
import { wcbStaticProps } from 'web-component-base/cem-plugin'
import { generateCustomData } from 'cem-plugin-vs-code-custom-data-generator'
export default {
globs: ['src/**/*.{js,ts}'],
outdir: '.',
plugins: [wcbStaticProps(), generateCustomData()],
}
.vscode/settings.json
{
"html.customData": ["./vscode.html-custom-data.json"]
}

I-restart ang VS Code at maku-complete ang <cozy- sa mga HTML file, na inaalok ang variant / disabled / max-count bilang attributes at ang kanilang mga type at defaults sa hover.

Ang bitag: Ang html.customData ay nag-a-apply lamang sa mga .html file. Ang mga wcb component ay nag-a-author ng markup nila sa html tagged templates sa loob ng .js / .ts, at ang mga iyon ay hindi dumadaan sa HTML language service. Nakakatulong ang rutang ito sa sinumang nagsusulat ng plain HTML pages laban sa iyong mga component. Hindi ito magsisindi sa loob ng sarili mong mga template.

Route 2: isang language server extension, para sa tagged templates

Seksyong pinamagatang “Route 2: isang language server extension, para sa tagged templates”

Para makuha ang parehong completions sa loob ng tagged templates, kailangan mo ng extension na nakakaunawa sa mga ito. Ang pinaka-kasalukuyang opsyon ay ang Custom Elements Manifest Language Server (pwrs.cem-language-server-vscode). Ini-autocomplete nito ang mga tag name at attributes sa loob ng template literals kapwa sa JS at TS, nagdaragdag ng hover documentation para sa attributes at defaults, at natutuklasan ang manifest sa pamamagitan ng customElements field sa itaas, walang kailangang .vscode/settings.json.

Dalawang alternatibo, parehong sulit malaman ang kalagayan:

  • wc-toolkit/wc-language-server: VS Code at JetBrains, manifest-driven din. Sinasabi sa sarili nitong alpha at experimental.
  • Matsuuu.custom-elements-language-server-project: ang isa na madalas mong matatagpuan sa mas lumang mga write-up. Alpha ito, at na-archive ang repository nito noong Enero 2026, kaya mas mainam ang isa sa dalawa sa itaas.

Ipadala ang manifest kasama ng isang package: distPaths()

Seksyong pinamagatang “Ipadala ang manifest kasama ng isang package: distPaths()”

Nagpu-publish ba ng iyong mga component sa npm? Nire-stamp ng analyzer ang path ng bawat module gamit ang eksaktong file na na-scan nito — kaya sa globs: ['src/**/*.ts'], ang bawat path sa custom-elements.json ay isang .ts na source file. Karamihan sa mga package ay nagpu-publish lamang ng kanilang naka-build na dist/ output (at hindi rin naman puwedeng import ang .ts sa runtime), kaya ang isang consumer na bumabasa ng manifest na iyon ay nire-resolve ang mga path na wala sa tarball.

Idagdag ang distPaths() pagkatapos ng wcbStaticProps() para muling isulat ang mga path na iyon papunta sa built output. Sa zero-config, mine-map nito ang src/dist/ at ang mga extension ng TypeScript tungo sa kanilang emitted JS form (.ts.js, .mts.mjs, .cts.cjs). Dumadaan ang ibang extension, kaya ang isang .js na source ay ang directory lang nito ang napapalitan:

custom-elements-manifest.config.mjs
import { wcbStaticProps, distPaths } from 'web-component-base/cem-plugin'
export default {
globs: ['src/**/*.ts'],
outdir: '.',
plugins: [wcbStaticProps(), distPaths()],
}

Para sa ibang layout, i-override ang rootDir / outDir: distPaths({ rootDir: 'lib', outDir: 'dist/esm' }). Patakbuhin ang cem analyze pagkatapos ng iyong build para umiral ang mga dist/ file na itinuturo nito. Tingnan ang API reference para sa ext extension remap. Ang hindi pagpu-publish ng manifest — hal., isang lokal na Storybook o editor setup sa ibabaw ng sarili mong source — ay hindi nangangailangan ng kahit ano rito.