├── .gitignore
├── .husky
├── .gitignore
└── pre-commit
├── .prettierignore
├── .prettierrc
├── .storybook
├── code-bubble-setup.js
├── main.ts
├── preview.ts
└── styles.css
├── .vscode
└── extensions.json
├── LICENSE
├── README.md
├── custom-elements-manifest.config.js
├── eslint.config.js
├── package.json
├── plop-templates
├── component.definition.ts.hbs
├── component.docs.hbs
├── component.stories.ts.hbs
├── component.styles.ts.hbs
├── component.test.ts.hbs
└── component.ts.hbs
├── plopfile.js
├── rollup.config.js
├── src
└── components
│ └── button
│ ├── button.mdx
│ ├── button.stories.ts
│ ├── button.styles.ts
│ ├── button.test.ts
│ ├── button.ts
│ └── index.ts
├── tsconfig.json
└── web-test-runner.config.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 | .pnpm-debug.log*
9 |
10 | # Diagnostic reports (https://nodejs.org/api/report.html)
11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
12 |
13 | # Runtime data
14 | pids
15 | *.pid
16 | *.seed
17 | *.pid.lock
18 |
19 | # Directory for instrumented libs generated by jscoverage/JSCover
20 | lib-cov
21 |
22 | # Coverage directory used by tools like istanbul
23 | coverage
24 | *.lcov
25 |
26 | # nyc test coverage
27 | .nyc_output
28 |
29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
30 | .grunt
31 |
32 | # Bower dependency directory (https://bower.io/)
33 | bower_components
34 |
35 | # node-waf configuration
36 | .lock-wscript
37 |
38 | # Compiled binary addons (https://nodejs.org/api/addons.html)
39 | build/Release
40 |
41 | # Dependency directories
42 | node_modules/
43 | jspm_packages/
44 |
45 | # Snowpack dependency directory (https://snowpack.dev/)
46 | web_modules/
47 |
48 | # TypeScript cache
49 | *.tsbuildinfo
50 |
51 | # Optional npm cache directory
52 | .npm
53 |
54 | # Optional eslint cache
55 | .eslintcache
56 |
57 | # Optional stylelint cache
58 | .stylelintcache
59 |
60 | # Microbundle cache
61 | .rpt2_cache/
62 | .rts2_cache_cjs/
63 | .rts2_cache_es/
64 | .rts2_cache_umd/
65 |
66 | # Optional REPL history
67 | .node_repl_history
68 |
69 | # Output of 'npm pack'
70 | *.tgz
71 |
72 | # Yarn Integrity file
73 | .yarn-integrity
74 |
75 | # dotenv environment variable files
76 | .env
77 | .env.development.local
78 | .env.test.local
79 | .env.production.local
80 | .env.local
81 |
82 | # parcel-bundler cache (https://parceljs.org/)
83 | .cache
84 | .parcel-cache
85 |
86 | # Next.js build output
87 | .next
88 | out
89 |
90 | # Nuxt.js build / generate output
91 | .nuxt
92 | dist
93 |
94 | # Gatsby files
95 | .cache/
96 | # Comment in the public line in if your project uses Gatsby and not Next.js
97 | # https://nextjs.org/blog/next-9-1#public-directory-support
98 | # public
99 |
100 | # vuepress build output
101 | .vuepress/dist
102 |
103 | # vuepress v2.x temp and cache directory
104 | .temp
105 | .cache
106 |
107 | # Docusaurus cache and generated files
108 | .docusaurus
109 |
110 | # Serverless directories
111 | .serverless/
112 |
113 | # FuseBox cache
114 | .fusebox/
115 |
116 | # DynamoDB Local files
117 | .dynamodb/
118 |
119 | # TernJS port file
120 | .tern-port
121 |
122 | # Stores VSCode versions used for testing VSCode extensions
123 | .vscode-test
124 |
125 | # yarn v2
126 | .yarn/cache
127 | .yarn/unplugged
128 | .yarn/build-state.yml
129 | .yarn/install-state.gz
130 | .pnp.*
131 |
132 | # Custom
133 | /cdn
134 | /eslint
135 | /public
136 | /react
137 | /types
138 | custom-elements.json
139 | vscode.css-custom-data.json
140 | vscode.html-custom-data.json
141 | web-types.json
--------------------------------------------------------------------------------
/.husky/.gitignore:
--------------------------------------------------------------------------------
1 | _
2 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | . "$(dirname "$0")/_/husky.sh"
3 |
4 | npx lint-staged
5 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | .gitignore
2 | cdn
3 | dist
4 | eslint
5 | plop-templates
6 | public
7 | react
8 | types
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "arrowParens": "avoid"
4 | }
5 |
--------------------------------------------------------------------------------
/.storybook/code-bubble-setup.js:
--------------------------------------------------------------------------------
1 | import { CodeBubble } from 'code-bubble';
2 | import packageJson from '../package.json';
3 |
4 | // This script sets up the CodeBubble configuration for live code examples in Storybook. It fetches the necessary library data and configures the sandbox environment for both HTML and React examples.
5 | // https://www.npmjs.com/package/code-bubble
6 | (async () => {
7 | async function fetchLibData(url) {
8 | const baseUrl = window.location.href.includes('localhost')
9 | ? ''
10 | : window.location.href.split('/iframe')[0];
11 |
12 | try {
13 | const response = await fetch(baseUrl + url); // Make the request
14 | return await response.text(); // Extract JSON data
15 | } catch (error) {
16 | console.error('Error fetching data:', url, error);
17 | return null; // Handle the error gracefully
18 | }
19 | }
20 |
21 | /** @type { import('code-bubble').CodeBubbleConfig } */
22 | new CodeBubble({
23 | sandbox: 'stackblitz',
24 | component: {
25 | frameworkButtons: {
26 | label: framework =>
27 | ({
28 | html: 'HTML',
29 | tsx: 'React',
30 | })[framework] || framework,
31 | },
32 | },
33 | sandboxConfig: {
34 | /** StackBlitz sandbox configuration */
35 | stackBlitz: {
36 | html: {
37 | project: {
38 | title: packageJson.name + ' Demo',
39 | description: 'A live web component demo',
40 | files: {
41 | [`libs/${packageJson.name}/index.js`]:
42 | await fetchLibData('/html/index.js'),
43 | },
44 | },
45 | exampleTemplate: {
46 | fileName: 'index.html',
47 | template: `
48 |
49 |
50 |
51 | ${packageJson.name} Demo
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 | %example%
60 |
61 | `,
62 | },
63 | },
64 | tsx: {
65 | project: {
66 | title: packageJson.name + ' React Demo',
67 | description: 'A live react/web component demo',
68 | files: {
69 | [`libs/${packageJson.name}/react/index.js`]:
70 | await fetchLibData('/react/index.js'),
71 | [`libs/${packageJson.name}/react/index.d.ts`]:
72 | await fetchLibData('/react/index.d.ts'),
73 | 'src/index.tsx': `import { createRoot } from "react-dom/client";
74 | import App from "./App";
75 |
76 | const rootElement = createRoot(document.getElementById("root"));
77 | rootElement.render();`,
78 | [`libs/${packageJson.name}/package.json`]: `{
79 | "name": "${packageJson.name}",
80 | "version": "${packageJson.version}",
81 | "description": "A live react/web component demo",
82 | "main": "index.js"
83 | }`,
84 | 'package.json': `{
85 | "name": "react-sandbox",
86 | "version": "1.0.0",
87 | "main": "src/index.tsx",
88 | "dependencies": {
89 | "react": "^18.2.0",
90 | "react-dom": "^18.2.0",
91 | "${packageJson.name}": "file:./libs/${packageJson.name}"
92 | },
93 | "scripts": {
94 | "dev": "vite",
95 | "build": "vite build",
96 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
97 | "preview": "vite preview"
98 | },
99 | "devDependencies": {
100 | "@types/react": "^18.3.3",
101 | "@types/react-dom": "^18.3.0",
102 | "@typescript-eslint/eslint-plugin": "^7.15.0",
103 | "@typescript-eslint/parser": "^7.15.0",
104 | "@vitejs/plugin-react": "^4.3.1",
105 | "eslint": "^8.57.0",
106 | "eslint-plugin-react-hooks": "^4.6.2",
107 | "eslint-plugin-react-refresh": "^0.4.7",
108 | "typescript": "^5.2.2",
109 | "vite": "^5.3.2"
110 | }
111 | }`,
112 | 'tsconfig.json': `{
113 | "include": ["./src/**/*"],
114 | "compilerOptions": {
115 | "strict": false,
116 | "esModuleInterop": true,
117 | "lib": ["ESNext", "DOM", "DOM.Iterable"],
118 | "jsx": "react-jsx",
119 | "baseUrl": "."
120 | }
121 | }`,
122 | 'vite.config.ts': `import { defineConfig } from 'vite';
123 | import react from '@vitejs/plugin-react';
124 |
125 | // https://vitejs.dev/config/
126 | export default defineConfig({
127 | plugins: [react()],
128 | });`,
129 | 'index.html': `
130 |
131 |
132 |
133 |
134 |
135 | React code example
136 |
137 |
138 |
139 |
140 |
141 | `,
142 | },
143 | },
144 | exampleTemplate: {
145 | fileName: 'src/App.tsx',
146 | template: `%example%`,
147 | },
148 | },
149 | },
150 | },
151 | });
152 | })();
153 |
--------------------------------------------------------------------------------
/.storybook/main.ts:
--------------------------------------------------------------------------------
1 | import type { StorybookConfig } from '@storybook/web-components-vite';
2 |
3 | const config: StorybookConfig = {
4 | stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
5 | addons: [
6 | '@storybook/addon-links',
7 | '@storybook/addon-essentials',
8 | '@storybook/addon-a11y',
9 | ],
10 | framework: {
11 | name: '@storybook/web-components-vite',
12 | options: {},
13 | },
14 | staticDirs: ['../public'],
15 | };
16 | export default config;
17 |
--------------------------------------------------------------------------------
/.storybook/preview.ts:
--------------------------------------------------------------------------------
1 | import { setCustomElementsManifest } from '@storybook/web-components';
2 | import customElements from '../custom-elements.json';
3 | import packageJson from '../package.json';
4 | import { setWcStorybookHelpersConfig } from 'wc-storybook-helpers';
5 | import { withActions } from '@storybook/addon-actions/decorator';
6 | import { setWcDoxConfig } from 'wc-dox';
7 | import './code-bubble-setup.js';
8 | import './styles.css';
9 | import '../public/html/index.js';
10 |
11 | import type { Preview } from '@storybook/web-components';
12 |
13 | setCustomElementsManifest(customElements);
14 |
15 | // This adds additional features to storybook - https://www.npmjs.com/package/wc-storybook-helpers
16 | setWcStorybookHelpersConfig({ typeRef: 'expandedType' });
17 |
18 | // This configures the documentation for the API documentation - https://www.npmjs.com/package/wc-dox
19 | setWcDoxConfig(customElements, {
20 | imports: {
21 | langOnPreTag: true,
22 | imports: [
23 | {
24 | label: 'HTML',
25 | lang: 'html',
26 | importTemplate: (tagName) =>
27 | ``,
28 | },
29 | {
30 | label: 'NPM',
31 | lang: 'js',
32 | importTemplate: (tagName) =>
33 | `import '${packageJson.name}/dist/components/${tagName}/index.js';`,
34 | },
35 | {
36 | label: 'React',
37 | lang: 'js',
38 | importTemplate: (tagName, className) =>
39 | `import { ${className} } from '${packageJson.name}/react/index.js';`,
40 | },
41 | ],
42 | },
43 | });
44 |
45 |
46 | const preview: Preview = {
47 | parameters: {
48 | controls: {
49 | expanded: true, // provides descriptions and additional info for controls
50 | sort: 'alpha', // sorts controls in alphabetical order
51 | },
52 | },
53 | decorators: [withActions],
54 | };
55 |
56 | export default preview;
57 |
--------------------------------------------------------------------------------
/.storybook/styles.css:
--------------------------------------------------------------------------------
1 | :not(:defined) {
2 | opacity: 0;
3 | }
4 |
5 | code-bubble {
6 | margin-bottom: 2rem;
7 | }
8 |
9 | code-bubble[code-bubble] pre[slot] {
10 | margin: 0;
11 | }
12 |
13 | code-bubble[code-bubble] pre button {
14 | display: none;
15 | }
16 |
17 | .docblock-source.sb-unstyled {
18 | margin: 0;
19 | border: 0;
20 | box-shadow: none;
21 | }
22 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "bierner.lit-html",
4 | "dbaeumer.vscode-eslint",
5 | "esbenp.prettier-vscode",
6 | "streetsidesoftware.code-spell-checker",
7 | "unifiedjs.vscode-mdx",
8 | "yzhang.markdown-all-in-one"
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Burton Smith
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Lit Starter Kit
2 |
3 | Welcome to the Lit Starter Kit. This is not an official kit for the Lit library, but this is a tool to get a component library up and running quickly.
4 |
5 | ## Features
6 |
7 | This repository is designed to be a "batteries included" repo, so you can hit the ground running with what you need to start delivering components. This repo includes:
8 |
9 | - ✅ Library and component scaffolding
10 | - ✅ [Storybook](https://storybook.js.org/docs/get-started/frameworks/web-components-vite?renderer=web-components) integration (with [helpers](https://www.npmjs.com/package/wc-storybook-helpers))
11 | - ✅ CDN build (in `/cdn`)
12 | - ✅ NPM build (in `/dist`)
13 | - ✅ Testing
14 | - ✅ Documentation
15 | - ✅ [React wrappers](https://www.npmjs.com/-package/custom-element-react-wrappers) (in - `/react`)
16 | - ✅ [JSX integration](https://www.npmjs.com/-package/custom-element-jsx-integration) - (in `/types`)
17 | - ✅ [Vue.js integration](https://www.npmjs.-com/package/-custom-element-vuejs-integration) (in `/- types`)
18 | - ✅ [Svelte integration](https://www.npmjs.-com/package/-custom-element-svelte-integration) (in `/- types`)
19 | - ✅ [SolidJS integration](https://www.npmjs.-com/package/-custom-element-solidjs-integration) (in `/- types`)
20 | - ✅ [HTML linter](https://www.npmjs.com/package/eslint-plugin-custom-element) (in `/eslint`)
21 |
22 | ## Getting Started
23 |
24 | You can choose to fork this repository directly or you can run the following command to create a new project.
25 |
26 | ```bash
27 | npm init lit-starter-kit your-project-name
28 | ```
29 |
30 | ## Running the Code
31 |
32 | The development environment uses [Storybook](https://storybook.js.org/) to showcase and document the components. The documentation files are written in MDX files to increase portability in case you wan to use a different tool for documenting your components.
33 |
34 | ```bash
35 | npm run dev
36 | ```
37 |
38 | ### Creating a New Component
39 |
40 | This project leverages [plop](https://www.npmjs.com/package/plop) to generate new components in your library. You can create a new component by running the following command and following the prompts.
41 |
42 | ```bash
43 | npm run new
44 | ```
45 |
46 | ## Building the Project
47 |
48 | Generating the final build assets will generate the `dist` assets for the NPM package, the content for the CDN located in the `cdn` directory at the root of the project, as well as the meta content for your components like framework integrations like types and react wrappers.
49 |
50 | ```bash
51 | npm run build
52 | ```
53 |
54 | ## Testing the Components
55 |
56 | Tests are written and executed using [web-test-runner](https://modern-web.dev/docs/test-runner/overview/) which execute your tests in _real_ browsers to validate your APIs are working as expected in the environments you intend to be using them in.
57 |
58 | Tests can be configured in the `web-test-runner.config.js` file located at the root of the project.
59 |
60 | Tests can be run using the following command:
61 |
62 | ```bash
63 | npm test
64 | ```
65 |
--------------------------------------------------------------------------------
/custom-elements-manifest.config.js:
--------------------------------------------------------------------------------
1 | import { getTsProgram, expandTypesPlugin } from 'cem-plugin-expanded-types';
2 | import { customElementReactWrapperPlugin } from 'custom-element-react-wrappers';
3 | import { customElementVsCodePlugin } from 'custom-element-vs-code-integration';
4 | import { customElementJetBrainsPlugin } from 'custom-element-jet-brains-integration';
5 | import { customElementSolidJsPlugin } from 'custom-element-solidjs-integration';
6 | import { customElementJsxPlugin } from 'custom-element-jsx-integration';
7 | import { customElementVuejsPlugin } from 'custom-element-vuejs-integration';
8 | import { customElementSveltePlugin } from 'custom-element-svelte-integration';
9 | import { cemInheritancePlugin } from 'custom-elements-manifest-inheritance';
10 | import { customElementLazyLoaderPlugin } from 'custom-element-lazy-loader';
11 | import { customJSDocTagsPlugin } from 'cem-plugin-custom-jsdoc-tags';
12 | import { customEsLintRuleGeneratorPlugin } from 'custom-element-eslint-rule-generator';
13 | import { cemDeprecatorPlugin } from "custom-elements-manifest-deprecator";
14 |
15 | export default {
16 | /** Globs to analyze */
17 | globs: ['src/components/**/*.ts'],
18 | /** Globs to exclude */
19 | exclude: ['src/**/*.test.ts', 'src/**/*.stories.ts', 'src/**/*.styles.ts'],
20 | /** Enable special handling for litelement */
21 | litelement: true,
22 | /** Provide custom plugins */
23 | plugins: [
24 | expandTypesPlugin(),
25 | cemInheritancePlugin(),
26 | cemDeprecatorPlugin(),
27 |
28 | customElementVsCodePlugin(),
29 | customElementJetBrainsPlugin(),
30 | customElementReactWrapperPlugin({
31 | outdir: 'react',
32 | modulePath: (_, tagName) =>
33 | `../dist/components/${tagName.replace('my-', '')}/index.js`,
34 | }),
35 | customElementSolidJsPlugin({
36 | outdir: 'types',
37 | fileName: 'custom-element-solidjs.d.ts',
38 | modulePath: (_, tagName) =>
39 | `../dist/components/${tagName.replace('my-', '')}/${tagName.replace('my-', '')}.js`,
40 | }),
41 | customElementJsxPlugin({
42 | outdir: 'types',
43 | modulePath: (_, tagName) =>
44 | `../dist/components/${tagName.replace('my-', '')}/${tagName.replace('my-', '')}.js`,
45 | }),
46 | customElementVuejsPlugin({
47 | outdir: 'types',
48 | fileName: 'custom-element-vuejs.d.ts',
49 | modulePath: (_, tagName) =>
50 | `../dist/components/${tagName.replace('my-', '')}/${tagName.replace('my-', '')}.js`,
51 | }),
52 | customElementSveltePlugin({
53 | outdir: 'types',
54 | fileName: 'custom-element-svelte.d.ts',
55 | modulePath: (_, tagName) =>
56 | `../dist/components/${tagName.replace('my-', '')}/${tagName.replace('my-', '')}.js`,
57 | }),
58 | customElementLazyLoaderPlugin({
59 | outdir: 'cdn',
60 | importPathTemplate: (_, tagName) =>
61 | `../dist/components/${tagName.replace('my-', '')}/${tagName.replace('my-', '')}.js`,
62 | }),
63 |
64 | customJSDocTagsPlugin({
65 | tags: {
66 | status: {},
67 | since: {},
68 | dependency: {
69 | mappedName: 'dependencies',
70 | isArray: true,
71 | },
72 | },
73 | }),
74 |
75 | customEsLintRuleGeneratorPlugin({
76 | outdir: 'eslint',
77 | }),
78 | ],
79 |
80 | overrideModuleCreation: ({ ts, globs }) => {
81 | const program = getTsProgram(ts, globs, 'tsconfig.json');
82 | return program
83 | .getSourceFiles()
84 | .filter(sf => globs.find(glob => sf.fileName.includes(glob)));
85 | },
86 | };
87 |
--------------------------------------------------------------------------------
/eslint.config.js:
--------------------------------------------------------------------------------
1 | import globals from 'globals';
2 | import js from '@eslint/js';
3 | import tseslint from 'typescript-eslint';
4 | import { configs as lit } from 'eslint-plugin-lit';
5 | import eslintConfigPrettier from 'eslint-config-prettier';
6 | import json from '@eslint/json';
7 | import markdown from '@eslint/markdown';
8 | import storybook from 'eslint-plugin-storybook';
9 | import litA11y from 'eslint-plugin-lit-a11y';
10 |
11 | /** @type {import('eslint').Linter.Config[]} */
12 | export default [
13 | { languageOptions: { globals: globals.browser } },
14 | eslintConfigPrettier,
15 | ...storybook.configs['flat/recommended'],
16 |
17 | // lint JSON files
18 | {
19 | files: ['**/*.json'],
20 | language: 'json/json',
21 | ...json.configs.recommended,
22 | rules: {
23 | 'json/no-duplicate-keys': 'error',
24 | 'no-irregular-whitespace': 'off',
25 | },
26 | },
27 |
28 | // lint MD files
29 | ...markdown.configs.recommended,
30 | {
31 | files: ['**/*.md'],
32 | rules: {
33 | 'no-irregular-whitespace': 'off',
34 | },
35 | },
36 |
37 | // lint JS/TS files
38 | ...tseslint.configs.recommended,
39 | {
40 | files: ['**/*.{js,mjs,cjs,ts}'],
41 | ...lit['flat/recommended'],
42 | ...js.configs.recommended,
43 | rules: {},
44 | },
45 |
46 | {
47 | plugins: {
48 | 'lit-a11y': litA11y,
49 | },
50 | },
51 |
52 | {
53 | ignores: [
54 | '.vscode/*',
55 | 'cdn/*',
56 | 'dist/*',
57 | 'eslint/*',
58 | 'plop-templates/*',
59 | 'public/*',
60 | 'react/*',
61 | 'types/*',
62 | 'custom-elements.json',
63 | 'vscode.css-custom-data.json',
64 | 'vscode.html-custom-data.json',
65 | 'web-types.json',
66 | 'tsconfig.json',
67 | ],
68 | },
69 | ];
70 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "lit-starter-kit",
3 | "version": "0.0.0",
4 | "description": "A starter kit for creating a Lit-based web component library.",
5 | "main": "index.js",
6 | "type": "module",
7 | "scripts": {
8 | "analyze": "cem analyze",
9 | "analyze:dev": "cem analyze --watch",
10 | "clean": "git clean -fqdx",
11 | "dev": "concurrently -k -r \"npm run analyze:dev\" \"npm run build:watch\" \"npm run storybook\"",
12 | "test": "web-test-runner --group default",
13 | "build": "npm run analyze && npm run build:cdn",
14 | "build:cdn": "tsc && rollup -c rollup.config.js",
15 | "build:watch": "concurrently -k -r \"tsc --watch\" \"rollup -c rollup.config.js --watch\"",
16 | "new": "plop",
17 | "deploy": "npm run build && npm publish",
18 | "format": "npm run format:eslint && npm run format:prettier",
19 | "format:eslint": "npx eslint --fix",
20 | "format:prettier": "npx prettier . --write",
21 | "lint": "npm run lint:eslint && npm run lint:prettier",
22 | "lint:eslint": "npx eslint",
23 | "lint:prettier": "npx prettier . --check",
24 | "prepare": "husky && npx playwright install-deps",
25 | "storybook": "storybook dev -p 6006",
26 | "build-storybook": "storybook build"
27 | },
28 | "author": "",
29 | "license": "MIT",
30 | "dependencies": {
31 | "lit": "^3.2.1"
32 | },
33 | "devDependencies": {
34 | "@custom-elements-manifest/analyzer": "^0.10.3",
35 | "@eslint/js": "^9.16.0",
36 | "@eslint/json": "^0.8.0",
37 | "@eslint/markdown": "^6.2.1",
38 | "@open-wc/testing": "^4.0.0",
39 | "@playwright/test": "^1.46.1",
40 | "@rollup/plugin-multi-entry": "^6.0.1",
41 | "@rollup/plugin-node-resolve": "^15.3.0",
42 | "@rollup/plugin-terser": "^0.4.4",
43 | "@rollup/plugin-typescript": "^12.1.1",
44 | "@storybook/addon-a11y": "^8.4.6",
45 | "@storybook/addon-actions": "^8.1.11",
46 | "@storybook/addon-essentials": "^8.1.11",
47 | "@storybook/addon-links": "^8.1.11",
48 | "@storybook/blocks": "^8.1.11",
49 | "@storybook/test": "^8.1.11",
50 | "@storybook/web-components": "^8.1.11",
51 | "@storybook/web-components-vite": "^8.1.11",
52 | "@types/mocha": "^10.0.2",
53 | "@web/dev-server-esbuild": "^1.0.2",
54 | "@web/test-runner": "^0.19.0",
55 | "@web/test-runner-commands": "^0.9.0",
56 | "@web/test-runner-playwright": "^0.11.0",
57 | "cem-plugin-custom-jsdoc-tags": "^1.1.2",
58 | "cem-plugin-expanded-types": "^1.3.3",
59 | "code-bubble": "^1.2.0",
60 | "concurrently": "^9.1.0",
61 | "custom-element-eslint-rule-generator": "^1.0.1",
62 | "custom-element-jet-brains-integration": "^1.6.2",
63 | "custom-element-jsx-integration": "^1.5.3",
64 | "custom-element-lazy-loader": "^1.3.1",
65 | "custom-element-react-wrappers": "^1.6.8",
66 | "custom-element-solidjs-integration": "^1.8.2",
67 | "custom-element-svelte-integration": "^1.1.2",
68 | "custom-element-vs-code-integration": "^1.4.1",
69 | "custom-element-vuejs-integration": "^1.3.3",
70 | "custom-elements-manifest-deprecator": "^1.1.1",
71 | "custom-elements-manifest-inheritance": "^1.1.1",
72 | "eslint": "^9.16.0",
73 | "eslint-config-prettier": "^9.1.0",
74 | "eslint-plugin-lit": "^1.15.0",
75 | "eslint-plugin-lit-a11y": "^1.1.0-next.1",
76 | "eslint-plugin-require-extensions": "^0.1.3",
77 | "eslint-plugin-storybook": "^0.11.1",
78 | "glob": "^11.0.0",
79 | "globals": "^15.13.0",
80 | "husky": "^9.0.11",
81 | "lint-staged": "^15.2.7",
82 | "plop": "^4.0.1",
83 | "prettier": "^3.3.2",
84 | "rollup": "^4.28.0",
85 | "rollup-plugin-dts": "^6.1.1",
86 | "rollup-plugin-summary": "^3.0.0",
87 | "storybook": "^8.1.11",
88 | "typescript": "^5.5.3",
89 | "typescript-eslint": "^8.17.0",
90 | "wc-dox": "^1.2.0",
91 | "wc-storybook-helpers": "^2.0.4"
92 | },
93 | "lint-staged": {
94 | "*.js": "eslint --cache --fix",
95 | "*.format:prettier": "prettier --write"
96 | },
97 | "files": [
98 | "cdn",
99 | "eslint",
100 | "dist",
101 | "react",
102 | "types",
103 | "index.d.ts",
104 | "index.js",
105 | "package.json",
106 | "custom-elements.json",
107 | "vscode.css-custom-data.json",
108 | "vscode.html-custom-data.json",
109 | "web-types.json"
110 | ],
111 | "keywords": [
112 | "web-components",
113 | "custom-elements",
114 | "lit-element",
115 | "typescript",
116 | "lit"
117 | ]
118 | }
119 |
--------------------------------------------------------------------------------
/plop-templates/component.definition.ts.hbs:
--------------------------------------------------------------------------------
1 | import { {{pascalCase tagName}} } from './{{kebabCase name}}.js';
2 |
3 | export type * from './{{kebabCase name}}.js';
4 |
5 | customElements.define('{{kebabCase tagName}}', {{pascalCase tagName}});
--------------------------------------------------------------------------------
/plop-templates/component.docs.hbs:
--------------------------------------------------------------------------------
1 | # {{dashToTitle name}}
2 |
3 | Add examples and descriptions of the component features here.
4 |
5 |
6 |
7 | ```html
8 | <{{kebabCase tagName}}>{{kebabCase tagName}}>
9 | ```
10 |
11 | ```tsx
12 | import { {{pascalCase tagName}} } from 'lit-starter-kit/react';
13 |
14 | export default () => {
15 | return (
16 | <>
17 | <{{pascalCase tagName}}>{{pascalCase tagName}}>
18 | >
19 | );
20 | };
21 | ```
22 |
23 |
24 |
25 | ## API
26 |
27 |
--------------------------------------------------------------------------------
/plop-templates/component.stories.ts.hbs:
--------------------------------------------------------------------------------
1 | import { StoryObj } from '@storybook/web-components';
2 | import { getWcStorybookHelpers } from 'wc-storybook-helpers';
3 | import type { {{pascalCase tagName}} } from './index.js';
4 |
5 | const { args, argTypes, events, template } = getWcStorybookHelpers('{{kebabCase tagName}}');
6 |
7 | export default {
8 | title: 'Components/{{pascalCase name}}',
9 | component: '{{kebabCase tagName}}',
10 | args,
11 | argTypes,
12 | parameters: {
13 | actions: {
14 | handles: events,
15 | },
16 | },
17 | };
18 |
19 |
20 | type Story = StoryObj<{{pascalCase tagName}} & typeof args>;
21 |
22 | export const Default: Story = {
23 | render: args => template(args),
24 | args: {}
25 | };
26 |
--------------------------------------------------------------------------------
/plop-templates/component.styles.ts.hbs:
--------------------------------------------------------------------------------
1 | import { css } from 'lit';
2 |
3 | export default css`
4 | :host {
5 | }
6 | `;
7 |
--------------------------------------------------------------------------------
/plop-templates/component.test.ts.hbs:
--------------------------------------------------------------------------------
1 | import './index.js';
2 | import { expect, fixture, html } from '@open-wc/testing';
3 | import type { {{pascalCase tagName}} } from './{{kebabCase name}}.js';
4 |
5 | describe('{{pascalCase tagName}}', () => {
6 | describe('accessibility', () => {
7 | it('default is accessible', async () => {
8 | const el = await fixture<{{pascalCase tagName}}>(html`<{{kebabCase tagName}}>{{kebabCase tagName}}>`);
9 | await expect(el).to.be.accessible();
10 | });
11 | });
12 | });
13 |
--------------------------------------------------------------------------------
/plop-templates/component.ts.hbs:
--------------------------------------------------------------------------------
1 | import { html, LitElement } from 'lit';
2 | import { property } from 'lit/decorators.js';
3 | import styles from './{{kebabCase name}}.styles.js';
4 |
5 | /**
6 | * Add a description here
7 | *
8 | * @tag {{kebabCase tagName}}
9 | * @since 0.0.0
10 | * @status experimental
11 | *
12 | **/
13 | export class {{pascalCase tagName}} extends LitElement {
14 | static override styles = styles;
15 |
16 | @property()
17 | heading = 'Hello, word!';
18 |
19 | override render() {
20 | return html`
21 | ${this.heading}
22 | `;
23 | }
24 | }
25 |
26 | export default {{pascalCase tagName}};
27 |
--------------------------------------------------------------------------------
/plopfile.js:
--------------------------------------------------------------------------------
1 | /** @arg {import('plop').NodePlopAPI} plop */
2 |
3 | export default function (plop) {
4 | plop.setHelper('dashToTitle', text => {
5 | const titleCase = plop.getHelper('titleCase');
6 | return titleCase(text.replace(/-/g, ' '));
7 | });
8 |
9 | plop.setGenerator('component', {
10 | description: 'generate a new component',
11 | prompts: [
12 | {
13 | type: 'input',
14 | name: 'name',
15 | message:
16 | 'Please enter your component name in kebab-case (e.g. button-group)',
17 | default: 'component',
18 | },
19 | {
20 | type: 'input',
21 | name: 'prefix',
22 | message:
23 | 'What is the prefix for your component? (e.g. my)',
24 | default: '',
25 | },
26 | ],
27 | actions: function (data) {
28 | const basename = data?.name;
29 | if (
30 | // Must only contain alphanumeric characters and dashes
31 | !/[a-z0-9-]+/.test(basename) ||
32 | // Must start with a letter
33 | !/^[a-z]/.test(basename) ||
34 | // Must not end in a dash
35 | basename.endsWith('-')
36 | ) {
37 | console.log(
38 | 'The name must only contain alphanumeric characters and dashes, start with a letter, and not end in a dash. Please try again.',
39 | );
40 | return [];
41 | }
42 |
43 | const BASE_PATH = `src/components/{{kebabCase name}}`;
44 |
45 | data.tagName = data?.prefix ? `${data.prefix}-${data.name}` : data.name;
46 |
47 | return [
48 | {
49 | type: 'add',
50 | skipIfExists: true,
51 | path: `${BASE_PATH}/{{kebabCase name}}.ts`,
52 | templateFile: 'plop-templates/component.ts.hbs',
53 | },
54 | {
55 | type: 'add',
56 | skipIfExists: true,
57 | path: `${BASE_PATH}/{{kebabCase name}}.styles.ts`,
58 | templateFile: 'plop-templates/component.styles.ts.hbs',
59 | },
60 | {
61 | type: 'add',
62 | skipIfExists: true,
63 | path: `${BASE_PATH}/{{kebabCase name}}.test.ts`,
64 | templateFile: 'plop-templates/component.test.ts.hbs',
65 | },
66 | {
67 | type: 'add',
68 | skipIfExists: true,
69 | path: `${BASE_PATH}/{{kebabCase name}}.mdx`,
70 | templateFile: 'plop-templates/component.docs.hbs',
71 | },
72 | {
73 | type: 'add',
74 | skipIfExists: true,
75 | path: `${BASE_PATH}/{{kebabCase name}}.stories.ts`,
76 | templateFile: 'plop-templates/component.stories.ts.hbs',
77 | },
78 | {
79 | type: 'add',
80 | skipIfExists: true,
81 | path: `${BASE_PATH}/index.ts`,
82 | templateFile: 'plop-templates/component.definition.ts.hbs',
83 | },
84 | ];
85 | },
86 | });
87 | }
88 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import { globSync } from 'glob';
2 | import path from 'node:path';
3 | import { fileURLToPath } from 'node:url';
4 | import typescript from '@rollup/plugin-typescript';
5 | import dts from 'rollup-plugin-dts';
6 | import resolve from '@rollup/plugin-node-resolve';
7 | import multi from '@rollup/plugin-multi-entry';
8 | import terser from '@rollup/plugin-terser';
9 | import summary from 'rollup-plugin-summary';
10 |
11 | export default [
12 | /** bundle components for the CDN */
13 | {
14 | watch: false,
15 | input: Object.fromEntries(
16 | globSync('src/**/*.ts', {
17 | ignore: [
18 | 'src/**/*.test.ts',
19 | 'src/**/*.stories.ts',
20 | 'src/**/*.styles.ts',
21 | ],
22 | }).map(file => [
23 | // This remove `src/` as well as the file extension from each
24 | // file, so e.g. src/nested/foo.js becomes nested/foo
25 | path.relative(
26 | 'src',
27 | file.slice(0, file.length - path.extname(file).length),
28 | ),
29 | // This expands the relative paths to absolute paths, so e.g.
30 | // src/nested/foo becomes /project/src/nested/foo.js
31 | fileURLToPath(new URL(file, import.meta.url)),
32 | ]),
33 | ),
34 | output: {
35 | dir: 'cdn',
36 | format: 'esm',
37 | },
38 | plugins: [
39 | typescript({
40 | tsconfig: 'tsconfig.json',
41 | outDir: 'cdn',
42 | sourceMap: false,
43 | declaration: false,
44 | declarationMap: false,
45 | }),
46 | resolve(),
47 | terser({
48 | ecma: 2021,
49 | module: true,
50 | warnings: true,
51 | }),
52 | summary(),
53 | ],
54 | },
55 | // watch version
56 | {
57 | watch: {
58 | buildDelay: 150,
59 | },
60 | input: Object.fromEntries(
61 | globSync('src/**/*.ts', {
62 | ignore: ['src/**/*.test.ts', 'src/**/*.stories.ts'],
63 | }).map(file => [
64 | // This remove `src/` as well as the file extension from each
65 | // file, so e.g. src/nested/foo.js becomes nested/foo
66 | path.relative(
67 | 'src',
68 | file.slice(0, file.length - path.extname(file).length),
69 | ),
70 | // This expands the relative paths to absolute paths, so e.g.
71 | // src/nested/foo becomes /project/src/nested/foo.js
72 | fileURLToPath(new URL(file, import.meta.url)),
73 | ]),
74 | ),
75 | output: {
76 | dir: 'cdn',
77 | format: 'esm',
78 | },
79 | plugins: [
80 | typescript({
81 | tsconfig: 'tsconfig.json',
82 | outDir: 'cdn',
83 | sourceMap: false,
84 | declaration: false,
85 | declarationMap: false,
86 | }),
87 | resolve(),
88 | ],
89 | },
90 |
91 | /** bundle components for sandboxes */
92 | {
93 | watch: false,
94 | input: globSync('src/**/index.ts'),
95 | output: {
96 | format: 'esm',
97 | dir: 'public/html',
98 | },
99 | plugins: [
100 | typescript({
101 | tsconfig: 'tsconfig.json',
102 | outDir: 'public/html',
103 | sourceMap: false,
104 | declaration: false,
105 | declarationMap: false,
106 | }),
107 | resolve(),
108 | multi({
109 | entryFileName: 'index.js',
110 | }),
111 | terser({
112 | ecma: 2021,
113 | module: true,
114 | warnings: true,
115 | }),
116 | summary(),
117 | ],
118 | },
119 | // watch version
120 | {
121 | watch: {
122 | buildDelay: 150,
123 | },
124 | input: globSync('src/**/index.ts'),
125 | output: {
126 | format: 'esm',
127 | dir: 'public/html',
128 | },
129 | plugins: [
130 | typescript({
131 | tsconfig: 'tsconfig.json',
132 | outDir: 'public/html',
133 | sourceMap: false,
134 | declaration: false,
135 | declarationMap: false,
136 | }),
137 | resolve(),
138 | multi({
139 | entryFileName: 'index.js',
140 | }),
141 | ],
142 | },
143 |
144 | /** bundle react components for sandboxes */
145 | {
146 | watch: false,
147 | input: 'react/index.js',
148 | output: {
149 | format: 'esm',
150 | file: 'public/react/index.js',
151 | sourcemap: false,
152 | },
153 | external: ['react'],
154 | plugins: [
155 | resolve(),
156 | terser({
157 | ecma: 2021,
158 | module: true,
159 | warnings: true,
160 | }),
161 | summary(),
162 | ],
163 | onwarn(warning) {
164 | if (
165 | /Could not resolve import/.test(warning.message) ||
166 | /'this' keyword is equivalent to 'undefined'/.test(warning.message)
167 | ) {
168 | return;
169 | }
170 |
171 | console.error(warning.message);
172 | },
173 | },
174 | // watch version
175 | {
176 | watch: {
177 | buildDelay: 150,
178 | },
179 | input: 'react/index.js',
180 | output: {
181 | format: 'esm',
182 | file: 'public/react/index.js',
183 | sourcemap: false,
184 | },
185 | external: ['react'],
186 | plugins: [resolve()],
187 | onwarn(warning) {
188 | if (
189 | /Could not resolve import/.test(warning.message) ||
190 | /'this' keyword is equivalent to 'undefined'/.test(warning.message)
191 | ) {
192 | return;
193 | }
194 |
195 | console.error(warning.message);
196 | },
197 | },
198 | // bundle react component types for sandboxes
199 | {
200 | watch: false,
201 | input: './react/index.d.ts',
202 | output: [{ file: 'public/react/index.d.ts', format: 'es' }],
203 | external: ['react'],
204 | plugins: [dts(), resolve(), summary()],
205 | },
206 | // watch version
207 | {
208 | watch: {
209 | buildDelay: 150,
210 | },
211 | input: './react/index.d.ts',
212 | output: [{ file: 'public/react/index.d.ts', format: 'es' }],
213 | external: ['react'],
214 | plugins: [dts(), resolve()],
215 | },
216 | ];
217 |
--------------------------------------------------------------------------------
/src/components/button/button.mdx:
--------------------------------------------------------------------------------
1 | # Button
2 |
3 | This is an example of a button component.
4 |
5 |
6 |
7 | ```html
8 | My Button
9 | ```
10 |
11 | ```tsx
12 | import { MyButton } from 'lit-starter-kit/react';
13 |
14 | export default () => {
15 | return My Button;
16 | };
17 | ```
18 |
19 |
20 |
21 | ## Variations
22 |
23 | Here are some examples of button variations.
24 |
25 |
26 |
27 | ```html
28 | My Button
29 | My Button
30 | My Button
31 | My Button
32 | ```
33 |
34 | ```tsx
35 | import { MyButton } from 'lit-starter-kit/react';
36 |
37 | export default () => {
38 | return <>
39 | My Button
40 | My Button
41 | My Button
42 | My Button
43 | >;
44 | };
45 | ```
46 |
47 |
48 |
49 | ## Disabled
50 |
51 | Here is an example of how to disable a button.
52 |
53 |
54 |
55 | ```html
56 | My Button
57 | ```
58 |
59 | ```tsx
60 | import { MyButton } from 'lit-starter-kit/react';
61 |
62 | export default () => {
63 | return My Button;
64 | };
65 | ```
66 |
67 |
68 |
69 | ## API
70 |
71 |
--------------------------------------------------------------------------------
/src/components/button/button.stories.ts:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from '@storybook/web-components';
2 | import { getWcStorybookHelpers } from 'wc-storybook-helpers';
3 | import { html } from 'lit';
4 |
5 | import type { MyButton } from './button.js';
6 |
7 | const { events, args, argTypes, template } = getWcStorybookHelpers('my-button');
8 |
9 | const meta: Meta = {
10 | title: 'Components/Button',
11 | component: 'my-button',
12 | args,
13 | argTypes,
14 | parameters: {
15 | actions: {
16 | handles: events,
17 | },
18 | },
19 | };
20 | export default meta;
21 |
22 | /**
23 | * create Story type that will provide autocomplete and docs for `args`,
24 | * but also allow for namespaced args like CSS Shadow Parts and Slots
25 | */
26 | type Story = StoryObj;
27 |
28 | export const Default: Story = {
29 | render: args => html`${template(args)}`,
30 | args: {
31 | 'default-slot': 'My Button',
32 | },
33 | };
34 |
--------------------------------------------------------------------------------
/src/components/button/button.styles.ts:
--------------------------------------------------------------------------------
1 | import { css } from 'lit';
2 |
3 | export default css`
4 | :host {
5 | --button-bg-color: #f0f0f0;
6 | --button-fg-color: #333;
7 | --button-border-color: transparent;
8 |
9 | display: inline-flex;
10 | }
11 |
12 | button {
13 | cursor: pointer;
14 | background-color: var(--button-bg-color);
15 | border: 1px solid var(--button-border-color);
16 | border-radius: 4px;
17 | color: var(--button-fg-color);
18 | padding: 8px 16px;
19 | }
20 |
21 | button:disabled {
22 | cursor: not-allowed;
23 | opacity: 0.5;
24 | }
25 |
26 | :host([variation='primary']) {
27 | --button-bg-color: #024996;
28 | --button-fg-color: white;
29 | --button-border-color: #024996;
30 | }
31 |
32 | :host([variation='hollow']) {
33 | --button-bg-color: transparent;
34 | --button-fg-color: #024996;
35 | --button-border-color: #024996;
36 | }
37 |
38 | :host([variation='transparent']) {
39 | --button-bg-color: transparent;
40 | --button-fg-color: #024996;
41 | --button-border-color: transparent;
42 | }
43 | `;
44 |
--------------------------------------------------------------------------------
/src/components/button/button.test.ts:
--------------------------------------------------------------------------------
1 | import './index.js';
2 | import { expect, fixture, html } from '@open-wc/testing';
3 | import { MyButton } from './index.js';
4 |
5 | describe('MyButton', () => {
6 | describe('accessibility', () => {
7 | it('default is accessible', async () => {
8 | const el = await fixture(html`My Button`);
9 | await expect(el).to.be.accessible();
10 | });
11 |
12 | it('variations are accessible', async () => {
13 | const el = await fixture(html`
14 | My Button
15 | My Button
16 | My Button
17 | `);
18 | await expect(el).to.be.accessible();
19 | });
20 |
21 | it('disabled is accessible', async () => {
22 | const el = await fixture(html`My Button`);
23 | const button = el.shadowRoot?.querySelector('button');
24 |
25 | await expect(el).to.be.accessible();
26 | await expect(button?.hasAttribute('disabled')).to.be.true;
27 | });
28 | });
29 | });
30 |
--------------------------------------------------------------------------------
/src/components/button/button.ts:
--------------------------------------------------------------------------------
1 | import { html, LitElement } from 'lit';
2 | import { property } from 'lit/decorators.js';
3 | import styles from './button.styles.js';
4 |
5 | /**
6 | * An example button component
7 | *
8 | * @tag my-button
9 | *
10 | * @csspart control - The button element
11 | *
12 | * @cssproperty [--button-bg-color=#f0f0f0] - The background color of the button
13 | * @cssproperty [--button-fg-color=#333] - The text color of the button
14 | * @cssproperty [--button-border-color=transparent] - The border color of the button
15 | *
16 | * @slot - The main content for the button
17 | *
18 | */
19 | export default class MyButton extends LitElement {
20 | static override styles = styles;
21 |
22 | /** Changes the display of the button */
23 | @property()
24 | variation?: 'default' | 'primary' | 'hollow' | 'transparent';
25 |
26 | /** Controls the disabled property of the button */
27 | @property({ type: Boolean })
28 | disabled = false;
29 |
30 | override render() {
31 | return html`
32 |
35 | `;
36 | }
37 | }
38 |
39 | export { MyButton };
40 |
--------------------------------------------------------------------------------
/src/components/button/index.ts:
--------------------------------------------------------------------------------
1 | import { MyButton } from './button.js';
2 |
3 | export type * from './button.js';
4 |
5 | customElements.define('my-button', MyButton);
6 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | /* Visit https://aka.ms/tsconfig to read more about this file */
4 |
5 | /* Projects */
6 | // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
7 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
8 | // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
9 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
10 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
11 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
12 |
13 | /* Language and Environment */
14 | "target": "ES2020" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
15 | "lib": [
16 | "ES2020",
17 | "DOM",
18 | "DOM.Iterable"
19 | ] /* Specify a set of bundled library declaration files that describe the target runtime environment. */,
20 | // "jsx": "preserve", /* Specify what JSX code is generated. */
21 | "experimentalDecorators": true /* Enable experimental support for TC39 stage 2 draft decorators. */,
22 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
23 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
24 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
25 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
26 | // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
27 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
28 | "useDefineForClassFields": false /* Emit ECMAScript-standard-compliant class fields. */,
29 | // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
30 |
31 | /* Modules */
32 | "module": "ESNext" /* Specify what module code is generated. */,
33 | "rootDir": "./src" /* Specify the root folder within your source files. */,
34 | "moduleResolution": "Node" /* Specify how TypeScript looks up a file from a given module specifier. */,
35 | // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
36 | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
37 | // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
38 | // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
39 | // "types": [], /* Specify type package names to be included without being referenced in a source file. */
40 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
41 | "moduleSuffixes": [] /* List of file name suffixes to search when resolving a module. */,
42 | "resolveJsonModule": true /* Enable importing .json files. */,
43 | // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */
44 |
45 | /* JavaScript Support */
46 | "allowJs": true /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */,
47 | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
48 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
49 |
50 | /* Emit */
51 | "declaration": true /* Generate .d.ts files from TypeScript and JavaScript files in your project. */,
52 | "declarationMap": true /* Create sourcemaps for d.ts files. */,
53 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
54 | "sourceMap": true /* Create source map files for emitted JavaScript files. */,
55 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
56 | "outDir": "./dist" /* Specify an output folder for all emitted files. */,
57 | // "removeComments": true, /* Disable emitting comments. */
58 | // "noEmit": true, /* Disable emitting files from a compilation. */
59 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
60 | // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
61 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
62 | // "sourceRoot": "/src", /* Specify the root path for debuggers to find the reference source code. */
63 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
64 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
65 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
66 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
67 | // "newLine": "crlf", /* Set the newline character for emitting files. */
68 | // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
69 | // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
70 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
71 | // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
72 | // "declarationDir": "./", /* Specify the output directory for generated declaration files. */
73 | // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
74 |
75 | /* Interop Constraints */
76 | "isolatedModules": true /* Ensure that each file can be safely transpiled without relying on other imports. */,
77 | // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
78 | "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
79 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
80 | "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
81 |
82 | /* Type Checking */
83 | "strict": true /* Enable all strict type-checking options. */,
84 | "noImplicitAny": true /* Enable error reporting for expressions and declarations with an implied 'any' type. */,
85 | // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
86 | // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
87 | // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
88 | // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
89 | // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
90 | // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
91 | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
92 | "noUnusedLocals": true /* Enable error reporting when local variables aren't read. */,
93 | "noUnusedParameters": true /* Raise an error when a function parameter isn't read. */,
94 | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
95 | "noImplicitReturns": true /* Enable error reporting for codepaths that do not explicitly return in a function. */,
96 | "noFallthroughCasesInSwitch": true /* Enable error reporting for fallthrough cases in switch statements. */,
97 | // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
98 | "noImplicitOverride": true /* Ensure overriding members in derived classes are marked with an override modifier. */,
99 | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
100 | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
101 | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
102 |
103 | /* Completeness */
104 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
105 | "skipLibCheck": true /* Skip type checking all .d.ts files. */
106 | },
107 | "include": ["src"],
108 | "exclude": ["**/*.stories.ts", "**/*.test.ts"]
109 | }
110 |
--------------------------------------------------------------------------------
/web-test-runner.config.js:
--------------------------------------------------------------------------------
1 | import { esbuildPlugin } from '@web/dev-server-esbuild';
2 | import { playwrightLauncher } from '@web/test-runner-playwright';
3 | import { fileURLToPath } from 'url';
4 |
5 | export default {
6 | rootDir: '.',
7 | files: 'src/**/*.test.ts', // "default" group
8 | concurrentBrowsers: 3,
9 | nodeResolve: {
10 | exportConditions: ['production', 'default'],
11 | },
12 | testFramework: {
13 | config: {
14 | timeout: 3000,
15 | retries: 1,
16 | },
17 | },
18 | plugins: [
19 | esbuildPlugin({
20 | ts: true,
21 | tsconfig: fileURLToPath(new URL('./tsconfig.json', import.meta.url)),
22 | }),
23 | ],
24 | browsers: [
25 | playwrightLauncher({ product: 'chromium' }),
26 | playwrightLauncher({ product: 'firefox' }),
27 | playwrightLauncher({ product: 'webkit' }),
28 | ],
29 | testRunnerHtml: testFramework => `
30 |
31 |
32 |
33 |
36 |
37 |
38 |
39 | `,
40 | };
41 |
--------------------------------------------------------------------------------