From a0e68567be6fb118b5ae2e4c22cf73452c6a85ba Mon Sep 17 00:00:00 2001 From: Christian Bromann <git@bromann.dev> Date: Wed, 21 Feb 2024 18:07:40 -0800 Subject: [PATCH 01/10] docs(publishing): update general docs on publishing --- docs/guides/publishing.md | 146 +++++++++++++++++++------ docs/output-targets/custom-elements.md | 112 ------------------- docs/output-targets/dist.md | 26 ----- 3 files changed, 114 insertions(+), 170 deletions(-) diff --git a/docs/guides/publishing.md b/docs/guides/publishing.md index bb2f6dfdb..544a5a766 100644 --- a/docs/guides/publishing.md +++ b/docs/guides/publishing.md @@ -5,52 +5,134 @@ description: Publishing A Component Library slug: /publishing --- -# Publishing A Component Library - There are numerous strategies to publish and distribute your component library to be consumed by external projects. One of the benefits of Stencil is that is makes it easy to generate the various [output targets](../output-targets/01-overview.md) that are right for your use-case. -## Publishing to Node Package Manager (NPM) +## Use Cases + +For using Stencil components in other projects there are two important output targets necessary: [`dist`](distribution) and [`dist-custom-elements`](custom-elements). Both export your components for different use cases. Luckily, all builds can be generated at the same time, using the same source code, and shipped in the same distribution. It would be up to the consumer of your component library to decide which build to use. + +### Lazy Loading -The first step and highly recommended step is to -[publish the component library to NPM](https://docs.npmjs.com/getting-started/publishing-npm-packages). NPM is an online software registry for sharing libraries, tools, utilities, packages, etc. Once the library is published to NPM, other projects are able to add your component library as a dependency and use the components within their own projects. +If you prefer to have your components automatically loaded when used in your application, we recommend to enable the [`dist`](distribution) output target. The bundle gives you a small entry file that registers all your components and only loads the full component logic when it gets rendered in your application. It doesn't matter if the actual application is written in HTML or created with vanilla JavaScript, jQuery, React, etc. +Your users can import your component library, e.g. called `my-design-system`, either via a `script` tag: -## `package.json` +```html +<script type="module" src="https://unpkg.com/my-design-system"></script> +``` + +or by importing it in the bootstrap script of your application: -The purpose of the `package.json` file is to give other tools instructions on how to find the package's files, and to provide information about the package. For example, bundlers such as [Rollup](https://rollupjs.org/) and [Webpack](https://webpack.js.org/) use this configuration to locate the project's entry files. +```ts +import 'my-design-system'; +``` -An advantage to using the compiler is that it is able to provide help on how to best set up the project for distribution. Below is a common setup found within a project's `package.json` file: +To ensure that the right entry file is loaded when importing the project, define the following fields in your `package.json`: ```json { - "main": "dist/index.cjs.js", - "module": "dist/index.js", - "es2015": "dist/esm/index.mjs", - "es2017": "dist/esm/index.mjs", - "types": "dist/types/components.d.ts", - "unpkg": "dist/my-project-name/my-project-name.esm.js", - "collection:main": "dist/collection/index.js", - "collection": "dist/collection/collection-manifest.json", - "files": [ - "dist/", - "css/", - "loader/" - ] + "exports": "./dist/esm/my-design-system.js", + "main": "./dist/cjs/my-design-system.js", + "unpkg": "dist/my-design-system/my-design-system.esm.js", +} +``` + +Read more about various options when it comes to distributing your components lazily in the [`dist`](distribution) output target section. + +#### Considerations + +Loading Stencil components in an application lazily is a great approach, especially if you develop a large design system with many components. It enables you to import a small single file and have all components available through the entire application. + +However be aware that this approach is not ideal in all cases. It requires your application to ship the bundled components as static assets in order for them to load properly. Furthermore, having many nested component dependencies can have an impact on the performance of your application. For example, given you have a component `CmpA` which uses a Stencil component `CmpB` which itself uses another Stencil component `CmpC`. In order to fully render `CmpA` the browser has to load 3 scripts sequentially which can result in undesired rendering delays. + +### Standalone + +The [`dist-custom-elements`](custom-elements) output target builds each component as a stand-alone class that extends `HTMLElement`. The output is a standardized custom element with the styles already attached and without any of Stencil's lazy-loading. This may be preferred for projects that are already handling bundling, lazy-loading and defining the custom elements themselves. + +You can use these standalone components by importing them via: + +```ts +import { MyComponent, defineCustomElementMyComponent } from 'my-design-system' + +// register to CustomElementRegistry +defineCustomElementMyComponent() + +// or extend custom element via +class MyCustomComponent extends MyComponent { + // ... } +define('my-custom-component', MyCustomComponent) ``` -| Property | Description | Recommended | -|----------|-----------------------------------------------------------------------------------------------------|-----------------------------------| -| `main` | Entry file in the CommonJS module format. | `dist/index.cjs.js` | -| `module` | Entry file in the ES module format. ES modules is the standardized and recommended format. | `dist/index.js` | -| `es2015` | Commonly used by framework bundling. | `dist/esm/index.mjs` | -| `es2017` | Commonly used by framework bundling. | `dist/esm/index.mjs` | -| `types` | Entry file to the project's types. | `dist/types/components.d.ts` | -| `unpkg` | Entry file for requests to the projects [unpkg](https://unpkg.com/) CDN. | `dist/{NAMESPACE}/{NAMESPACE}.js` | -| `files` | Array of files that should be included in a npm release. | `["dist/", "loader/"]` | +To ensure that the right entry file is loaded when importing the project, define the following fields in your `package.json`: -The `collection` properties are used to allow lazy loading in other Stencil applications. +```json +{ + "exports": { + ".": { + "import": "./dist/components/index.js", + "types": "./dist/components/index.d.ts" + }, + "my-component": { + "import": "./dist/components/my-component.js", + "types": "./dist/components/my-component.d.ts" + } + }, + "types": "dist/components/index.d.ts", +} +``` + +If you define exports targets for all your components as shown above and by using `customElementsExportBehavior: 'auto-define-custom-elements'` as output target option, you can skip the `defineCustomElement` call and directly import the component where you need it: + +```ts +import 'my-design-system/my-component' +``` :::note -If you are distributing both the `dist` and `dist-custom-elements`, then it's best to pick one of them as the main entry. +If you are distributing both the `dist` and `dist-custom-elements`, then it's best to pick one of them as the main entry depending on which use case is more prominent. ::: + +Read more about various options when it comes to distributing your components as standalone components in the [`dist-custom-elements`](custom-elements) output target section. + +#### Considerations + +This distribution strategy is useful when you use an external bundler such as [Vite](https://vitejs.dev/), [WebPack](https://webpack.js.org/) or [Rollup](https://rollupjs.org) to compile your application. They ensure that only the components used within your application are bundled into compilation. + +#### Usage in TypeScript + +If you plan to support consuming your component library in TypeScript you'll need to set `generateTypeDeclarations: true` on the your output target in `stencil.config.ts`, like so: + +```tsx title="stencil.config.ts" +import { Config } from '@stencil/core'; + +export const config: Config = { + outputTargets: [ + { + type: 'dist-custom-elements', + generateTypeDeclarations: true, + }, + // ... + ], + // ... +}; +``` + +Then you can set the `types` property in `package.json` so that consumers of your package can find the type definitions, like so: + +```tsx title="package.json" +{ + "types": "dist/components/index.d.ts", + "dependencies": { + "@stencil/core": "latest" + }, + ... +} +``` + +:::note +If you set the `dir` property on the output target config, replace `dist/components` in the above snippet with the path set in the config. +::: + +## Publishing to NPM + +The first step and highly recommended step is to [publish the component library to NPM](https://docs.npmjs.com/getting-started/publishing-npm-packages). [NPM](https://www.npmjs.com/) is an online software registry for sharing libraries, tools, utilities, packages, etc. Once the library is published to NPM, other projects are able to add your component library as a dependency and use the components within their own projects. diff --git a/docs/output-targets/custom-elements.md b/docs/output-targets/custom-elements.md index 1e98c632d..2bc3bdf2b 100644 --- a/docs/output-targets/custom-elements.md +++ b/docs/output-targets/custom-elements.md @@ -128,32 +128,6 @@ _default: `false`_ Setting this flag to `true` will cause file minification to follow what is specified in the [Stencil config](../config/01-overview.md#minifyjs). _However_, if [`externalRuntime`](#externalruntime) is enabled, it will override this option and always result in minification being disabled. -## Consuming Custom Elements - -By default, the custom elements files will be written to `dist/components/`. This directory can be configured using the output target's [`dir`](#dir) config. - -The generated files will each export a component class and will already have the styles bundled. However, this build does not define the custom elements or apply any polyfills. -Static assets referenced within components will need to be set using `setAssetPath` (see [Making Assets Available](#making-assets-available)). - -Below is an example of defining a custom element: - -```tsx -import { defineCustomElement } from 'my-library/dist/components/hello-world'; - -defineCustomElement(); // Same as manually calling: customElements.define('hello-world', HelloWorld); -``` - -The output directory will also contain an `index.js` file which exports some helper methods by default. The contents of the file -will look something like: - -```js -export { setAssetPath, setPlatformOptions } from '@stencil/core/internal/client'; -``` - -:::note -The contents may look different if [`customElementsExportBehavior`](#customelementsexportbehavior) is specified! -::: - ## Making Assets Available For performance reasons, the generated bundle does not include [local assets](../guides/assets.md) built within the JavaScript output, @@ -180,84 +154,6 @@ Make sure to copy the assets over to a public directory in your app. This config bundling, and where your assets can be loaded from. How the files are copied to the production build directory depends on the bundler or tooling. The configs below provide examples of how to do this automatically with popular bundlers. -## Distributing Custom Elements - -See our docs on [publishing a component library](../guides/publishing.md) for information on setting up the library's `package.json` file and publishing to a package manager. - -By default, custom elements will need to be imported from the [output directory](#dir) specified on the output target config: - -```tsx -import { MyComponent } from 'best-web-components/dist/components/my-component'; -``` - -However, the `module` property in the `package.json` can be modified to point to the custom element output: - -```tsx title="package.json" -{ - "module": "dist/components/index.js", - "dependencies": { - "@stencil/core": "latest" - }, - ... -} -``` - -:::note -Be sure to set `@stencil/core` as a dependency of the package as well. -::: - -As a result, components can alternatively be imported from the root of the published package: - -```tsx -import { MyComponent } from 'best-web-components'; -``` - -:::note -If you are distributing the output of both the -[`dist`](./dist.md) and `dist-custom-elements` targets, then -it's up to you to choose which one of them should be available in the -`module` entry. -::: - -### Usage in TypeScript - -If you plan to support consuming your component library in TypeScript you'll -need to set `generateTypeDeclarations: true` on the your output target in -`stencil.config.ts`, like so: - -```tsx title="stencil.config.ts" -import { Config } from '@stencil/core'; - -export const config: Config = { - outputTargets: [ - { - type: 'dist-custom-elements', - generateTypeDeclarations: true, - }, - // ... - ], - // ... -}; -``` - -Then you can set the `types` property in `package.json` so that consumers of -your package can find the type definitions, like so: - -```tsx title="package.json" -{ - "module": "dist/components/index.js", - "types": "dist/components/index.d.ts", - "dependencies": { - "@stencil/core": "latest" - }, - ... -} -``` - -:::note -If you set the `dir` property on the output target config, replace `dist/components` in the above snippet with the path set in the config. -::: - ## Example Bundler Configs Instructions for consuming the custom elements bundle vary depending on the bundler you're using. These examples will help your users consume your components with webpack and Rollup. @@ -336,11 +232,3 @@ export default { ], }; ``` - -## How is this different from the "dist" output target? - -The `dist-custom-elements` builds each component as a stand-alone class that extends `HTMLElement`. The output is a standardized custom element with the styles already attached and without any of Stencil's lazy-loading. This may be preferred for projects that are already handling bundling, lazy-loading and defining the custom elements themselves. - -The `dist` output target, on the other hand, is more for projects that want to allow components to lazy-load themselves, without having to setup bundling configurations to do so. - -Luckily, all builds can be generated at the same time, using the same source code, and shipped in the same distribution. It would be up to the consumer of your component library to decide which build to use. diff --git a/docs/output-targets/dist.md b/docs/output-targets/dist.md index 4279308d8..6c18f5f3f 100644 --- a/docs/output-targets/dist.md +++ b/docs/output-targets/dist.md @@ -113,29 +113,3 @@ applyPolyfills().then(() => { ``` This is an alternative approach to e.g. loading the components directly through a script tag as mentioned below. Read more about `setNonce` and when to set it in our guide on [Content Security Policy Nonces](../guides/csp-nonce.md). - -## Distribution Options - -Each output target's form of bundling and distribution has its own pros and cons. Luckily you can just worry about writing good source code for your component. Stencil will handle generating the various bundles and consumers of your library can decide how to apply your components to their external projects. Below are a few of the options. - -### Script tag - -- Use a script tag linked to a CDN copy of your published NPM module, for example: `<script type="module" src='https://cdn.jsdelivr.net/npm/my-name@0.0.1/dist/myname.js'></script>`. -- The initial script itself is extremely tiny and does not represent the entire library. It's only a small registry. -- You can use any or all components within your library anywhere within that webpage. -- It doesn't matter if the actual component was written within the HTML or created with vanilla JavaScript, jQuery, React, etc. -- Only the components used on that page will be requested and lazy-loaded. - -### Importing the `dist` library using a bundler - -- Run `npm install my-name --save` -- Add an `import` within the root component: `import my-component`; -- Stencil will automatically setup the lazy-loading capabilities for the Stencil library. -- Then you can use the element anywhere in your template, JSX, HTML etc. - -### Importing the `dist` library into another Stencil app - -- Run `npm install my-name --save` -- Add an `import` within the root component: `import my-component`; -- Stencil will automatically setup the lazy-loading capabilities for the Stencil library. -- Then you can use the element anywhere in your template, JSX, HTML etc. From 8c8af57059d0d4c9a0974fc2d7e57f3c7c6a10a8 Mon Sep 17 00:00:00 2001 From: Christian Bromann <git@bromann.dev> Date: Wed, 21 Feb 2024 18:32:16 -0800 Subject: [PATCH 02/10] fix links --- docs/framework-integration/angular.md | 2 +- docs/guides/publishing.md | 4 ++-- docs/output-targets/custom-elements.md | 2 +- docs/output-targets/dist.md | 9 --------- 4 files changed, 4 insertions(+), 13 deletions(-) diff --git a/docs/framework-integration/angular.md b/docs/framework-integration/angular.md index e63721bc2..e608d0438 100644 --- a/docs/framework-integration/angular.md +++ b/docs/framework-integration/angular.md @@ -230,7 +230,7 @@ import { defineCustomElements } from 'stencil-library/loader'; export class ComponentLibraryModule {} ``` -See the [documentation](../output-targets/dist.md#distribution-options) for more information on defining custom elements using the +See the [documentation](../output-targets/dist.md) for more information on defining custom elements using the `dist` output target, or [update the Angular output target](#do-i-have-to-use-the-dist-output-target) to use `dist-custom-elements`. ### Link Your Packages (Optional) diff --git a/docs/guides/publishing.md b/docs/guides/publishing.md index 544a5a766..7ce46c81d 100644 --- a/docs/guides/publishing.md +++ b/docs/guides/publishing.md @@ -41,7 +41,7 @@ Read more about various options when it comes to distributing your components la #### Considerations -Loading Stencil components in an application lazily is a great approach, especially if you develop a large design system with many components. It enables you to import a small single file and have all components available through the entire application. +To start, Stencil was designed to lazy-load itself only when the component was actually used on a page. There are many benefits to this approach, such as simply adding a script tag to any page and the entire library is available for use, yet only the components actually used are downloaded. For example, [`@ionic/core`](https://www.npmjs.com/package/@ionic/core) comes with over 100 components, but a one webpage may only need `ion-toggle`. Instead of requesting the entire component library, or generating a custom bundle for just `ion-toggle`, the `dist` output target is able to generate a tiny entry build ready to load any of its components on-demand. However be aware that this approach is not ideal in all cases. It requires your application to ship the bundled components as static assets in order for them to load properly. Furthermore, having many nested component dependencies can have an impact on the performance of your application. For example, given you have a component `CmpA` which uses a Stencil component `CmpB` which itself uses another Stencil component `CmpC`. In order to fully render `CmpA` the browser has to load 3 scripts sequentially which can result in undesired rendering delays. @@ -96,7 +96,7 @@ Read more about various options when it comes to distributing your components as #### Considerations -This distribution strategy is useful when you use an external bundler such as [Vite](https://vitejs.dev/), [WebPack](https://webpack.js.org/) or [Rollup](https://rollupjs.org) to compile your application. They ensure that only the components used within your application are bundled into compilation. +The `dist-custom-elements` is a direct build of the custom element that extends `HTMLElement`, without any lazy-loading. This distribution strategy may be preferred for projects that use an external bundler such as [Vite](https://vitejs.dev/), [WebPack](https://webpack.js.org/) or [Rollup](https://rollupjs.org) to compile the application. They ensure that only the components used within your application are bundled into compilation. #### Usage in TypeScript diff --git a/docs/output-targets/custom-elements.md b/docs/output-targets/custom-elements.md index 2bc3bdf2b..be15d04db 100644 --- a/docs/output-targets/custom-elements.md +++ b/docs/output-targets/custom-elements.md @@ -68,7 +68,7 @@ export const config: Config = { | `default` | No additional re-export or auto-definition behavior will be performed.<br/><br/>This value will be used if no explicit value is set in the config, or if a given value is not a valid option. | | `auto-define-custom-elements` | A component and its children will be automatically defined with the `CustomElementRegistry` when the component's module is imported. | | `bundle` | A utility `defineCustomElements()` function is exported from the `index.js` file of the output directory. This function can be used to quickly define all Stencil components in a project on the custom elements registry. | -| `single-export-module` | All component and custom element definition helper functions will be exported from the `index.js` file in the output directory. This file can be used as the root module when distributing your component library, see [below](#distributing-custom-elements) for more details. | +| `single-export-module` | All component and custom element definition helper functions will be exported from the `index.js` file in the output directory. This file can be used as the root module when distributing your component library, see [Publishing](/publishing) for more details. | :::note At this time, components that do not use JSX cannot be automatically diff --git a/docs/output-targets/dist.md b/docs/output-targets/dist.md index 6c18f5f3f..5c33af5d2 100644 --- a/docs/output-targets/dist.md +++ b/docs/output-targets/dist.md @@ -17,15 +17,6 @@ outputTargets: [ ] ``` - -## How is this different from "dist-custom-elements" output target? - -To start, Stencil was designed to lazy-load itself only when the component was actually used on a page. There are many benefits to this approach, such as simply adding a script tag to any page and the entire library is available for use, yet only the components actually used are downloaded. For example, [`@ionic/core`](https://www.npmjs.com/package/@ionic/core) comes with over 100 components, but a one webpage may only need `ion-toggle`. Instead of requesting the entire component library, or generating a custom bundle for just `ion-toggle`, the `dist` output target is able to generate a tiny entry build ready to load any of its components on-demand. - -The `dist-custom-elements` on the other hand is a direct build of the custom element that extends `HTMLElement`, without any lazy-loading. The custom elements bundle does not apply polyfills, nor automatically define each custom elements. This may be preferred for projects that will handle bundling, lazy-loading and defining the custom elements themselves. - -Luckily, both builds can be generated at the same time, and shipped in the same distribution. It would be up to the consumer of your component library to decide which build to use. - ## Config ### collectionDir From 3f07d5c14e3351659e9bd85418a4ca32fb0ce568 Mon Sep 17 00:00:00 2001 From: Christian Bromann <git@bromann.dev> Date: Wed, 21 Feb 2024 18:35:15 -0800 Subject: [PATCH 03/10] fix more links --- docs/guides/publishing.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/guides/publishing.md b/docs/guides/publishing.md index 7ce46c81d..82180b790 100644 --- a/docs/guides/publishing.md +++ b/docs/guides/publishing.md @@ -9,11 +9,11 @@ There are numerous strategies to publish and distribute your component library t ## Use Cases -For using Stencil components in other projects there are two important output targets necessary: [`dist`](distribution) and [`dist-custom-elements`](custom-elements). Both export your components for different use cases. Luckily, all builds can be generated at the same time, using the same source code, and shipped in the same distribution. It would be up to the consumer of your component library to decide which build to use. +For using Stencil components in other projects there are two important output targets necessary: [`dist`](../output-targets/distribution.md) and [`dist-custom-elements`](../output-targets/custom-elements.md). Both export your components for different use cases. Luckily, all builds can be generated at the same time, using the same source code, and shipped in the same distribution. It would be up to the consumer of your component library to decide which build to use. ### Lazy Loading -If you prefer to have your components automatically loaded when used in your application, we recommend to enable the [`dist`](distribution) output target. The bundle gives you a small entry file that registers all your components and only loads the full component logic when it gets rendered in your application. It doesn't matter if the actual application is written in HTML or created with vanilla JavaScript, jQuery, React, etc. +If you prefer to have your components automatically loaded when used in your application, we recommend to enable the [`dist`](../output-targets/distribution.md) output target. The bundle gives you a small entry file that registers all your components and only loads the full component logic when it gets rendered in your application. It doesn't matter if the actual application is written in HTML or created with vanilla JavaScript, jQuery, React, etc. Your users can import your component library, e.g. called `my-design-system`, either via a `script` tag: @@ -37,7 +37,7 @@ To ensure that the right entry file is loaded when importing the project, define } ``` -Read more about various options when it comes to distributing your components lazily in the [`dist`](distribution) output target section. +Read more about various options when it comes to distributing your components lazily in the [`dist`](../output-targets/distribution.md) output target section. #### Considerations @@ -47,7 +47,7 @@ However be aware that this approach is not ideal in all cases. It requires your ### Standalone -The [`dist-custom-elements`](custom-elements) output target builds each component as a stand-alone class that extends `HTMLElement`. The output is a standardized custom element with the styles already attached and without any of Stencil's lazy-loading. This may be preferred for projects that are already handling bundling, lazy-loading and defining the custom elements themselves. +The [`dist-custom-elements`](../output-targets/custom-elements.md) output target builds each component as a stand-alone class that extends `HTMLElement`. The output is a standardized custom element with the styles already attached and without any of Stencil's lazy-loading. This may be preferred for projects that are already handling bundling, lazy-loading and defining the custom elements themselves. You can use these standalone components by importing them via: @@ -92,7 +92,7 @@ import 'my-design-system/my-component' If you are distributing both the `dist` and `dist-custom-elements`, then it's best to pick one of them as the main entry depending on which use case is more prominent. ::: -Read more about various options when it comes to distributing your components as standalone components in the [`dist-custom-elements`](custom-elements) output target section. +Read more about various options when it comes to distributing your components as standalone components in the [`dist-custom-elements`](../output-targets/custom-elements.md) output target section. #### Considerations From e597aa0a7f1c48cb3e2360b02dd551fbdfb8f642 Mon Sep 17 00:00:00 2001 From: Christian Bromann <git@bromann.dev> Date: Fri, 23 Feb 2024 10:36:23 -0800 Subject: [PATCH 04/10] PR feedback --- docs/guides/publishing.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/guides/publishing.md b/docs/guides/publishing.md index 82180b790..38b7d5456 100644 --- a/docs/guides/publishing.md +++ b/docs/guides/publishing.md @@ -9,11 +9,11 @@ There are numerous strategies to publish and distribute your component library t ## Use Cases -For using Stencil components in other projects there are two important output targets necessary: [`dist`](../output-targets/distribution.md) and [`dist-custom-elements`](../output-targets/custom-elements.md). Both export your components for different use cases. Luckily, all builds can be generated at the same time, using the same source code, and shipped in the same distribution. It would be up to the consumer of your component library to decide which build to use. +To use your Stencil components in other projects, there are two different output targets to consider: [`dist`](../output-targets/dist.md) and [`dist-custom-elements`](../output-targets/custom-elements.md). Both export your components for different use cases. Luckily, both can be generated at the same time, using the same source code, and shipped in the same distribution. It would be up to the consumer of your component library to decide which build to use. ### Lazy Loading -If you prefer to have your components automatically loaded when used in your application, we recommend to enable the [`dist`](../output-targets/distribution.md) output target. The bundle gives you a small entry file that registers all your components and only loads the full component logic when it gets rendered in your application. It doesn't matter if the actual application is written in HTML or created with vanilla JavaScript, jQuery, React, etc. +If you prefer to have your components automatically loaded when used in your application, we recommend to enable the [`dist`](../output-targets/dist.md) output target. The bundle gives you a small entry file that registers all your components and only loads the full component logic when it gets rendered in your application. It doesn't matter if the actual application is written in HTML or created with vanilla JavaScript, jQuery, React, etc. Your users can import your component library, e.g. called `my-design-system`, either via a `script` tag: @@ -37,11 +37,11 @@ To ensure that the right entry file is loaded when importing the project, define } ``` -Read more about various options when it comes to distributing your components lazily in the [`dist`](../output-targets/distribution.md) output target section. +Read more about various options when it comes to configuring your project's components for lazy loading in the [`dist`](../output-targets/distribution.md) output target section. #### Considerations -To start, Stencil was designed to lazy-load itself only when the component was actually used on a page. There are many benefits to this approach, such as simply adding a script tag to any page and the entire library is available for use, yet only the components actually used are downloaded. For example, [`@ionic/core`](https://www.npmjs.com/package/@ionic/core) comes with over 100 components, but a one webpage may only need `ion-toggle`. Instead of requesting the entire component library, or generating a custom bundle for just `ion-toggle`, the `dist` output target is able to generate a tiny entry build ready to load any of its components on-demand. +To start, Stencil was designed to lazy-load itself only when the component was actually used on a page. There are many benefits to this approach, such as simply adding a script tag to any page and the entire library is available for use, yet only the components actually used are downloaded. For example, [`@ionic/core`](https://www.npmjs.com/package/@ionic/core) comes with over 100 components, but a webpage may only need `ion-toggle`. Instead of requesting the entire component library, or generating a custom bundle for just `ion-toggle`, the `dist` output target is able to generate a tiny entry build ready to load any of its components on-demand. However be aware that this approach is not ideal in all cases. It requires your application to ship the bundled components as static assets in order for them to load properly. Furthermore, having many nested component dependencies can have an impact on the performance of your application. For example, given you have a component `CmpA` which uses a Stencil component `CmpB` which itself uses another Stencil component `CmpC`. In order to fully render `CmpA` the browser has to load 3 scripts sequentially which can result in undesired rendering delays. @@ -100,7 +100,7 @@ The `dist-custom-elements` is a direct build of the custom element that extends #### Usage in TypeScript -If you plan to support consuming your component library in TypeScript you'll need to set `generateTypeDeclarations: true` on the your output target in `stencil.config.ts`, like so: +If you plan to support consuming your component library in TypeScript you'll need to set `generateTypeDeclarations: true` on the output target in your `stencil.config.ts`, like so: ```tsx title="stencil.config.ts" import { Config } from '@stencil/core'; @@ -119,7 +119,7 @@ export const config: Config = { Then you can set the `types` property in `package.json` so that consumers of your package can find the type definitions, like so: -```tsx title="package.json" +```json title="package.json" { "types": "dist/components/index.d.ts", "dependencies": { @@ -135,4 +135,4 @@ If you set the `dir` property on the output target config, replace `dist/compone ## Publishing to NPM -The first step and highly recommended step is to [publish the component library to NPM](https://docs.npmjs.com/getting-started/publishing-npm-packages). [NPM](https://www.npmjs.com/) is an online software registry for sharing libraries, tools, utilities, packages, etc. Once the library is published to NPM, other projects are able to add your component library as a dependency and use the components within their own projects. +[NPM](https://www.npmjs.com/) is an online software registry for sharing libraries, tools, utilities, packages, etc. To make your Stencil project widely available to be consumed, it's recommended to [publish the component library to NPM](https://docs.npmjs.com/getting-started/publishing-npm-packages). Once the library is published to NPM, other projects are able to add your component library as a dependency and use the components within their own projects. From 37e36421a173432d58f565af231c4be8138bf35e Mon Sep 17 00:00:00 2001 From: Christian Bromann <git@bromann.dev> Date: Fri, 23 Feb 2024 10:37:38 -0800 Subject: [PATCH 05/10] fix link --- docs/guides/publishing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guides/publishing.md b/docs/guides/publishing.md index 38b7d5456..04d48a404 100644 --- a/docs/guides/publishing.md +++ b/docs/guides/publishing.md @@ -37,7 +37,7 @@ To ensure that the right entry file is loaded when importing the project, define } ``` -Read more about various options when it comes to configuring your project's components for lazy loading in the [`dist`](../output-targets/distribution.md) output target section. +Read more about various options when it comes to configuring your project's components for lazy loading in the [`dist`](../output-targets/dist.md) output target section. #### Considerations From 78406c92cb45ab5b9e9ea3cd438a0007b01e3706 Mon Sep 17 00:00:00 2001 From: Christian Bromann <git@bromann.dev> Date: Wed, 28 Feb 2024 09:52:25 -0800 Subject: [PATCH 06/10] Update docs/guides/publishing.md Co-authored-by: Ryan Waskiewicz <ryanwaskiewicz@gmail.com> --- docs/guides/publishing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guides/publishing.md b/docs/guides/publishing.md index 04d48a404..8353885bb 100644 --- a/docs/guides/publishing.md +++ b/docs/guides/publishing.md @@ -82,7 +82,7 @@ To ensure that the right entry file is loaded when importing the project, define } ``` -If you define exports targets for all your components as shown above and by using `customElementsExportBehavior: 'auto-define-custom-elements'` as output target option, you can skip the `defineCustomElement` call and directly import the component where you need it: +If you define exports targets for all your components as shown above and by using [`customElementsExportBehavior: 'auto-define-custom-elements'`](../docs/custom-elements.md#customelementsexportbehavior) as output target option, you can skip the `defineCustomElement` call and directly import the component where you need it: ```ts import 'my-design-system/my-component' From a3ee020495d912357b7e5556362f16f76d906203 Mon Sep 17 00:00:00 2001 From: Christian Bromann <git@bromann.dev> Date: Wed, 28 Feb 2024 10:37:40 -0800 Subject: [PATCH 07/10] PR feedback --- docs/guides/publishing.md | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/docs/guides/publishing.md b/docs/guides/publishing.md index 8353885bb..c6b7c312e 100644 --- a/docs/guides/publishing.md +++ b/docs/guides/publishing.md @@ -49,6 +49,8 @@ However be aware that this approach is not ideal in all cases. It requires your The [`dist-custom-elements`](../output-targets/custom-elements.md) output target builds each component as a stand-alone class that extends `HTMLElement`. The output is a standardized custom element with the styles already attached and without any of Stencil's lazy-loading. This may be preferred for projects that are already handling bundling, lazy-loading and defining the custom elements themselves. +The generated files will each export a component class and will already have the styles bundled. However, this build does not define the custom elements or apply any polyfills. Static assets referenced within components will need to be set using `setAssetPath` (see [Making Assets Available](#making-assets-available)). + You can use these standalone components by importing them via: ```ts @@ -64,7 +66,7 @@ class MyCustomComponent extends MyComponent { define('my-custom-component', MyCustomComponent) ``` -To ensure that the right entry file is loaded when importing the project, define the following fields in your `package.json`: +To ensure that the right entry file is loaded when importing the project, define different [exports fields](https://nodejs.org/api/packages.html#exports) in your `package.json`: ```json { @@ -73,7 +75,7 @@ To ensure that the right entry file is loaded when importing the project, define "import": "./dist/components/index.js", "types": "./dist/components/index.d.ts" }, - "my-component": { + "./my-component": { "import": "./dist/components/my-component.js", "types": "./dist/components/my-component.d.ts" } @@ -82,6 +84,15 @@ To ensure that the right entry file is loaded when importing the project, define } ``` +This allows us to map certain import paths to specific components within our project and allows users to only import the component code they are interested in and reduce the amount of code that needs to downloaded by the browser, e.g.: + +```js +// this import loads all compiled components +import { MyComponent } from 'my-design-system' +// only import compiled code for MyComponent +import { MyComponent } from 'my-design-system/my-component' +``` + If you define exports targets for all your components as shown above and by using [`customElementsExportBehavior: 'auto-define-custom-elements'`](../docs/custom-elements.md#customelementsexportbehavior) as output target option, you can skip the `defineCustomElement` call and directly import the component where you need it: ```ts @@ -94,6 +105,16 @@ If you are distributing both the `dist` and `dist-custom-elements`, then it's be Read more about various options when it comes to distributing your components as standalone components in the [`dist-custom-elements`](../output-targets/custom-elements.md) output target section. +The output directory will also contain an `index.js` file which exports some helper methods by default. The contents of the file will look something like: + +```js +export { setAssetPath, setPlatformOptions } from '@stencil/core/internal/client'; +``` + +:::note +The contents may look different if [`customElementsExportBehavior`](#customelementsexportbehavior) is specified! +::: + #### Considerations The `dist-custom-elements` is a direct build of the custom element that extends `HTMLElement`, without any lazy-loading. This distribution strategy may be preferred for projects that use an external bundler such as [Vite](https://vitejs.dev/), [WebPack](https://webpack.js.org/) or [Rollup](https://rollupjs.org) to compile the application. They ensure that only the components used within your application are bundled into compilation. From 6a133c6cd6b302294f8f7055cb8eeec18049978d Mon Sep 17 00:00:00 2001 From: Christian Bromann <git@bromann.dev> Date: Wed, 28 Feb 2024 10:40:32 -0800 Subject: [PATCH 08/10] fix links --- docs/guides/publishing.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/guides/publishing.md b/docs/guides/publishing.md index c6b7c312e..d33076292 100644 --- a/docs/guides/publishing.md +++ b/docs/guides/publishing.md @@ -49,7 +49,7 @@ However be aware that this approach is not ideal in all cases. It requires your The [`dist-custom-elements`](../output-targets/custom-elements.md) output target builds each component as a stand-alone class that extends `HTMLElement`. The output is a standardized custom element with the styles already attached and without any of Stencil's lazy-loading. This may be preferred for projects that are already handling bundling, lazy-loading and defining the custom elements themselves. -The generated files will each export a component class and will already have the styles bundled. However, this build does not define the custom elements or apply any polyfills. Static assets referenced within components will need to be set using `setAssetPath` (see [Making Assets Available](#making-assets-available)). +The generated files will each export a component class and will already have the styles bundled. However, this build does not define the custom elements or apply any polyfills. Static assets referenced within components will need to be set using `setAssetPath` (see [Making Assets Available](../output-targets/custom-elements.md#making-assets-available)). You can use these standalone components by importing them via: @@ -93,7 +93,7 @@ import { MyComponent } from 'my-design-system' import { MyComponent } from 'my-design-system/my-component' ``` -If you define exports targets for all your components as shown above and by using [`customElementsExportBehavior: 'auto-define-custom-elements'`](../docs/custom-elements.md#customelementsexportbehavior) as output target option, you can skip the `defineCustomElement` call and directly import the component where you need it: +If you define exports targets for all your components as shown above and by using [`customElementsExportBehavior: 'auto-define-custom-elements'`](../output-targets/custom-elements.md#customelementsexportbehavior) as output target option, you can skip the `defineCustomElement` call and directly import the component where you need it: ```ts import 'my-design-system/my-component' @@ -112,7 +112,7 @@ export { setAssetPath, setPlatformOptions } from '@stencil/core/internal/client' ``` :::note -The contents may look different if [`customElementsExportBehavior`](#customelementsexportbehavior) is specified! +The contents may look different if [`customElementsExportBehavior`](../output-targets/custom-elements.md#customelementsexportbehavior) is specified! ::: #### Considerations From 3b98c019b09fe672de57e23e427b9159715229e5 Mon Sep 17 00:00:00 2001 From: Christian Bromann <git@bromann.dev> Date: Fri, 1 Mar 2024 12:52:59 -0800 Subject: [PATCH 09/10] Update docs/guides/publishing.md Co-authored-by: Alice Pote <alice.writes.wrongs@gmail.com> --- docs/guides/publishing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guides/publishing.md b/docs/guides/publishing.md index d33076292..1c4acdf01 100644 --- a/docs/guides/publishing.md +++ b/docs/guides/publishing.md @@ -13,7 +13,7 @@ To use your Stencil components in other projects, there are two different output ### Lazy Loading -If you prefer to have your components automatically loaded when used in your application, we recommend to enable the [`dist`](../output-targets/dist.md) output target. The bundle gives you a small entry file that registers all your components and only loads the full component logic when it gets rendered in your application. It doesn't matter if the actual application is written in HTML or created with vanilla JavaScript, jQuery, React, etc. +If you prefer to have your components automatically loaded when used in your application, we recommend enabling the [`dist`](../output-targets/dist.md) output target. The bundle gives you a small entry file that registers all your components and defers loading the full component logic until it is rendered in your application. It doesn't matter if the actual application is written in HTML or created with vanilla JavaScript, jQuery, React, etc. Your users can import your component library, e.g. called `my-design-system`, either via a `script` tag: From 65d5bc68690904b2984b2951e1b08a233adc3a90 Mon Sep 17 00:00:00 2001 From: Christian Bromann <git@bromann.dev> Date: Mon, 11 Mar 2024 08:59:03 -0700 Subject: [PATCH 10/10] add note to externalRuntime option --- docs/output-targets/custom-elements.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/output-targets/custom-elements.md b/docs/output-targets/custom-elements.md index be15d04db..b6fed7bbe 100644 --- a/docs/output-targets/custom-elements.md +++ b/docs/output-targets/custom-elements.md @@ -97,6 +97,8 @@ Setting this flag to `true` results in the following behaviors: 2. Filenames will not be hashed. 3. All imports from packages under `@stencil/core/*` will be marked as external and therefore not included in the generated Rollup bundle. +Ensure that `@stencil/core` is included in your list of dependencies if you set this option to `true`. This is crucial to prevent any runtime errors. + ### generateTypeDeclarations _default: `true`_