diff --git a/.scripts/gen-component-declaration.mjs b/.scripts/gen-component-declaration.mjs new file mode 100644 index 000000000..404cb867a --- /dev/null +++ b/.scripts/gen-component-declaration.mjs @@ -0,0 +1,77 @@ +// The file is not designed to run directly. `cwd` should be project root. +import fs from 'fs-extra' +import path from 'path' +import process from 'process' + +const componentDirectory = './src/components'; + +// Components to be ignored for creating volar type +const IGNORE = [ + "FieldBody", + "SliderThumb", + "TableMobileSort", + "TablePagination", + "PaginationButton", + "DatepickerTable", + "DatepickerTableRow", + "DatepickerMonth", + "PickerWrapper", + "NotificationNotice", +]; + +const TYPE_ROOT = process.cwd() + +function getComponents(dir) { + const files = fs.readdirSync(dir, { recursive: true }); + return files + // filter only vue files and remove test files + .filter(f => f.includes(".vue") && !f.includes("tests")) + // remove path + .map(f => f.includes("\\") ? f.substring(f.lastIndexOf("\\") + 1) : f) + // remove .vue suffix + .map(f => f.substring(0, f.indexOf(".vue"))) + // filter blacklist + .filter((key) => !IGNORE.includes(key)) +} + +function exist (path) { + return fs.existsSync(path) +} + +function generateComponentsType () { + if(!exist(path.resolve(TYPE_ROOT, componentDirectory))) + throw new Error("Path not exist: " + componentDirectory); + + const globalComponents = getComponents(componentDirectory); + + const components = {} + globalComponents + // add global O prefix + .map((dir) => "O" + dir) + // add type declaration + .forEach((key) => { + components[key] = `(typeof import("@oruga-ui/oruga-next"))["${key}"];`; + }); + + const lines = Object.entries(components) + .filter(([name]) => components[name]) + .map(([name, v]) => { + if (!/^\w+$/.test(name)) { + name = `'${name}'` + } + return `${name}: ${v}` + }); + + const code = `// Auto generated component declarations +declare module "vue" { + export interface GlobalComponents { + ${lines.join('\n ')} + } +} +export {}; +`; + + fs.writeFileSync(path.resolve(TYPE_ROOT, 'volar.d.ts'), code, 'utf-8') +} + +generateComponentsType() diff --git a/packages/docs-next/documentation/index.md b/packages/docs-next/documentation/index.md index 5661a6190..c47112e7f 100644 --- a/packages/docs-next/documentation/index.md +++ b/packages/docs-next/documentation/index.md @@ -105,6 +105,20 @@ yarn add @oruga-ui/theme-oruga @import '@oruga-ui/theme-oruga/dist/scss/oruga-full.scss'; ``` +### Volar support +If you are using Volar, you can specify global component types by configuring `compilerOptions.types` in `tsconfig.json`. + +```js +// tsconfig.json +{ + "compilerOptions": { + // ... + "types": ["@oruga-ui/oruga-next/volar"] + } +} +``` + + ## Nuxt module Oruga doesn't provide a [Nuxt.js](https://nuxtjs.org) module at the moment. diff --git a/packages/oruga-next/package.json b/packages/oruga-next/package.json index c709c3178..7dd57b3ba 100644 --- a/packages/oruga-next/package.json +++ b/packages/oruga-next/package.json @@ -23,6 +23,7 @@ "src", "nuxt", "types/*.d.ts", + "volar.d.ts", "README.md" ], "sideEffects": [ @@ -40,7 +41,7 @@ "dev": "vite", "build:vue": "rimraf dist && vite build && vite build --mode minify", "build:vue:watch": "vite build --mode minify --watch", - "build:lib": "rimraf dist && npm run test:ts && npm run build:vue", + "build:lib": "rimraf dist && npm run test:ts && npm run build:vue && npm run gen-volar-dts", "build:lib:watch": "npm link && npm run build:vue:watch", "publish:lib": "cp ../../README.md . && npm run build:lib && npm publish", "test": "rm -rf .nyc_output coverage && NODE_ENV=test cypress run --component", @@ -48,6 +49,7 @@ "test:ts": "vue-tsc --noEmit --skipLibCheck", "test:watch": "rm -rf .nyc_output coverage && NODE_ENV=test cypress open --component", "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --quiet --ignore-path .gitignore", + "gen-volar-dts": "node ../../.scripts/gen-component-declaration.mjs --bundle --platform=node", "update": "ncu -u" }, "keywords": [ diff --git a/packages/oruga-next/volar.d.ts b/packages/oruga-next/volar.d.ts new file mode 100644 index 000000000..bc1d2bc7b --- /dev/null +++ b/packages/oruga-next/volar.d.ts @@ -0,0 +1,42 @@ +// Auto generated component declarations +declare module "vue" { + export interface GlobalComponents { + OAutocomplete: (typeof import("@oruga-ui/oruga-next"))["OAutocomplete"]; + OButton: (typeof import("@oruga-ui/oruga-next"))["OButton"]; + OCarousel: (typeof import("@oruga-ui/oruga-next"))["OCarousel"]; + OCarouselItem: (typeof import("@oruga-ui/oruga-next"))["OCarouselItem"]; + OCheckbox: (typeof import("@oruga-ui/oruga-next"))["OCheckbox"]; + OCollapse: (typeof import("@oruga-ui/oruga-next"))["OCollapse"]; + ODatepicker: (typeof import("@oruga-ui/oruga-next"))["ODatepicker"]; + ODatetimepicker: (typeof import("@oruga-ui/oruga-next"))["ODatetimepicker"]; + ODropdown: (typeof import("@oruga-ui/oruga-next"))["ODropdown"]; + ODropdownItem: (typeof import("@oruga-ui/oruga-next"))["ODropdownItem"]; + OField: (typeof import("@oruga-ui/oruga-next"))["OField"]; + OIcon: (typeof import("@oruga-ui/oruga-next"))["OIcon"]; + OInput: (typeof import("@oruga-ui/oruga-next"))["OInput"]; + OLoading: (typeof import("@oruga-ui/oruga-next"))["OLoading"]; + OMenu: (typeof import("@oruga-ui/oruga-next"))["OMenu"]; + OMenuItem: (typeof import("@oruga-ui/oruga-next"))["OMenuItem"]; + OModal: (typeof import("@oruga-ui/oruga-next"))["OModal"]; + ONotification: (typeof import("@oruga-ui/oruga-next"))["ONotification"]; + OPagination: (typeof import("@oruga-ui/oruga-next"))["OPagination"]; + ORadio: (typeof import("@oruga-ui/oruga-next"))["ORadio"]; + OSelect: (typeof import("@oruga-ui/oruga-next"))["OSelect"]; + OSidebar: (typeof import("@oruga-ui/oruga-next"))["OSidebar"]; + OSkeleton: (typeof import("@oruga-ui/oruga-next"))["OSkeleton"]; + OSlider: (typeof import("@oruga-ui/oruga-next"))["OSlider"]; + OSliderTick: (typeof import("@oruga-ui/oruga-next"))["OSliderTick"]; + OStepItem: (typeof import("@oruga-ui/oruga-next"))["OStepItem"]; + OSteps: (typeof import("@oruga-ui/oruga-next"))["OSteps"]; + OSwitch: (typeof import("@oruga-ui/oruga-next"))["OSwitch"]; + OTable: (typeof import("@oruga-ui/oruga-next"))["OTable"]; + OTableColumn: (typeof import("@oruga-ui/oruga-next"))["OTableColumn"]; + OTabItem: (typeof import("@oruga-ui/oruga-next"))["OTabItem"]; + OTabs: (typeof import("@oruga-ui/oruga-next"))["OTabs"]; + OTaginput: (typeof import("@oruga-ui/oruga-next"))["OTaginput"]; + OTimepicker: (typeof import("@oruga-ui/oruga-next"))["OTimepicker"]; + OTooltip: (typeof import("@oruga-ui/oruga-next"))["OTooltip"]; + OUpload: (typeof import("@oruga-ui/oruga-next"))["OUpload"]; + } +} +export {};