From 8061bb2ae936a6ea2522dbb58cb740625c1b0685 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 19 Nov 2024 10:21:21 +0100 Subject: [PATCH 01/19] chore(deps): bump vitepress from 1.4.3 to 1.5.0 (#3091) Bumps [vitepress](https://github.com/vuejs/vitepress) from 1.4.3 to 1.5.0. - [Release notes](https://github.com/vuejs/vitepress/releases) - [Changelog](https://github.com/vuejs/vitepress/blob/main/CHANGELOG.md) - [Commits](https://github.com/vuejs/vitepress/compare/v1.4.3...v1.5.0) --- updated-dependencies: - dependency-name: vitepress dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pnpm-lock.yaml | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 009f95c7..eae4a2e2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13,7 +13,7 @@ importers: version: 4.4.2 '@vue/theme': specifier: ^2.3.0 - version: 2.3.0(@algolia/client-search@4.24.0)(search-insights@2.17.2)(vitepress@1.4.3(@algolia/client-search@4.24.0)(@types/node@22.7.5)(postcss@8.4.47)(search-insights@2.17.2)(typescript@5.6.3))(vue@3.5.12(typescript@5.6.3)) + version: 2.3.0(@algolia/client-search@4.24.0)(search-insights@2.17.2)(vitepress@1.5.0(@algolia/client-search@4.24.0)(@types/node@22.7.5)(postcss@8.4.47)(search-insights@2.17.2)(typescript@5.6.3))(vue@3.5.12(typescript@5.6.3)) dynamics.js: specifier: ^1.1.5 version: 1.1.5 @@ -22,7 +22,7 @@ importers: version: 3.12.5 vitepress: specifier: ^1.4.3 - version: 1.4.3(@algolia/client-search@4.24.0)(@types/node@22.7.5)(postcss@8.4.47)(search-insights@2.17.2)(typescript@5.6.3) + version: 1.5.0(@algolia/client-search@4.24.0)(@types/node@22.7.5)(postcss@8.4.47)(search-insights@2.17.2)(typescript@5.6.3) vue: specifier: ^3.5.12 version: 3.5.12(typescript@5.6.3) @@ -288,6 +288,12 @@ packages: cpu: [x64] os: [win32] + '@iconify-json/simple-icons@1.2.10': + resolution: {integrity: sha512-9OK1dsSjXlH36lhu5n+BlSoXuqFjHUErGLtNdzHpq0vHq4YFBuGYWtZ+vZTHLreRQ8ijPRv/6EsgkV+nf6AReQ==} + + '@iconify/types@2.0.0': + resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} + '@jridgewell/sourcemap-codec@1.5.0': resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} @@ -827,8 +833,8 @@ packages: terser: optional: true - vitepress@1.4.3: - resolution: {integrity: sha512-956c2K2Mr0ubY9bTc2lCJD3g0mgo0mARB1iJC/BqUt4s0AM8Wl60wSU4zbFnzV7X2miFK1XJDKzGZnuEN90umw==} + vitepress@1.5.0: + resolution: {integrity: sha512-q4Q/G2zjvynvizdB3/bupdYkCJe2umSAMv9Ju4d92E6/NXJ59z70xB0q5p/4lpRyAwflDsbwy1mLV9Q5+nlB+g==} hasBin: true peerDependencies: markdown-it-mathjax3: ^4 @@ -1083,6 +1089,12 @@ snapshots: '@esbuild/win32-x64@0.21.5': optional: true + '@iconify-json/simple-icons@1.2.10': + dependencies: + '@iconify/types': 2.0.0 + + '@iconify/types@2.0.0': {} + '@jridgewell/sourcemap-codec@1.5.0': {} '@rollup/rollup-android-arm-eabi@4.24.0': @@ -1304,7 +1316,7 @@ snapshots: '@vue/shared@3.5.12': {} - '@vue/theme@2.3.0(@algolia/client-search@4.24.0)(search-insights@2.17.2)(vitepress@1.4.3(@algolia/client-search@4.24.0)(@types/node@22.7.5)(postcss@8.4.47)(search-insights@2.17.2)(typescript@5.6.3))(vue@3.5.12(typescript@5.6.3))': + '@vue/theme@2.3.0(@algolia/client-search@4.24.0)(search-insights@2.17.2)(vitepress@1.5.0(@algolia/client-search@4.24.0)(@types/node@22.7.5)(postcss@8.4.47)(search-insights@2.17.2)(typescript@5.6.3))(vue@3.5.12(typescript@5.6.3))': dependencies: '@docsearch/css': 3.6.2 '@docsearch/js': 3.6.2(@algolia/client-search@4.24.0)(search-insights@2.17.2) @@ -1312,7 +1324,7 @@ snapshots: body-scroll-lock: 4.0.0-beta.0 normalize.css: 8.0.1 tiny-decode: 0.1.3 - vitepress: 1.4.3(@algolia/client-search@4.24.0)(@types/node@22.7.5)(postcss@8.4.47)(search-insights@2.17.2)(typescript@5.6.3) + vitepress: 1.5.0(@algolia/client-search@4.24.0)(@types/node@22.7.5)(postcss@8.4.47)(search-insights@2.17.2)(typescript@5.6.3) transitivePeerDependencies: - '@algolia/client-search' - '@types/react' @@ -1669,10 +1681,11 @@ snapshots: '@types/node': 22.7.5 fsevents: 2.3.3 - vitepress@1.4.3(@algolia/client-search@4.24.0)(@types/node@22.7.5)(postcss@8.4.47)(search-insights@2.17.2)(typescript@5.6.3): + vitepress@1.5.0(@algolia/client-search@4.24.0)(@types/node@22.7.5)(postcss@8.4.47)(search-insights@2.17.2)(typescript@5.6.3): dependencies: '@docsearch/css': 3.6.2 '@docsearch/js': 3.6.2(@algolia/client-search@4.24.0)(search-insights@2.17.2) + '@iconify-json/simple-icons': 1.2.10 '@shikijs/core': 1.22.2 '@shikijs/transformers': 1.22.2 '@shikijs/types': 1.22.2 From b266b8191d62a21d71e2f619f2543cff4fcbd086 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Isma=C3=ABl=20Maurice?= <55036198+tisma95@users.noreply.github.com> Date: Tue, 19 Nov 2024 10:21:56 +0100 Subject: [PATCH 02/19] Update the transition group (#3092) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix(developpers): Remove duplicate * Fix(transition-group): Add relativ url and anchor --------- Co-authored-by: Tuo N. Ismaël --- src/guide/built-ins/transition-group.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/guide/built-ins/transition-group.md b/src/guide/built-ins/transition-group.md index 0fa3ce24..030745b1 100644 --- a/src/guide/built-ins/transition-group.md +++ b/src/guide/built-ins/transition-group.md @@ -80,9 +80,9 @@ Now it looks much better - even animating smoothly when the whole list is shuffl [Full Example](/examples/#list-transition) -### Custom TransitionGroup classes +### Custom TransitionGroup classes {#custom-transitiongroup-classes} -You can also specify custom transition classes for the moving element by passing the `moveClass` prop to ``, just like [custom transition classes on ``](https://vuejs.org/guide/built-ins/transition.html#custom-transition-classes). +You can also specify custom transition classes for the moving element by passing the `moveClass` prop to ``, just like [custom transition classes on ``](/guide/built-ins/transition.html#custom-transition-classes). ## Staggering List Transitions {#staggering-list-transitions} From 6e1090cb895c206e4634811ca236740b8f940f60 Mon Sep 17 00:00:00 2001 From: Nick Mousavi <52695644+biomousavi@users.noreply.github.com> Date: Tue, 19 Nov 2024 13:23:15 +0400 Subject: [PATCH 03/19] docs: align custom-directive example with content (#3093) docs: align example description with content --- src/guide/reusability/custom-directives.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/guide/reusability/custom-directives.md b/src/guide/reusability/custom-directives.md index 6b6aa435..a8b51935 100644 --- a/src/guide/reusability/custom-directives.md +++ b/src/guide/reusability/custom-directives.md @@ -25,7 +25,7 @@ In addition to the default set of directives shipped in core (like `v-model` or We have introduced two forms of code reuse in Vue: [components](/guide/essentials/component-basics) and [composables](./composables). Components are the main building blocks, while composables are focused on reusing stateful logic. Custom directives, on the other hand, are mainly intended for reusing logic that involves low-level DOM access on plain elements. -A custom directive is defined as an object containing lifecycle hooks similar to those of a component. The hooks receive the element the directive is bound to. Here is an example of a directive that focuses an input when the element is inserted into the DOM by Vue: +A custom directive is defined as an object containing lifecycle hooks similar to those of a component. The hooks receive the element the directive is bound to. Here is an example of a directive that adds a class to an element when it is inserted into the DOM by Vue:
From 1cea4314f762939ab2c52ecfb20cafb6eb40e16d Mon Sep 17 00:00:00 2001 From: Joe Pea Date: Tue, 19 Nov 2024 01:28:38 -0800 Subject: [PATCH 04/19] Corrected and improved docs on type definitions for Custom Elements. (#3073) Corrected and improved documentation on type definitions for Custom Elements. The doc was incorrect for two reasons: - For Vue-based custom elements, the types passed into `GlobalComponents` need to be the Vue component types, not the custom element types, or the custom elements will not be type checked in Vue templates. - The name of the elements need to have at least two words and a hyphen, otherwise with single words the custom elements will not work at all. --- src/guide/extras/web-components.md | 270 +++++++++++++++++++++++++++-- 1 file changed, 259 insertions(+), 11 deletions(-) diff --git a/src/guide/extras/web-components.md b/src/guide/extras/web-components.md index 6bf9a5de..75b72bbc 100644 --- a/src/guide/extras/web-components.md +++ b/src/guide/extras/web-components.md @@ -220,6 +220,8 @@ If the custom elements will be used in an application that is also using Vue, yo It is recommended to export the individual element constructors to give your users the flexibility to import them on-demand and register them with desired tag names. You can also export a convenience function to automatically register all elements. Here's an example entry point of a Vue custom element library: ```js +// elements.js + import { defineCustomElement } from 'vue' import Foo from './MyFoo.ce.vue' import Bar from './MyBar.ce.vue' @@ -236,31 +238,277 @@ export function register() { } ``` -If you have many components, you can also leverage build tool features such as Vite's [glob import](https://vitejs.dev/guide/features.html#glob-import) or webpack's [`require.context`](https://webpack.js.org/guides/dependency-management/#requirecontext) to load all components from a directory. +A consumer can use the elements in a Vue file, + +```vue + + + +``` + +or in any other framework such as one with JSX, and with custom names: -### Web Components and TypeScript {#web-components-and-typescript} +```jsx +import { MyFoo, MyBar } from 'path/to/elements.js' + +customElements.define('some-foo', MyFoo) +customElements.define('some-bar', MyBar) + +export function MyComponent() { + return <> + + + + +} +``` -If you are developing an application or a library, you may want to [type check](/guide/scaling-up/tooling.html#typescript) your Vue components, including those that are defined as custom elements. +### Vue-based Web Components and TypeScript {#web-components-and-typescript} -Custom elements are registered globally using native APIs, so by default they won't have type inference when used in Vue templates. To provide type support for Vue components registered as custom elements, we can register global component typings using the the [`GlobalComponents` interface](https://github.com/vuejs/language-tools/blob/master/packages/vscode-vue/README.md#usage) in Vue templates and/or in [JSX](https://www.typescriptlang.org/docs/handbook/jsx.html#intrinsic-elements): +When writing Vue SFC templates, you may want to [type check](/guide/scaling-up/tooling.html#typescript) your Vue components, including those that are defined as custom elements. + +Custom elements are registered globally in browsers using their built-in APIs, and by default they won't have type inference when used in Vue templates. To provide type support for Vue components registered as custom elements, we can register global component typings by augmenting the [`GlobalComponents` interface](https://github.com/vuejs/language-tools/blob/master/packages/vscode-vue/README.md#usage) for type checking in Vue templates (JSX users can augment the [JSX.IntrinsicElements](https://www.typescriptlang.org/docs/handbook/jsx.html#intrinsic-elements) type instead, which is not shown here). + +Here is how to define the type for a custom element made with Vue: ```typescript import { defineCustomElement } from 'vue' -// vue SFC -import CounterSFC from './src/components/counter.ce.vue' +// Import the Vue component. +import SomeComponent from './src/components/SomeComponent.ce.vue' + +// Turn the Vue component into a Custom Element class. +export const SomeElement = defineCustomElement(SomeComponent) + +// Remember to register the element class with the browser. +customElements.define('some-element', SomeElement) + +// Add the new element type to Vue's GlobalComponents type. +declare module 'vue' { + interface GlobalComponents { + // Be sure to pass in the Vue component type here (SomeComponent, *not* SomeElement). + // Custom Elements require a hyphen in their name, so use the hyphenated element name here. + 'some-element': typeof SomeComponent + } +} +``` + +## Non-Vue Web Components and TypeScript + +Here is the recommended way to enable type checking in SFC templates of Custom +Elements that are not built with Vue. + +> [!Note] +> This approach is one possible way to do it, but it may vary depending on the +> framework being used to create the custom elements. + +Suppose we have a custom element with some JS properties and events defined, and +it is shipped in a library called `some-lib`: + +```ts +// file: some-lib/src/SomeElement.ts + +// Define a class with typed JS properties. +export class SomeElement extends HTMLElement { + foo: number = 123 + bar: string = 'blah' + + lorem: boolean = false + + // This method should not be exposed to template types. + someMethod() { + /* ... */ + } + + // ... implementation details omitted ... + // ... assume the element dispatches events named "apple-fell" ... +} + +customElements.define('some-element', SomeElement) + +// This is a list of properties of SomeElement that will be selected for type +// checking in framework templates (f.e. Vue SFC templates). Any other +// properties will not be exposed. +export type SomeElementAttributes = 'foo' | 'bar' + +// Define the event types that SomeElement dispatches. +export type SomeElementEvents = { + 'apple-fell': AppleFellEvent +} + +export class AppleFellEvent extends Event { + /* ... details omitted ... */ +} +``` + +The implementation details have been omitted, but the important part is that we +have type definitions for two things: prop types and event types. + +Let's create a type helper for easily registering custom element type +definitions in Vue: + +```ts +// file: some-lib/src/DefineCustomElement.ts + +// We can re-use this type helper per each element we need to define. +type DefineCustomElement< + ElementType extends HTMLElement, + Events extends EventMap = {}, + SelectedAttributes extends keyof ElementType = keyof ElementType +> = new () => ElementType & { + // Use $props to define the properties exposed to template type checking. Vue + // specifically reads prop definitions from the `$props` type. Note that we + // combine the element's props with the global HTML props and Vue's special + // props. + /** @deprecated Do not use the $props property on a Custom Element ref, this is for template prop types only. */ + $props: HTMLAttributes & + Partial> & + PublicProps + + // Use $emit to specifically define event types. Vue specifically reads event + // types from the `$emit` type. Note that `$emit` expects a particular format + // that we map `Events` to. + /** @deprecated Do not use the $emit property on a Custom Element ref, this is for template prop types only. */ + $emit: VueEmit +} + +type EventMap = { + [event: string]: Event +} + +// This maps an EventMap to the format that Vue's $emit type expects. +type VueEmit = EmitFn<{ + [K in keyof T]: (event: T[K]) => void +}> +``` + +> [!Note] +> We marked `$props` and `$emit` as deprecated so that when we get a `ref` to a +> custom element we will not be tempted to use these properties, as these +> properties are for type checking purposes only when it comes to custom elements. +> These properties do not actually exist on the custom element instances. + +Using the type helper we can now select the JS properties that should be exposed +for type checking in Vue templates: + +```ts +// file: some-lib/src/SomeElement.vue.ts + +import { + SomeElement, + SomeElementAttributes, + SomeElementEvents +} from './SomeElement.js' +import type { Component } from 'vue' +import type { DefineCustomElement } from './DefineCustomElement' + +// Add the new element type to Vue's GlobalComponents type. +declare module 'vue' { + interface GlobalComponents { + 'some-element': DefineCustomElement< + SomeElement, + SomeElementAttributes, + SomeElementEvents + > + } +} +``` -// turn component into web components -export const Counter = defineCustomElement(CounterSFC) +Suppose that `some-lib` builds its source TypeScript files into a `dist/` folder. A user of +`some-lib` can then import `SomeElement` and use it in a Vue SFC like so: -// register global typings +```vue + + + +``` + +If an element does not have type definitions, the types of the properties and events can be +defined in a more manual fashion: + +```vue + + + ``` +Custom Element authors should not automatically export framework-specific custom +element type definitions from their libraries, for example they should not +export them from an `index.ts` file that also exports the rest of the library, +otherwise users will have unexpected module augmentation errors. Users should +import the framework-specific type definition file that they need. + ## Web Components vs. Vue Components {#web-components-vs-vue-components} Some developers believe that framework-proprietary component models should be avoided, and that exclusively using Custom Elements makes an application "future-proof". Here we will try to explain why we believe that this is an overly simplistic take on the problem. From 0773ed439c142d140ac6eebbe858b250a73be441 Mon Sep 17 00:00:00 2001 From: Vincent Anjiri Date: Tue, 19 Nov 2024 12:46:44 +0300 Subject: [PATCH 05/19] Refactor api.data.ts: Add comments and improve code clarity (#3098) - Added comments to explain the purpose and functionality of key functions. - Clarified the logic behind header parsing, slug generation, and file caching. - Documented the use of regular expressions for header cleaning and anchor extraction. - Improved readability and maintainability of the codebase. --- src/api/api.data.ts | 146 ++++++++++++++++++++++++++------------------ 1 file changed, 88 insertions(+), 58 deletions(-) diff --git a/src/api/api.data.ts b/src/api/api.data.ts index 719054fd..5c4f023b 100644 --- a/src/api/api.data.ts +++ b/src/api/api.data.ts @@ -1,15 +1,16 @@ // api.data.ts -// a file ending with data.(j|t)s will be evaluated in Node.js import fs from 'fs' import path from 'path' import type { MultiSidebarConfig } from '@vue/theme/src/vitepress/config.ts' import { sidebar } from '../../.vitepress/config' +// Interface defining the structure of a single header in the API interface APIHeader { anchor: string text: string } +// Interface defining the structure of an API group with text, anchor, and items export interface APIGroup { text: string anchor: string @@ -20,79 +21,108 @@ export interface APIGroup { }[] } -// declare resolved data type +// Declare the resolved data type for API groups export declare const data: APIGroup[] -export default { - // declare files that should trigger HMR - watch: './*.md', - // read from fs and generate the data - load(): APIGroup[] { - return (sidebar as MultiSidebarConfig)['/api/'].map((group) => ({ - text: group.text, - anchor: slugify(group.text), - items: group.items.map((item) => ({ - ...item, - headers: parsePageHeaders(item.link) - })) - })) - } +// Utility function to generate a slug from a string (used for anchor links) +function slugify(text: string): string { + return ( + text + // Replace special characters and spaces with hyphens + .replace(/[\s~`!@#$%^&*()\-_+=[\]{}|\\;:"'<>,.?/]+/g, '-') + // Remove continuous separators + .replace(/-{2,}/g, '-') + // Remove leading/trailing hyphens + .replace(/^-+|-+$/g, '') + // Ensure it doesn't start with a number (e.g. #121) + .replace(/^(\d)/, '_$1') + // Convert to lowercase + .toLowerCase() + ) } -const headersCache = new Map< - string, - { - headers: APIHeader[] - timestamp: number - } ->() - -function parsePageHeaders(link: string) { - const fullPath = path.join(__dirname, '../', link) + '.md' - const timestamp = fs.statSync(fullPath).mtimeMs +// Utility function to parse headers from a markdown file at a given link +function parsePageHeaders(link: string): APIHeader[] { + const fullPath = path.join(__dirname, '../', link) + '.md' // Resolve the full file path + const timestamp = fs.statSync(fullPath).mtimeMs // Get the last modified timestamp of the file + // Check if the file is cached and if its timestamp matches const cached = headersCache.get(fullPath) if (cached && timestamp === cached.timestamp) { - return cached.headers + return cached.headers // Return cached headers if they're up-to-date } - const src = fs.readFileSync(fullPath, 'utf-8') - const h2s = src.match(/^## [^\n]+/gm) + const src = fs.readFileSync(fullPath, 'utf-8') // Read the markdown file + const headers = extractHeadersFromMarkdown(src) // Extract headers from the file content + + // Store the extracted headers along with the file's timestamp in the cache + headersCache.set(fullPath, { + timestamp, + headers + }) + + return headers +} + +// Helper function to extract all headers (h2) from markdown content +function extractHeadersFromMarkdown(src: string): APIHeader[] { + const h2s = src.match(/^## [^\n]+/gm) // Match all h2 headers (## header) + const anchorRE = /\{#([^}]+)\}/ // Regular expression to match the anchor link in header (e.g. {#some-anchor}) let headers: APIHeader[] = [] + if (h2s) { - const anchorRE = /\{#([^}]+)\}/ + // Process each h2 header and extract text and anchor headers = h2s.map((h) => { - const text = h - .slice(2) - .replace(/