diff --git a/packages/nutui-auto-import-resolver/README.md b/packages/nutui-auto-import-resolver/README.md new file mode 100644 index 0000000000..b5a749fdb2 --- /dev/null +++ b/packages/nutui-auto-import-resolver/README.md @@ -0,0 +1,165 @@ +# NutUI Auto Import Resolver + +English | [简体中文](./README.zh-CN.md) + +`@nutui/auto-import-resolver` is a resolver for [unplugin-vue-components](https://github.com/unplugin/unplugin-vue-components) that enables on-demand importing of NutUI components. + +### Features + +- Supports `Vite`, `Webpack`, `Vue CLI`, and more. +- Style files support CSS, SASS, default CSS +- Automatically imports the corresponding CSS styles for the components. + +### Installation + +```shell +# via pnpm +pnpm add @nutui/auto-import-resolver unplugin-vue-components -D + +# via npm +npm i @nutui/auto-import-resolver unplugin-vue-components -D + +# via yarn +yarn add @nutui/auto-import-resolver unplugin-vue-components -D + +# via Bun +bun add @nutui/auto-import-resolver unplugin-vue-components -D +``` + +## Usage Default + +### Vite + +```ts +// vite.config.ts +import Components from 'unplugin-vue-components/vite'; +import NutUIResolver from '@nutui/auto-import-resolver'; + +export default defineConfig({ + plugins: [ + Components({ + resolvers: [NutUIResolver()], + }), + ] +}); +``` + +### Vue CLI + +```ts +// vue.config.js +import Components from 'unplugin-vue-components/webpack'; +const NutUIResolver = require('@nutui/auto-import-resolver'); + +module.exports = { + configureWebpack: { + plugins: [ + Components({ + resolvers: [NutUIResolver()], + }), + ], + } +}; +``` + +### Webpack + +```ts +// webpack.config.js +import Components from 'unplugin-vue-components/webpack'; +const NutUIResolver = require('@nutui/auto-import-resolver'); + +module.exports = { + plugins: [ + Components({ + resolvers: [NutUIResolver()], + }), + ] +}; +``` + +## Usage Sass + +### Vite + +```ts +// vite.config.ts +import Components from 'unplugin-vue-components/vite'; +import NutUIResolver from '@nutui/auto-import-resolver'; + +export default defineConfig({ + plugins: [ + Components({ + resolvers: [NutUIResolver({ importStyle: 'sass' })] + }) + ], + // 配置全局样式变量 + css: { + preprocessorOptions: { + scss: { + additionalData: `@import "@nutui/nutui/dist/styles/variables.scss";` + } + } + } +}); +``` + +### Vue CLI + +```ts +// vue.config.js +import Components from 'unplugin-vue-components/webpack'; +const NutUIResolver = require('@nutui/auto-import-resolver'); + +module.exports = { + configureWebpack: { + plugins: [ + Components({ + resolvers: [NutUIResolver({ importStyle: 'sass' })] + }) + ] + }, + // 配置全局样式变量 + css: { + loaderOptions: { + scss: { + additionalData: `@import "@nutui/nutui/dist/styles/variables.scss";` + } + } + } +}; +``` + +### Webpack + +```ts +// webpack.config.js +import Components from 'unplugin-vue-components/webpack'; +const NutUIResolver = require('@nutui/auto-import-resolver'); + +module.exports = { + plugins: [ + Components({ + resolvers: [NutUIResolver({ importStyle: 'sass' })] + }) + ], + module: { + rules: [ + { + test: /\.s[ac]ss$/i, + use: [ + 'style-loader', + 'css-loader', + { + // 配置全局样式变量 + loader: 'sass-loader', + options: { + additionalData: `@import "@nutui/nutui/dist/styles/variables.scss";` + } + } + ] + } + ] + } +}; +``` \ No newline at end of file diff --git a/packages/nutui-auto-import-resolver/README.zh-CN.md b/packages/nutui-auto-import-resolver/README.zh-CN.md new file mode 100644 index 0000000000..d5d01a80b0 --- /dev/null +++ b/packages/nutui-auto-import-resolver/README.zh-CN.md @@ -0,0 +1,165 @@ +# Nutui Auto Import Resolver + +[English](./README.md) | 简体中文 + +`@nutui/auto-import-resolver` 是 [unplugin-vue-components](https://github.com/unplugin/unplugin-vue-components) 的一个解析器,用于实现 NutUI 按需引入。 + +### 特性 + +- 支持 `Vite`, `Webpack`, `Vue CLI` 等 +- 样式文件支持 CSS,SASS,默认 CSS +- 支持自动引入组件对应的 CSS 样式 + +### 安装 + +```shell +# via pnpm +pnpm add @nutui/auto-import-resolver unplugin-vue-components -D + +# via npm +npm i @nutui/auto-import-resolver unplugin-vue-components -D + +# via yarn +yarn add @nutui/auto-import-resolver unplugin-vue-components -D + +# via Bun +bun add @nutui/auto-import-resolver unplugin-vue-components -D +``` + +## 默认使用 + +### Vite + +```ts +// vite.config.ts +import Components from 'unplugin-vue-components/vite'; +import NutUIResolver from '@nutui/auto-import-resolver'; + +export default defineConfig({ + plugins: [ + Components({ + resolvers: [NutUIResolver()] + }) + ] +}); +``` + +### Vue CLI + +```ts +// vue.config.js +import Components from 'unplugin-vue-components/webpack'; +const NutUIResolver = require('@nutui/auto-import-resolver'); + +module.exports = { + configureWebpack: { + plugins: [ + Components({ + resolvers: [NutUIResolver()] + }) + ] + } +}; +``` + +### Webpack + +```ts +// webpack.config.js +import Components from 'unplugin-vue-components/webpack'; +const NutUIResolver = require('@nutui/auto-import-resolver'); + +module.exports = { + plugins: [ + Components({ + resolvers: [NutUIResolver()] + }) + ] +}; +``` + +## 使用 Sass + +### Vite + +```ts +// vite.config.ts +import Components from 'unplugin-vue-components/vite'; +import NutUIResolver from '@nutui/auto-import-resolver'; + +export default defineConfig({ + plugins: [ + Components({ + resolvers: [NutUIResolver({ importStyle: 'sass' })] + }) + ], + // 配置全局样式变量 + css: { + preprocessorOptions: { + scss: { + additionalData: `@import "@nutui/nutui/dist/styles/variables.scss";` + } + } + } +}); +``` + +### Vue CLI + +```ts +// vue.config.js +import Components from 'unplugin-vue-components/webpack'; +const NutUIResolver = require('@nutui/auto-import-resolver'); + +module.exports = { + configureWebpack: { + plugins: [ + Components({ + resolvers: [NutUIResolver({ importStyle: 'sass' })] + }) + ] + }, + // 配置全局样式变量 + css: { + loaderOptions: { + scss: { + additionalData: `@import "@nutui/nutui/dist/styles/variables.scss";` + } + } + } +}; +``` + +### Webpack + +```ts +// webpack.config.js +import Components from 'unplugin-vue-components/webpack'; +const NutUIResolver = require('@nutui/auto-import-resolver'); + +module.exports = { + plugins: [ + Components({ + resolvers: [NutUIResolver({ importStyle: 'sass' })] + }) + ], + module: { + rules: [ + { + test: /\.s[ac]ss$/i, + use: [ + 'style-loader', + 'css-loader', + { + // 配置全局样式变量 + loader: 'sass-loader', + options: { + additionalData: `@import "@nutui/nutui/dist/styles/variables.scss";` + } + } + ] + } + ] + } +}; +``` diff --git a/packages/nutui-auto-import-resolver/package.json b/packages/nutui-auto-import-resolver/package.json new file mode 100644 index 0000000000..0f5fd040ad --- /dev/null +++ b/packages/nutui-auto-import-resolver/package.json @@ -0,0 +1,52 @@ +{ + "name": "@nutui/auto-import-resolver", + "version": "0.0.1", + "description": "nutui auto import resolver based on unplugin-vue-components", + "keywords": [ + "nutui", + "vue", + "resolver", + "jdf2e" + ], + "main": "dist/index.js", + "module": "dist/index.mjs", + "typings": "dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.mjs", + "require": "./dist/index.js" + } + }, + "files": [ + "dist", + "README.md", + "README.zh-CN.md", + "package.json" + ], + "scripts": { + "clean": "rimraf ./dist", + "build:types": "tsc -p ./tsconfig.json --emitDeclarationOnly", + "build": "pnpm clean && vite build && pnpm build:types" + }, + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org/" + }, + "repository": { + "type": "git", + "url": "https://github.com/jdf2e/nutui.git", + "directory": "packages/nutui-auto-import-resolver" + }, + "homepage": "https://github.com/jdf2e/nutui/tree/v4/packages/nutui-auto-import-resolver", + "bugs": { + "url": "https://github.com/jdf2e/nutui/issues" + }, + "author": "jdf2e", + "license": "MIT", + "devDependencies": { + "rimraf": "^5.0.0", + "typescript": "^5.0.4", + "vite": "^4.4.9" + } +} diff --git a/packages/nutui-auto-import-resolver/src/index.ts b/packages/nutui-auto-import-resolver/src/index.ts new file mode 100644 index 0000000000..4a8577ab8d --- /dev/null +++ b/packages/nutui-auto-import-resolver/src/index.ts @@ -0,0 +1,66 @@ +import type { ComponentResolveResult, ComponentResolver } from 'unplugin-vue-components/types'; + +export interface NutUIResolverOptions { + /** + * import style css or sass with components + * + * @default 'css' + */ + importStyle?: boolean | 'css' | 'sass'; + + /** + * NutUI or NutUI-Taro + * + * @default false + */ + taro?: boolean; + + /** + * compatible with unplugin-auto-import + * + * @default false + */ + autoImport?: boolean; +} + +const nutFunctions = ['showToast', 'showNotify', 'showDialog', 'showImagePreview']; + +function getNutResolved(name: string, options: NutUIResolverOptions): ComponentResolveResult { + const { importStyle = true, taro = false, autoImport = false } = options; + + const packageName = taro ? '@nutui/nutui-taro' : '@nutui/nutui'; + + if (!importStyle) return { name, from: packageName }; + + const componentName = autoImport ? name.slice(4) : name; + + let style = `${packageName}/dist/packages/${componentName.toLowerCase()}/style/css`; + + if (importStyle === 'sass') { + style = `${packageName}/dist/packages/${componentName.toLowerCase()}/style`; + } + + return { + name, + from: packageName, + sideEffects: style + }; +} + +/** + * Resolver for NutUI 4.0+ + * + * @link https://github.com/jdf2e/nutui + */ +export default function NutUIResolver(options: NutUIResolverOptions = {}): ComponentResolver { + return { + type: 'component', + resolve: (name) => { + const { autoImport = false } = options; + + if (autoImport && nutFunctions.includes(name)) return getNutResolved(name, options); + + if (name.startsWith('Nut')) return getNutResolved(name.slice(3), options); + } + }; +} diff --git a/packages/nutui-auto-import-resolver/tsconfig.json b/packages/nutui-auto-import-resolver/tsconfig.json new file mode 100644 index 0000000000..ad4ab96d56 --- /dev/null +++ b/packages/nutui-auto-import-resolver/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "jsx": "preserve", + "strict": true, + "target": "ES2015", + "module": "ESNext", + "skipLibCheck": true, + "esModuleInterop": true, + "moduleResolution": "Node", + "lib": ["esnext", "dom"], + "outDir": "./dist", + "declaration": true + }, + "include": ["src/**/*"], + "exclude": ["**/node_modules", "**/.*/"] +} diff --git a/packages/nutui-auto-import-resolver/vite.config.ts b/packages/nutui-auto-import-resolver/vite.config.ts new file mode 100644 index 0000000000..9d087141bb --- /dev/null +++ b/packages/nutui-auto-import-resolver/vite.config.ts @@ -0,0 +1,21 @@ +import { defineConfig } from 'vite'; + +export default defineConfig({ + build: { + minify: false, + target: 'es2015', + lib: { + entry: 'src/index.ts', + name: 'name', + fileName: (format) => { + if (format === 'es') { + return 'index.mjs'; + } else { + return 'index.js'; + } + }, + formats: ['es', 'cjs'] + }, + emptyOutDir: false + } +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 44191e6dbb..a57b4e4346 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -118,6 +118,18 @@ importers: specifier: 1.8.15 version: 1.8.15(typescript@5.2.2) + packages/nutui-auto-import-resolver: + devDependencies: + rimraf: + specifier: ^5.0.0 + version: 5.0.5 + typescript: + specifier: ^5.0.4 + version: 5.2.2 + vite: + specifier: ^4.4.9 + version: 4.4.11(@types/node@18.18.5)(sass@1.69.3) + packages/nutui-eslint-config: dependencies: '@typescript-eslint/eslint-plugin': diff --git a/publish/nutui-taro/package.json b/publish/nutui-taro/package.json index 5139d744f2..3525816002 100644 --- a/publish/nutui-taro/package.json +++ b/publish/nutui-taro/package.json @@ -7,7 +7,7 @@ "style": "dist/style.css", "typings": "dist/types/index.d.ts", "sideEffects": [ - "dist/packages/**/style.mjs", + "dist/packages/**/style/*.mjs", "*.scss", "*.css" ], diff --git a/publish/nutui/package.json b/publish/nutui/package.json index b306211884..023117eb4e 100644 --- a/publish/nutui/package.json +++ b/publish/nutui/package.json @@ -7,7 +7,7 @@ "style": "dist/style.css", "typings": "dist/types/index.d.ts", "sideEffects": [ - "dist/packages/**/style.mjs", + "dist/packages/**/style/*.mjs", "*.scss", "*.css" ], diff --git a/scripts/generate-themes.cjs b/scripts/generate-themes.cjs index b2abcfcedb..63f8105375 100644 --- a/scripts/generate-themes.cjs +++ b/scripts/generate-themes.cjs @@ -2,6 +2,8 @@ const target = process.argv[2]; const config = require('../src/config.json'); const path = require('path'); const fs = require('fs-extra'); +const sass = require('sass'); + let sassFileStr = ``; let tasks = []; if (!target) { @@ -19,30 +21,152 @@ config.nav.map((item) => { path.resolve(__dirname, `../src/packages/__VUE/${folderName}/index.scss`), path.resolve(__dirname, `../dist/packages/${folderName}/index.scss`) ) - .catch((error) => {}) + .catch((error) => { console.error(error) }) ); }); }); tasks.push(fs.copy(path.resolve(__dirname, '../src/packages/styles'), path.resolve(__dirname, '../dist/styles'))); -Promise.all(tasks).then((res) => { +const themesEnum = { + 'jdt': 'variables-jdt', + 'jdb': 'variables-jdb', + 'jddkh': 'variables-jddkh' +}; + +// 将scss文件额外转换一份css +const sassTocss = () => { + let sassTocssTasks = []; + config.nav.map((item) => { + item.packages.forEach((element) => { + let folderName = element.name.toLowerCase(); + + try { + const filePath = path.resolve(__dirname, `../dist/packages/${folderName}/main.scss`); + sassTocssTasks.push( + // 写入main.scss,引入变量文件variables.scss和组件样式index.scss + fs.outputFile( + filePath, + `@import '../../styles/variables.scss';\n@import './index.scss';\n`, + 'utf8', + (error) => { + if (error) return console.error(error); + try { + // 编译sass为css + const result = sass.compile(filePath, { style: 'compressed' }); + // 删除main.scss + fs.unlinkSync(filePath); + // 写入index.css + fs.outputFile( + path.resolve(__dirname, `../dist/packages/${folderName}/index.css`), + result.css, + 'utf8', + (error) => { + if (error) return console.log(error); + } + ) + } catch (err) { + console.error(err); + } + } + ) + ) + } catch (err) { + console.error(err); + } + }) + }) + Promise.all(sassTocssTasks).then(() => { + console.log(`css文件写入成功`); + }); +} + +// 解析scss文件,生成css变量 +const parseFile = (filename, theme = 'default') => { + return fs.readFile(filename, 'utf-8', (err, data) => { + if (err) { + console.error(`无法读取文件: ${err}`); + return; + } + + const variables = {}; + const lines = data.split('\n'); + lines.forEach((line) => { + if (line.startsWith('$')) { + const trimmedLine = line.trim().replace(';', ''); + const [key, value] = trimmedLine.split(': '); + variables[key] = value; + } + }); + + let fileContent = `@import './${themesEnum[theme]}.scss';\n:root {\n`; + for (const key in variables) { + if (Object.prototype.hasOwnProperty.call(variables, key)) { + const variableName = key.slice(1); + fileContent += ` --nut-${variableName}: #{$${variableName}};\n`; + } + } + fileContent += `}`; + const base = theme === 'default' ? 'base' : `base-${theme}`; + const filePath = path.resolve(__dirname, `../dist/styles/${base}.scss`); + fs.outputFile( + filePath, + fileContent, + 'utf8', + (error) => { + if (error) return console.error(error) + // 编译sass为css + const result = sass.compile(filePath, { style: 'compressed' }); + // base.scss + fs.unlinkSync(filePath); + // 写入index.css + fs.outputFile( + path.resolve(__dirname, `../dist/styles/${base}.css`), + result.css, + 'utf8', + (error) => { + if (error) return console.log(error); + } + ) + } + ); + }); +} + +// 循环themesEnum,生成不同的css变量主题 +const variablesResolver = () => { + let variablesResolverTasks = []; + Object.keys(themesEnum).forEach((theme) => { + variablesResolverTasks.push(parseFile(path.resolve(__dirname, `../dist/styles/${themesEnum[theme]}.scss`), theme)) + }); + Promise.all(variablesResolverTasks).then(() => { + console.log('base文件写入成功') + }) +} + +Promise.all(tasks).then(() => { let themes = [ { file: 'default.scss', sourcePath: `@import '../variables.scss';` }, { file: 'jdt.scss', sourcePath: `@import '../variables-jdt.scss';` }, { file: 'jdb.scss', sourcePath: `@import '../variables-jdb.scss';` }, { file: 'jddkh.scss', sourcePath: `@import '../variables-jddkh.scss';` } ]; - + tasks = []; themes.forEach((item) => { - fs.outputFile( - path.resolve(__dirname, `../dist/styles/themes/${item.file}`), - `${item.sourcePath} -${sassFileStr}`, - 'utf8', - (error) => { - // logger.success(`文件写入成功`); - } - ); + tasks.push( + fs.outputFile( + path.resolve(__dirname, `../dist/styles/themes/${item.file}`), + `${item.sourcePath}\n${sassFileStr}`, + 'utf8', + (error) => { + if (error) return console.error(error) + } + ) + ) + }); + Promise.all(tasks).then(() => { + console.log(`sass文件写入成功`); + sassTocss(); + variablesResolver() }); }); diff --git a/scripts/generate-unplugin-deps.cjs b/scripts/generate-unplugin-deps.cjs index c81655ddb6..49399fe9ad 100644 --- a/scripts/generate-unplugin-deps.cjs +++ b/scripts/generate-unplugin-deps.cjs @@ -76,9 +76,15 @@ tasks.push( styleMap.forEach((value, key) => { const name = key.toLowerCase(); // gen style - const outputStyleMjs = `import '../../styles/reset.css';\nimport './index.scss';\n`; + const outputStyleMjs = `import '../../../styles/reset.css';\nimport '../index.scss';\n`; + const outputStyleCssMjs = `import '../../../styles/reset.css';\nimport '../index.css';\n`; tasks.push( - fs.outputFile(path.resolve(__dirname, `../dist/packages/${name}/style.mjs`), outputStyleMjs, 'utf8', () => { + fs.outputFile(path.resolve(__dirname, `../dist/packages/${name}/style/index.mjs`), outputStyleMjs, 'utf8', () => { + // console.log('') + }) + ); + tasks.push( + fs.outputFile(path.resolve(__dirname, `../dist/packages/${name}/style/css.mjs`), outputStyleCssMjs, 'utf8', () => { // console.log('') }) ); diff --git a/src/packages/__VUE/animate/index.scss b/src/packages/__VUE/animate/index.scss index d935a2d7c3..0f0ac5dc2b 100644 --- a/src/packages/__VUE/animate/index.scss +++ b/src/packages/__VUE/animate/index.scss @@ -239,10 +239,10 @@ box-sizing: border-box; border: 4 * 1px solid rgba(255, 255, 255, 0.6); position: absolute; - border-radius: calc(60 / 2) * 1px; + border-radius: calc((60 / 2) * 1px); right: 50%; - margin-top: calc(-30 / 2) * 1px; - margin-right: calc(-60 / 2) * 1px; + margin-top: calc(-30 / 2 * 1px); + margin-right: calc(-60 / 2 * 1px); z-index: 1; transform: scale(0); animation: twinkle 2s ease-out infinite; diff --git a/src/packages/__VUE/uploader/index.scss b/src/packages/__VUE/uploader/index.scss index 2bf3a1b869..a54d992983 100644 --- a/src/packages/__VUE/uploader/index.scss +++ b/src/packages/__VUE/uploader/index.scss @@ -27,9 +27,6 @@ display: flex; align-items: center; justify-content: center; - - &.list { - } } &__input { @@ -53,7 +50,7 @@ justify-content: center; margin-right: 10px; margin-bottom: 10px; - box-shadow: 0 2px 10px 0 rgb(0 0 0 / 10%); + box-shadow: 0 2px 10px 0 rgba(#000, 0.1); &__progress { position: absolute; diff --git a/src/packages/styles/variables-jdb.scss b/src/packages/styles/variables-jdb.scss index 1aaa6cee69..dd1f397c1f 100644 --- a/src/packages/styles/variables-jdb.scss +++ b/src/packages/styles/variables-jdb.scss @@ -100,13 +100,15 @@ $dark-color3: rgba(232, 230, 227, 0.8) !default; $dark-color-gray: $text-color !default; $dark-calendar-choose-color: rgba(227, 227, 227, 0.2) !default; -$font-family: +$font-family: var( + --nut-font-family, PingFang SC, Microsoft YaHei, Helvetica, Hiragino Sans GB, SimSun, - sans-serif !default; + sans-serif +) !default; // ---- Animation ---- $animation-duration: 0.25s !default; @@ -976,4 +978,7 @@ $invoice-padding: 10px 10px 20px !default; $trendarrow-font-size: 14px !default; $trendarrow-before-icon-margin: 4px !default; +// Space +$space-gap: var(--nut-space-gap, 8px) !default; + @import './mixins/index'; diff --git a/src/packages/styles/variables-jddkh.scss b/src/packages/styles/variables-jddkh.scss index 7ffd104133..f008757aae 100644 --- a/src/packages/styles/variables-jddkh.scss +++ b/src/packages/styles/variables-jddkh.scss @@ -31,13 +31,15 @@ $dark-color3: rgba(232, 230, 227, 0.8) !default; $dark-color-gray: $text-color !default; $dark-calendar-choose-color: rgba(227, 227, 227, 0.2) !default; -$font-family: +$font-family: var( + --nut-font-family, PingFang SC, Microsoft YaHei, Helvetica, Hiragino Sans GB, SimSun, - sans-serif !default; + sans-serif +) !default; // ---- Animation ---- $animation-duration: 0.25s !default; @@ -908,4 +910,7 @@ $invoice-padding: 10px 10px 20px !default; $trendarrow-font-size: 14px !default; $trendarrow-before-icon-margin: 4px !default; +// Space +$space-gap: var(--nut-space-gap, 8px) !default; + @import './mixins/index'; diff --git a/src/packages/styles/variables-jdt.scss b/src/packages/styles/variables-jdt.scss index d4b69d4253..8826f284ef 100644 --- a/src/packages/styles/variables-jdt.scss +++ b/src/packages/styles/variables-jdt.scss @@ -934,4 +934,7 @@ $invoice-padding: var(--nut-invoice-padding, 10px 10px 20px) !default; $trendarrow-font-size: var(--nut-trendarrow-font-size, 14px) !default; $trendarrow-before-icon-margin: var(--nut-trendarrow-before-icon-margin, 4px) !default; +// Space +$space-gap: var(--nut-space-gap, 8px) !default; + @import './mixins/index';