diff --git a/.editorconfig b/.editorconfig index ec90d88..bfccc60 100644 --- a/.editorconfig +++ b/.editorconfig @@ -13,6 +13,7 @@ trim_trailing_whitespace = false [*.{yml,yaml}] indent_size = 2 +indent_style = space [*.{js,ts,mts,vue}] indent_size = 2 diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 0051b4b..0000000 --- a/.eslintignore +++ /dev/null @@ -1,4 +0,0 @@ -# don't ever lint node_modules -node_modules -# don't lint build output (make sure it's set to your correct build folder name) -dist diff --git a/.eslintrc-auto-import.json b/.eslintrc-auto-import.json new file mode 100755 index 0000000..1e15d05 --- /dev/null +++ b/.eslintrc-auto-import.json @@ -0,0 +1,65 @@ +{ + "globals": { + "CSSProperties": true, + "Component": true, + "ComponentPublicInstance": true, + "ComputedRef": true, + "EffectScope": true, + "InjectionKey": true, + "PropType": true, + "Ref": true, + "VNode": true, + "computed": true, + "createApp": true, + "customRef": true, + "defineAsyncComponent": true, + "defineComponent": true, + "effectScope": true, + "getCurrentInstance": true, + "getCurrentScope": true, + "h": true, + "inject": true, + "isProxy": true, + "isReactive": true, + "isReadonly": true, + "isRef": true, + "markRaw": true, + "nextTick": true, + "onActivated": true, + "onBeforeMount": true, + "onBeforeUnmount": true, + "onBeforeUpdate": true, + "onDeactivated": true, + "onErrorCaptured": true, + "onMounted": true, + "onRenderTracked": true, + "onRenderTriggered": true, + "onScopeDispose": true, + "onServerPrefetch": true, + "onUnmounted": true, + "onUpdated": true, + "provide": true, + "reactive": true, + "readonly": true, + "ref": true, + "resolveComponent": true, + "shallowReactive": true, + "shallowReadonly": true, + "shallowRef": true, + "toRaw": true, + "toRef": true, + "toRefs": true, + "triggerRef": true, + "unref": true, + "useAttrs": true, + "useCssModule": true, + "useCssVars": true, + "useSlots": true, + "useTheme": true, + "useTemplateRef": true, + "watch": true, + "watchEffect": true, + "watchPostEffect": true, + "watchSyncEffect": true + } +} diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index 4d657bd..0000000 --- a/.eslintrc.js +++ /dev/null @@ -1,143 +0,0 @@ -module.exports = { - env: { - browser: true, - node: true, - }, - extends: [ - 'eslint:recommended', - 'plugin:vue/essential', - "@vue/typescript/recommended", - "prettier", - ], - ignorePatterns: [ - '.eslintrc.js', - 'stylelint.config.js', - 'vite.build.config.mts', - 'vite.config.mts', - '*.bk.vue', - ], - overrides: [ - { - files: [ - '**/*.spec.{j,t}s?(x)', - ], - env: { - jest: true, - }, - }, - ], - globals: { - Entry: true, - loadLanguages: true, - Prism: true, - }, - parserOptions: { - parser: '@typescript-eslint/parser', - }, - plugins: [ - '@typescript-eslint', - 'import', - 'prettier', - 'vue', - ], - root: true, - settings: { - 'import/resolver': { - 'babel-module': {}, - }, - }, - rules: { - "@typescript-eslint/ban-types": [ - "error", - { - "extendDefaults": true, - "types": { - "{}": false - } - } - ], - '@typescript-eslint/no-empty-function': 0, - 'brace-style': ['error', 'stroustrup'], - 'default-case': [ - 'error', { - commentPattern: '^skip\\sdefault', - }, - ], - 'func-names': ['error', 'never'], - 'function-paren-newline': 0, - 'import/no-self-import': 0, - 'import/no-extraneous-dependencies': 0, - 'implicit-arrow-linebreak': ['warn', 'beside'], - indent: [2, 'tab', { SwitchCase: 1 }], - 'no-tabs': [0, { allowIndentationTabs: true }], - 'linebreak-style': 0, - 'max-len': 0, - 'no-else-return': ['error', { allowElseIf: true }], - 'no-console': ['warn', { allow: ['warn', 'error', 'info'] }], - 'no-const-assign': 'error', - 'no-debugger': 0, - 'no-new': 0, - 'no-unused-vars': 1, - 'no-use-before-define': 0, - 'no-useless-escape': 0, - 'no-param-reassign': [ - 'error', { - props: true, - ignorePropertyModificationsFor: ['field', 'model', 'el', 'item', 'state', 'Vue', 'vue'], - }, - ], - 'no-underscore-dangle': [ - 'error', { - allow: ['_data'], - allowAfterThis: true, - }, - ], - 'no-plusplus': [ - 'error', { allowForLoopAfterthoughts: true }, - ], - 'object-curly-newline': ['error', { - ObjectPattern: { multiline: false }, - }], - 'operator-linebreak': ['error', 'after'], - 'prefer-destructuring': [ - 'error', { - array: false, - object: false, - }, - { - enforceForRenamedProperties: false, - }, - ], - 'space-before-function-paren': ['error', { - anonymous: 'never', - named: 'never', - asyncArrow: 'never', - }], - 'vue/attributes-order': ['error', { - 'alphabetical': true, - 'order': [ - 'DEFINITION', - 'LIST_RENDERING', - 'CONDITIONALS', - 'RENDER_MODIFIERS', - 'GLOBAL', - ['UNIQUE', 'SLOT'], - 'TWO_WAY_BINDING', - 'OTHER_DIRECTIVES', - 'OTHER_ATTR', - 'EVENTS', - 'CONTENT', - ], - }], - 'vue/html-closing-bracket-newline': 0, - 'vue/html-indent': 0, - 'vue/html-self-closing': 0, - 'vue/max-attributes-per-line': 0, - 'vue/no-multiple-template-root': 0, - 'vue/no-v-for-template-key': 0, - 'vue/no-template-shadow': 0, - 'vue/no-v-html': 0, - 'vue/singleline-html-element-content-newline': 0, - 'vue/valid-template-root': 0, - }, -}; diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..36c32e7 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,43 @@ +# Lint, type-check, test and build on pull requests and pushes to dev +name: CI + +on: + pull_request: + branches: [ main, dev ] + push: + branches: [ dev ] + +permissions: + contents: read + +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: true + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + version: 10.33.0 + + - name: Use Node.js from .nvmrc + uses: actions/setup-node@v4 + with: + node-version-file: .nvmrc + cache: pnpm + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Lint + run: pnpm lint + + # Type-check, unit tests (Vitest) and library build + - name: Build & Test + run: pnpm build diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9591942..33a2d34 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -16,27 +16,29 @@ jobs: build: runs-on: ubuntu-latest - strategy: - matrix: - node-version: [20] - steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 + + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + version: 10.33.0 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 + - name: Use Node.js from .nvmrc + uses: actions/setup-node@v4 with: - node-version: ${{ matrix.node-version }} + node-version-file: .nvmrc + cache: pnpm + + - name: Install dependencies + run: pnpm install --frozen-lockfile - - name: Build + - name: Build & Deploy run: | git config --global user.email "webdevnerdstuff@gmail.com" git config --global user.name "webdevnerdstuff" - mkdir docs - npm install --g gh-pages - npm i - npm run build:docs + pnpm build:docs git remote set-url origin https://git:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git - npm run deploy -u "github-actions-bot " + pnpm run deploy -u "github-actions-bot " env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 7189fc9..03d363a 100644 --- a/.gitignore +++ b/.gitignore @@ -9,11 +9,13 @@ lerna-debug.log* node_modules docs +dist dist-ssr *.local # Editor directories and files .vscode/* +.history/* !.vscode/extensions.json .idea .DS_Store @@ -23,8 +25,17 @@ dist-ssr *.sln *.sw? -.history - .stylelintcache src/plugin/themes/scss/highlight-template.scss -dist/themes/scss/highlight-template.scss + + +.eslintcache + +src/plugin/**/*.bk.* + +# Temp files and directories +*__TEMP.* +*__TEMP/ + +# Claude Code +.claude/ diff --git a/.husky/pre-commit b/.husky/pre-commit index a22812c..94f2647 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,4 +1 @@ -#!/usr/bin/env sh -. "$(dirname -- "$0")/_/husky.sh" - -npx lint-staged && npm run test:commit +npx lint-staged && npm run test:build diff --git a/.npmrc b/.npmrc index 8a472ff..4c3dbe5 100644 --- a/.npmrc +++ b/.npmrc @@ -1 +1 @@ -node-version=20.10.0 +node-version=22.11.0 diff --git a/.nvmrc b/.nvmrc index d5a1596..7af24b7 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -20.10.0 +22.11.0 diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..c194d9d --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,126 @@ +# AGENTS.md — vue-code-block + +## Project Overview + +`@wdns/vue-code-block` is a Vue 3 + TypeScript component that syntax-highlights code using either +[PrismJS](https://prismjs.com/) or [Highlight.js](https://highlightjs.org/). It supports themes +(bundled Neon Bunny themes or CDN-loaded library themes), copy/run tabs, a browser-window style, and +PrismJS plugins such as `line-numbers` via the `prism-plugin` prop. + +## Tech Stack + +- **Framework**: Vue 3 (Composition API, ` - - - + diff --git a/src/documentation/components/ExampleContainer.vue b/src/documentation/components/ExampleContainer.vue new file mode 100644 index 0000000..2756423 --- /dev/null +++ b/src/documentation/components/ExampleContainer.vue @@ -0,0 +1,141 @@ + + + + + diff --git a/src/documentation/components/MenuComponent.vue b/src/documentation/components/MenuComponent.vue index 278f60f..243bcbe 100644 --- a/src/documentation/components/MenuComponent.vue +++ b/src/documentation/components/MenuComponent.vue @@ -1,65 +1,72 @@ - - diff --git a/src/documentation/components/PropsTable.vue b/src/documentation/components/PropsTable.vue index 8442513..6c4ffaa 100644 --- a/src/documentation/components/PropsTable.vue +++ b/src/documentation/components/PropsTable.vue @@ -77,33 +77,24 @@ - diff --git a/src/documentation/components/examples/BrowserWindowExample.vue b/src/documentation/components/examples/BrowserWindowExample.vue new file mode 100644 index 0000000..0bc303f --- /dev/null +++ b/src/documentation/components/examples/BrowserWindowExample.vue @@ -0,0 +1,34 @@ + + + diff --git a/src/documentation/components/examples/BrowserWindowExamples.vue b/src/documentation/components/examples/BrowserWindowExamples.vue deleted file mode 100644 index db91cb9..0000000 --- a/src/documentation/components/examples/BrowserWindowExamples.vue +++ /dev/null @@ -1,43 +0,0 @@ - - - diff --git a/src/documentation/components/examples/ButtonExamples.vue b/src/documentation/components/examples/ButtonExamples.vue deleted file mode 100644 index f1f68c4..0000000 --- a/src/documentation/components/examples/ButtonExamples.vue +++ /dev/null @@ -1,76 +0,0 @@ - - - diff --git a/src/documentation/components/examples/CodeBlockRadiusExample.vue b/src/documentation/components/examples/CodeBlockRadiusExample.vue new file mode 100644 index 0000000..f42275e --- /dev/null +++ b/src/documentation/components/examples/CodeBlockRadiusExample.vue @@ -0,0 +1,54 @@ + + + diff --git a/src/documentation/components/examples/CopyAndRunTabsExample.vue b/src/documentation/components/examples/CopyAndRunTabsExample.vue new file mode 100644 index 0000000..3dffa96 --- /dev/null +++ b/src/documentation/components/examples/CopyAndRunTabsExample.vue @@ -0,0 +1,47 @@ + + + diff --git a/src/documentation/components/examples/CopyButtonExample.vue b/src/documentation/components/examples/CopyButtonExample.vue new file mode 100644 index 0000000..913e2c5 --- /dev/null +++ b/src/documentation/components/examples/CopyButtonExample.vue @@ -0,0 +1,32 @@ + + + diff --git a/src/documentation/components/examples/CopyTabExample.vue b/src/documentation/components/examples/CopyTabExample.vue new file mode 100644 index 0000000..6f548ae --- /dev/null +++ b/src/documentation/components/examples/CopyTabExample.vue @@ -0,0 +1,40 @@ + + + diff --git a/src/documentation/components/examples/CopyTextExample.vue b/src/documentation/components/examples/CopyTextExample.vue new file mode 100644 index 0000000..c2f4524 --- /dev/null +++ b/src/documentation/components/examples/CopyTextExample.vue @@ -0,0 +1,58 @@ + + + diff --git a/src/documentation/components/examples/CssExample.vue b/src/documentation/components/examples/CssExample.vue new file mode 100644 index 0000000..f763872 --- /dev/null +++ b/src/documentation/components/examples/CssExample.vue @@ -0,0 +1,65 @@ + + + diff --git a/src/documentation/components/examples/FloatingTabsExample.vue b/src/documentation/components/examples/FloatingTabsExample.vue new file mode 100644 index 0000000..d4fbb79 --- /dev/null +++ b/src/documentation/components/examples/FloatingTabsExample.vue @@ -0,0 +1,56 @@ + + + diff --git a/src/documentation/components/examples/HeightExample.vue b/src/documentation/components/examples/HeightExample.vue new file mode 100644 index 0000000..22688dc --- /dev/null +++ b/src/documentation/components/examples/HeightExample.vue @@ -0,0 +1,55 @@ + + + diff --git a/src/documentation/components/examples/HtmlExample.vue b/src/documentation/components/examples/HtmlExample.vue new file mode 100644 index 0000000..6e7bf36 --- /dev/null +++ b/src/documentation/components/examples/HtmlExample.vue @@ -0,0 +1,56 @@ + + + diff --git a/src/documentation/components/examples/IndentExample.vue b/src/documentation/components/examples/IndentExample.vue new file mode 100644 index 0000000..b0c5475 --- /dev/null +++ b/src/documentation/components/examples/IndentExample.vue @@ -0,0 +1,68 @@ + + + diff --git a/src/documentation/components/examples/JavascriptExample.vue b/src/documentation/components/examples/JavascriptExample.vue new file mode 100644 index 0000000..b61f937 --- /dev/null +++ b/src/documentation/components/examples/JavascriptExample.vue @@ -0,0 +1,62 @@ + + + diff --git a/src/documentation/components/examples/JsonExample.vue b/src/documentation/components/examples/JsonExample.vue new file mode 100644 index 0000000..45b3f10 --- /dev/null +++ b/src/documentation/components/examples/JsonExample.vue @@ -0,0 +1,49 @@ + + + diff --git a/src/documentation/components/examples/LangExamples.vue b/src/documentation/components/examples/LangExamples.vue deleted file mode 100644 index d37a122..0000000 --- a/src/documentation/components/examples/LangExamples.vue +++ /dev/null @@ -1,190 +0,0 @@ - - - diff --git a/src/documentation/components/examples/OtherExamples.vue b/src/documentation/components/examples/OtherExamples.vue deleted file mode 100644 index b5bdf6c..0000000 --- a/src/documentation/components/examples/OtherExamples.vue +++ /dev/null @@ -1,406 +0,0 @@ - - - - - - diff --git a/src/documentation/components/examples/PersistentCopyButtonExample.vue b/src/documentation/components/examples/PersistentCopyButtonExample.vue new file mode 100644 index 0000000..d801ba1 --- /dev/null +++ b/src/documentation/components/examples/PersistentCopyButtonExample.vue @@ -0,0 +1,34 @@ + + + diff --git a/src/documentation/components/examples/PhpExample.vue b/src/documentation/components/examples/PhpExample.vue new file mode 100644 index 0000000..cb9451b --- /dev/null +++ b/src/documentation/components/examples/PhpExample.vue @@ -0,0 +1,145 @@ + + + diff --git a/src/documentation/components/examples/PluginExample.vue b/src/documentation/components/examples/PluginExample.vue new file mode 100644 index 0000000..8ffde66 --- /dev/null +++ b/src/documentation/components/examples/PluginExample.vue @@ -0,0 +1,61 @@ + + + diff --git a/src/documentation/components/examples/PluginExamples.vue b/src/documentation/components/examples/PluginExamples.vue deleted file mode 100644 index 01bbe5d..0000000 --- a/src/documentation/components/examples/PluginExamples.vue +++ /dev/null @@ -1,104 +0,0 @@ - - - diff --git a/src/documentation/components/examples/RunTabExample.vue b/src/documentation/components/examples/RunTabExample.vue new file mode 100644 index 0000000..b4c1e3d --- /dev/null +++ b/src/documentation/components/examples/RunTabExample.vue @@ -0,0 +1,49 @@ + + + diff --git a/src/documentation/components/examples/RunTextExample.vue b/src/documentation/components/examples/RunTextExample.vue new file mode 100644 index 0000000..962473c --- /dev/null +++ b/src/documentation/components/examples/RunTextExample.vue @@ -0,0 +1,70 @@ + + + diff --git a/src/documentation/components/examples/TabExamples.vue b/src/documentation/components/examples/TabExamples.vue deleted file mode 100644 index 2f3e09f..0000000 --- a/src/documentation/components/examples/TabExamples.vue +++ /dev/null @@ -1,244 +0,0 @@ - - - diff --git a/src/documentation/components/examples/TabGapExample.vue b/src/documentation/components/examples/TabGapExample.vue new file mode 100644 index 0000000..ded7353 --- /dev/null +++ b/src/documentation/components/examples/TabGapExample.vue @@ -0,0 +1,58 @@ + + + diff --git a/src/documentation/components/examples/TypescriptExample.vue b/src/documentation/components/examples/TypescriptExample.vue new file mode 100644 index 0000000..636f2c9 --- /dev/null +++ b/src/documentation/components/examples/TypescriptExample.vue @@ -0,0 +1,53 @@ + + + diff --git a/src/documentation/components/examples/VueExample.vue b/src/documentation/components/examples/VueExample.vue new file mode 100644 index 0000000..3431bfe --- /dev/null +++ b/src/documentation/components/examples/VueExample.vue @@ -0,0 +1,45 @@ + + + diff --git a/src/documentation/components/examples/helpers.ts b/src/documentation/components/examples/helpers.ts new file mode 100644 index 0000000..a2d9132 --- /dev/null +++ b/src/documentation/components/examples/helpers.ts @@ -0,0 +1,89 @@ +/** + * Code shown by the examples that demonstrate props rather than a language. + */ +export const sampleCode = `const greeting = 'Hello, World!'; + +function sayHello(name) { + console.log(\`\${greeting} My name is \${name}.\`); +} + +sayHello('Neon Bunny');`; + + +// Events go last, then sort by the attribute name without its binding prefix. +function compareAttrs(a: string, b: string): number { + const aIsEvent = a.startsWith('@'); + const bIsEvent = b.startsWith('@'); + + if (aIsEvent !== bIsEvent) { + return aIsEvent ? 1 : -1; + } + + return a.replace(/^[:@]/, '').localeCompare(b.replace(/^[:@]/, '')); +} + + +interface TemplateOptions { + attrs: string[]; + library: string; + slot?: string; +} + +/** + * Builds the Template snippet for an example, adding the attribute for the selected library. + */ +export function buildTemplateCode(options: TemplateOptions): string { + const { attrs, library, slot } = options; + const allAttrs = [...attrs, library].sort(compareAttrs); + const attrLines = allAttrs.map((attr) => ` ${attr}`).join('\n'); + + if (slot) { + const slotLines = slot.split('\n').map((line) => ` ${line}`).join('\n'); + + return ``; + } + + return ``; +} + + +interface ScriptOptions { + body?: string; + code?: string; + imports?: string[]; +} + +/** + * Builds the Script snippet for an example. `code` becomes the `code` constant passed to VCodeBlock. + */ +export function buildScriptCode(options: ScriptOptions): string { + const { body, code, imports = [] } = options; + const parts: string[] = []; + + if (imports.length) { + parts.push(imports.join('\n')); + } + + if (code !== undefined) { + const escapedCode = code.replace(/\\/g, '\\\\').replace(/`/g, '\\`').replace(/\$\{/g, '\\${'); + parts.push(`const code = \`${escapedCode}\`;`); + } + + if (body) { + parts.push(body); + } + + return ``; +} diff --git a/src/documentation/components/examples/index.ts b/src/documentation/components/examples/index.ts index 61f76a4..80eb707 100644 --- a/src/documentation/components/examples/index.ts +++ b/src/documentation/components/examples/index.ts @@ -1,17 +1,46 @@ -import AdditionalLangExamples from './AdditionalLangExamples.vue'; -import BrowserWindowExamples from './BrowserWindowExamples.vue'; -import ButtonExamples from './ButtonExamples.vue'; -import LangExamples from './LangExamples.vue'; -import OtherExamples from './OtherExamples.vue'; -import PluginExamples from './PluginExamples.vue'; -import TabExamples from './TabExamples.vue'; +import BrowserWindowExample from './BrowserWindowExample.vue'; +import CodeBlockRadiusExample from './CodeBlockRadiusExample.vue'; +import CopyAndRunTabsExample from './CopyAndRunTabsExample.vue'; +import CopyButtonExample from './CopyButtonExample.vue'; +import CopyTabExample from './CopyTabExample.vue'; +import CopyTextExample from './CopyTextExample.vue'; +import CssExample from './CssExample.vue'; +import FloatingTabsExample from './FloatingTabsExample.vue'; +import HeightExample from './HeightExample.vue'; +import HtmlExample from './HtmlExample.vue'; +import IndentExample from './IndentExample.vue'; +import JavascriptExample from './JavascriptExample.vue'; +import JsonExample from './JsonExample.vue'; +import PersistentCopyButtonExample from './PersistentCopyButtonExample.vue'; +import PhpExample from './PhpExample.vue'; +import PluginExample from './PluginExample.vue'; +import RunTabExample from './RunTabExample.vue'; +import RunTextExample from './RunTextExample.vue'; +import TabGapExample from './TabGapExample.vue'; +import TypescriptExample from './TypescriptExample.vue'; +import VueExample from './VueExample.vue'; + export { - AdditionalLangExamples, - BrowserWindowExamples, - ButtonExamples, - LangExamples, - OtherExamples, - PluginExamples, - TabExamples, + BrowserWindowExample, + CodeBlockRadiusExample, + CopyAndRunTabsExample, + CopyButtonExample, + CopyTabExample, + CopyTextExample, + CssExample, + FloatingTabsExample, + HeightExample, + HtmlExample, + IndentExample, + JavascriptExample, + JsonExample, + PersistentCopyButtonExample, + PhpExample, + PluginExample, + RunTabExample, + RunTextExample, + TabGapExample, + TypescriptExample, + VueExample, }; diff --git a/src/documentation/layout/AppBar.vue b/src/documentation/layout/AppBar.vue index b3cc15f..89db2ad 100644 --- a/src/documentation/layout/AppBar.vue +++ b/src/documentation/layout/AppBar.vue @@ -14,7 +14,6 @@ > - @@ -67,8 +56,8 @@ - {{ selectedLibrary.title }} Active + {{ codeBlockSettings.plugin.title }} Active - diff --git a/src/documentation/sections/DependenciesSection.vue b/src/documentation/sections/DependenciesSection.vue index bec1380..3edd9f9 100644 --- a/src/documentation/sections/DependenciesSection.vue +++ b/src/documentation/sections/DependenciesSection.vue @@ -45,11 +45,10 @@ - diff --git a/src/documentation/sections/DescriptionSection.vue b/src/documentation/sections/DescriptionSection.vue index b321476..4e909d6 100644 --- a/src/documentation/sections/DescriptionSection.vue +++ b/src/documentation/sections/DescriptionSection.vue @@ -37,7 +37,7 @@ - diff --git a/src/documentation/sections/EventsSection.vue b/src/documentation/sections/EventsSection.vue index b6a9fb0..51698be 100644 --- a/src/documentation/sections/EventsSection.vue +++ b/src/documentation/sections/EventsSection.vue @@ -52,11 +52,11 @@ - diff --git a/src/documentation/sections/LegalSection.vue b/src/documentation/sections/LegalSection.vue index 5b989f4..296f4e5 100644 --- a/src/documentation/sections/LegalSection.vue +++ b/src/documentation/sections/LegalSection.vue @@ -21,9 +21,6 @@ - diff --git a/src/documentation/sections/LicenseSection.vue b/src/documentation/sections/LicenseSection.vue index f674f8e..3de1838 100644 --- a/src/documentation/sections/LicenseSection.vue +++ b/src/documentation/sections/LicenseSection.vue @@ -31,9 +31,7 @@ - diff --git a/src/documentation/sections/PlaygroundSection.vue b/src/documentation/sections/PlaygroundSection.vue index 29968eb..695d017 100644 --- a/src/documentation/sections/PlaygroundSection.vue +++ b/src/documentation/sections/PlaygroundSection.vue @@ -17,14 +17,14 @@ @@ -32,10 +32,10 @@ - diff --git a/src/documentation/sections/PropsSection.vue b/src/documentation/sections/PropsSection.vue index cf4baea..8ac05a4 100644 --- a/src/documentation/sections/PropsSection.vue +++ b/src/documentation/sections/PropsSection.vue @@ -23,11 +23,11 @@ - diff --git a/src/documentation/sections/SlotsSection.vue b/src/documentation/sections/SlotsSection.vue index cc4c004..4bebc44 100644 --- a/src/documentation/sections/SlotsSection.vue +++ b/src/documentation/sections/SlotsSection.vue @@ -49,10 +49,10 @@ @@ -80,10 +80,10 @@ @@ -111,10 +111,10 @@ @@ -125,14 +125,12 @@ - diff --git a/src/plugin/components/__tests__/__snapshots__/StatusIcons.test.ts.snap b/src/plugin/components/__tests__/__snapshots__/StatusIcons.test.ts.snap index 4ceb956..e8eb83e 100644 --- a/src/plugin/components/__tests__/__snapshots__/StatusIcons.test.ts.snap +++ b/src/plugin/components/__tests__/__snapshots__/StatusIcons.test.ts.snap @@ -3,7 +3,7 @@ exports[`StatusIcons Component > should mount the copy icon 1`] = ` " - + " `; @@ -12,7 +12,7 @@ exports[`StatusIcons Component > should mount the failed icon 1`] = ` " - + " `; @@ -21,7 +21,7 @@ exports[`StatusIcons Component > should mount the success icon 1`] = ` " - + " `; diff --git a/src/plugin/composables/__tests__/classes.test.ts b/src/plugin/composables/__tests__/classes.test.ts index 7430fb7..f469fc7 100644 --- a/src/plugin/composables/__tests__/classes.test.ts +++ b/src/plugin/composables/__tests__/classes.test.ts @@ -12,8 +12,8 @@ import { Props, UseTheme, } from '../../types'; -import { AllProps } from '../../utils/props'; -import { pluginName } from '../../utils/globals'; +import { AllProps } from '../../data/props'; +import { pluginName } from '../../data/globals'; const defaultProps: Props = { ...AllProps }; diff --git a/src/plugin/composables/__tests__/styles.test.ts b/src/plugin/composables/__tests__/styles.test.ts index 7276563..7e3f150 100644 --- a/src/plugin/composables/__tests__/styles.test.ts +++ b/src/plugin/composables/__tests__/styles.test.ts @@ -10,7 +10,7 @@ import { Props, UseTheme, } from '../../types'; -import { AllProps } from '../../utils/props'; +import { AllProps } from '../../data/props'; const defaultProps: Props = { ...AllProps }; diff --git a/src/plugin/composables/classes.ts b/src/plugin/composables/classes.ts index f6a6a46..33ec21a 100644 --- a/src/plugin/composables/classes.ts +++ b/src/plugin/composables/classes.ts @@ -1,4 +1,4 @@ -import { pluginName } from '../utils/globals'; +import { pluginName } from '../data/globals'; import { UseCodeBlockClasses, UseCopyButtonClasses, @@ -8,8 +8,7 @@ import { } from '@/plugin/types'; - -function getTheme(useTheme: string | boolean): string | boolean { +function getTheme(useTheme: Settings['theme']): Settings['theme'] { return useTheme === '' || useTheme === 'prism' ? 'default' : useTheme; } diff --git a/src/plugin/composables/helpers.ts b/src/plugin/composables/helpers.ts index c000a91..abfeb02 100644 --- a/src/plugin/composables/helpers.ts +++ b/src/plugin/composables/helpers.ts @@ -17,3 +17,42 @@ export const useConvertToUnit: UseConvertToUnit = (options) => { return `${Number(str)}${unit}`; }; + + +// -------------------------------------------------- Deep Merging # +/** + * Object check. + */ +function isObject(item: object): boolean { + return (item && typeof item === 'object' && !Array.isArray(item)); +} + +/** + * Deep merge objects. + */ +export function useDeepMerge(target: object | object[], ...sources: object[]): object { + if (!sources.length) { + return target; + } + + const source = sources.shift() as object[]; + + if (isObject(target) && isObject(source)) { + for (const key in source) { + // @ts-ignore + if (isObject(source[key])) { + if (!target[key as keyof object]) { + Object.assign(target, { [key]: {} }); + } + + // @ts-ignore + useDeepMerge(target[key as keyof object], source[key]); + } + else { + Object.assign(target, { [key]: source[key] }); + } + } + } + + return useDeepMerge(target, ...sources); +} diff --git a/src/plugin/data/globals.ts b/src/plugin/data/globals.ts new file mode 100644 index 0000000..06af4f1 --- /dev/null +++ b/src/plugin/data/globals.ts @@ -0,0 +1,5 @@ +import type { InjectionKey } from 'vue'; + +export const pluginName = 'v-code-block'; + +export const pluginOptionsInjectionKey: InjectionKey = Symbol(); diff --git a/src/plugin/utils/props.ts b/src/plugin/data/props.ts similarity index 67% rename from src/plugin/utils/props.ts rename to src/plugin/data/props.ts index d4160ff..df84736 100644 --- a/src/plugin/utils/props.ts +++ b/src/plugin/data/props.ts @@ -1,15 +1,15 @@ export const AllProps = { browserWindow: false, - cssPath: undefined, code: '', codeBlockRadius: '0.5rem', copyButton: true, + copyFailedText: 'Copy failed!', copyIcons: true, + copySuccessText: 'Copied!', copyTab: true, - copyFailedText: 'Copy failed!', copyText: 'Copy Code', - copySuccessText: 'Copied!', + cssPath: undefined, floatingTabs: true, height: 'auto', highlightjs: false, @@ -18,11 +18,16 @@ export const AllProps = { lang: 'javascript', maxHeight: 'auto', persistentCopyButton: false, - prismjs: false, prismPlugin: false, + prismjs: false, runTab: false, runText: 'Run', tabGap: '0.25rem', tabs: false, theme: 'neon-bunny', }; + +// Used as the component prop defaults so unset props can fall back to the global plugin options // +export const UndefinedProps = Object.fromEntries( + Object.keys(AllProps).map((key) => [key, undefined]), +) as { [K in keyof typeof AllProps]: undefined }; diff --git a/src/plugin/index.ts b/src/plugin/index.ts index e894053..8e76e55 100644 --- a/src/plugin/index.ts +++ b/src/plugin/index.ts @@ -1,30 +1,32 @@ -import { defineAsyncComponent } from 'vue'; -import type { App } from 'vue'; import type { Props } from './types'; +import type { App, Plugin } from 'vue'; import './styles/cssVariables.css'; import './styles/utilities.scss'; import './styles/main.scss'; import './styles/themeStyles.scss'; +import { useDeepMerge } from './composables/helpers'; +import { pluginOptionsInjectionKey } from './data/globals'; +import { AllProps } from './data/props'; import VCodeBlock from './VCodeBlock.vue'; export const codeBlockOptions = Symbol(); -export function createVCodeBlock(options: Props = {}) { +export function createVCodeBlock(options: Props = {}): Plugin { const install = (app: App) => { - app.provide(codeBlockOptions, options); + const pluginOptions: PluginOptions = useDeepMerge({}, AllProps, options); - app.component('CodeBlock', defineAsyncComponent(() => import('./VCodeBlock.vue'))); - app.component('VCodeBlock', defineAsyncComponent(() => import('./VCodeBlock.vue'))); + app.provide(pluginOptionsInjectionKey, pluginOptions); + + app.component('CodeBlock', VCodeBlock); + app.component('VCodeBlock', VCodeBlock); }; return { install, - }; + } as Plugin; } export default VCodeBlock; -export { - VCodeBlock, -}; +export {VCodeBlock}; diff --git a/src/plugin/themes/index.ts b/src/plugin/themes/index.ts index 85ba43f..61274fc 100644 --- a/src/plugin/themes/index.ts +++ b/src/plugin/themes/index.ts @@ -2931,5 +2931,5 @@ export { neonBunnyThemeMin, neonBunnyCarrotThemeMin, neonBunnyHighlightThemeMin, - neonBunnyCarrotHighlightThemeMin + neonBunnyCarrotHighlightThemeMin, }; diff --git a/src/plugin/types/index.ts b/src/plugin/types/index.ts index c64cbbf..5a99805 100644 --- a/src/plugin/types/index.ts +++ b/src/plugin/types/index.ts @@ -1,6 +1,6 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable no-unused-vars */ -import type{ + + +import type { CSSProperties, MaybeRef, } from 'vue'; @@ -9,8 +9,16 @@ import VCodeBlock from '../VCodeBlock.vue'; export * from '../index'; +// -------------------------------------------------- Globals // +declare global { + interface KeyStringAny { + [key: string]: T; + } +} + + // -------------------------------------------------- Types // -export type UseTheme = MaybeRef; +export type UseTheme = MaybeRef; export type CopyStatus = MaybeRef<'copy' | 'success' | 'failed'>; @@ -46,6 +54,13 @@ export interface Props { }; +declare global { + export interface PluginOptions extends Partial { } + + export interface Settings extends PluginOptions { } +} + + // -------------------------------------------------- Helpers // export interface UseConvertToUnit { ( @@ -62,7 +77,7 @@ export interface UseCodeBlockClasses { ( options: { isMobile: MaybeRef; - isPrism: MaybeRef; + isPrism: MaybeRef; } ): object; } @@ -147,7 +162,7 @@ export interface UseTabGroupStyles { } -declare module "vue" { +declare module 'vue' { interface ComponentCustomProperties { } diff --git a/src/plugin/types/vue-shim.d.ts b/src/plugin/types/vue-shim.d.ts index 30fc40a..8cb6130 100644 --- a/src/plugin/types/vue-shim.d.ts +++ b/src/plugin/types/vue-shim.d.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ declare module '*.vue' { import type { DefineComponent } from 'vue'; const component: DefineComponent<{}, {}, any>; diff --git a/src/plugin/utils/globals.ts b/src/plugin/utils/globals.ts deleted file mode 100644 index 6bfb35a..0000000 --- a/src/plugin/utils/globals.ts +++ /dev/null @@ -1 +0,0 @@ -export const pluginName = 'v-code-block'; diff --git a/src/plugins/index.ts b/src/plugins/index.ts index 6cde45b..802ea87 100644 --- a/src/plugins/index.ts +++ b/src/plugins/index.ts @@ -1,7 +1,7 @@ -import { loadFonts } from '@/plugins/webfontloader'; +import type { App } from 'vue'; import vuetify from '@/plugins/vuetify'; +import { loadFonts } from '@/plugins/webfontloader'; -import type { App } from 'vue'; export function registerPlugins(app: App) { loadFonts(); diff --git a/src/plugins/theme.ts b/src/plugins/theme.ts index 50f23d5..97b9bfa 100644 --- a/src/plugins/theme.ts +++ b/src/plugins/theme.ts @@ -1,4 +1,4 @@ -import colors from 'vuetify/lib/util/colors.mjs'; +import colors from 'vuetify/util/colors'; export const dark = { colors: { diff --git a/src/plugins/vuetify.ts b/src/plugins/vuetify.ts index a198cd4..740269c 100644 --- a/src/plugins/vuetify.ts +++ b/src/plugins/vuetify.ts @@ -1,11 +1,11 @@ import 'vuetify/styles'; import '@mdi/font/css/materialdesignicons.css'; +import { createVuetify } from 'vuetify'; import * as components from 'vuetify/components'; import * as directives from 'vuetify/directives'; -import defaultThemes from './theme'; -import { createVuetify } from 'vuetify'; -import { aliases, mdi } from 'vuetify/iconsets/mdi'; import { fa } from 'vuetify/iconsets/fa-svg'; +import { aliases, mdi } from 'vuetify/iconsets/mdi'; +import defaultThemes from './theme'; export default createVuetify({ diff --git a/src/stores/index.ts b/src/stores/index.ts index cca9013..c8acd7e 100644 --- a/src/stores/index.ts +++ b/src/stores/index.ts @@ -1,11 +1,11 @@ -import { defineStore } from 'pinia'; -// @ts-expect-error @ts-ignore +/* eslint-disable vue/sort-keys */ import packageJson from '@root/package.json'; +import { defineStore } from 'pinia'; export const useCoreStore = defineStore('core', () => { const scopedPackageName = packageJson.name; - const packageName = scopedPackageName.split('/')[1]; + const packageName = scopedPackageName.split('/')[1] ?? scopedPackageName; const dependencies = packageJson.dependencies; const prismVersion = dependencies.prismjs.replace('^', ''); @@ -378,6 +378,7 @@ export const useCoreStore = defineStore('core', () => { license: `${repoBaseUrl}/blob/main/LICENSE.md`, neonBunnyTheme: 'https://marketplace.visualstudio.com/items?itemName=WebDevNerdStuff.neon-bunny', npm: `https://www.npmjs.com/package/${scopedPackageName}`, + pnpm: 'https://pnpm.io/', prismjs: 'https://prismjs.com/', uaParser: 'https://www.npmjs.com/package/ua-parser-js', vue: 'https://vuejs.org/', @@ -428,9 +429,9 @@ export const useCoreStore = defineStore('core', () => { return { ...actions, ...getters, - highlightThemes, highlightJsLinks, highlightJsVersion, + highlightThemes, libraries, links, neonBunnyThemes, diff --git a/src/stores/menu.ts b/src/stores/menu.ts index 4f33936..9fdcb8a 100644 --- a/src/stores/menu.ts +++ b/src/stores/menu.ts @@ -6,48 +6,132 @@ export const useMenuStore = defineStore('menu', () => { const coreStore = useCoreStore(); const links = coreStore.links; - const eventItems = [ + const exampleItems = [ { - href: '#examples', - icon: 'mdi:mdi-code-json', - key: 'languages', - title: 'Languages', + href: '#examples-javascript', + icon: 'mdi:mdi-language-javascript', + key: 'examples-javascript', + title: 'JavaScript', + }, + { + href: '#examples-vue', + icon: 'mdi:mdi-vuejs', + key: 'examples-vue', + title: 'Vue', + }, + { + href: '#examples-css', + icon: 'mdi:mdi-language-css3', + key: 'examples-css', + title: 'CSS', + }, + { + href: '#examples-html', + icon: 'mdi:mdi-language-html5', + key: 'examples-html', + title: 'HTML', + }, + { + href: '#examples-typescript', + icon: 'mdi:mdi-language-typescript', + key: 'examples-typescript', + title: 'TypeScript', }, { - href: '#examples-additional-languages', + href: '#examples-json', icon: 'mdi:mdi-code-json', - key: 'add-languages', - title: 'Additional languages', + key: 'examples-json', + title: 'JSON', + }, + { + href: '#examples-php', + icon: 'mdi:mdi-language-php', + key: 'examples-php', + title: 'PHP', }, { href: '#examples-plugins', - icon: 'mdi:mdi-code-json', - key: 'plugins', + icon: 'mdi:mdi-puzzle', + key: 'examples-plugins', title: 'Plugins', }, { - href: '#examples-tabs', - icon: 'mdi:mdi-code-json', - key: 'tabs', - title: 'Tabs', + href: '#examples-copy-tab', + icon: 'mdi:mdi-content-copy', + key: 'examples-copy-tab', + title: 'Copy Tab', + }, + { + href: '#examples-run-tab', + icon: 'mdi:mdi-play', + key: 'examples-run-tab', + title: 'Run Tab', + }, + { + href: '#examples-copy-and-run-tabs', + icon: 'mdi:mdi-tab', + key: 'examples-copy-and-run-tabs', + title: 'Copy and Run Tabs', }, { href: '#examples-copy-button', - icon: 'mdi:mdi-code-json', - key: 'copy-button', + icon: 'mdi:mdi-content-copy', + key: 'examples-copy-button', title: 'Copy Button', }, + { + href: '#examples-persistent-copy-button', + icon: 'mdi:mdi-pin', + key: 'examples-persistent-copy-button', + title: 'Persistent Copy Button', + }, { href: '#examples-browser-window', - icon: 'mdi:mdi-code-json', - key: 'browser-window', + icon: 'mdi:mdi-application-outline', + key: 'examples-browser-window', title: 'Browser Window', }, { - href: '#examples-other-props', - icon: 'mdi:mdi-code-json', - key: 'other-props', - title: 'Other Props', + href: '#examples-code-block-radius', + icon: 'mdi:mdi-rounded-corner', + key: 'examples-code-block-radius', + title: 'Code Block Radius', + }, + { + href: '#examples-copy-text', + icon: 'mdi:mdi-form-textbox', + key: 'examples-copy-text', + title: 'Copy Text', + }, + { + href: '#examples-run-text', + icon: 'mdi:mdi-play-box-outline', + key: 'examples-run-text', + title: 'Run Text', + }, + { + href: '#examples-floating-tabs', + icon: 'mdi:mdi-tab-unselected', + key: 'examples-floating-tabs', + title: 'Floating Tabs', + }, + { + href: '#examples-height', + icon: 'mdi:mdi-arrow-expand-vertical', + key: 'examples-height', + title: 'Height', + }, + { + href: '#examples-tab-gap', + icon: 'mdi:mdi-arrow-expand-horizontal', + key: 'examples-tab-gap', + title: 'Tab Gap', + }, + { + href: '#examples-indent', + icon: 'mdi:mdi-format-indent-increase', + key: 'examples-indent', + title: 'Indent', }, ]; @@ -105,15 +189,10 @@ export const useMenuStore = defineStore('menu', () => { { href: '#examples', icon: 'mdi:mdi-code-json', - items: [...eventItems], + items: [...exampleItems], key: 'examples', title: 'Examples', }, - // { - // href: '#examples', - // icon: 'mdi:mdi-code-json', - // title: 'Examples', - // }, { href: '#playground', icon: 'mdi:mdi-seesaw', diff --git a/src/stores/props.ts b/src/stores/props.ts index 5e1ef19..0cfbb5c 100644 --- a/src/stores/props.ts +++ b/src/stores/props.ts @@ -1,6 +1,5 @@ -import { defineStore } from 'pinia'; -// @ts-expect-error @ts-ignore import packageJson from '@root/package.json'; +import { defineStore } from 'pinia'; const dependencies = packageJson.dependencies; const prismVersion = dependencies.prismjs.replace('^', ''); @@ -19,7 +18,7 @@ const prismLinks = { }; -const propsHeaders = [ +const propsHeaders: Docs.TableHeader[] = [ { align: 'start', filterable: true, @@ -53,7 +52,7 @@ const propsHeaders = [ }, ]; -const componentProps = [ +const componentProps: Docs.PropItem[] = [ { default: 'false', desc: 'To give the code block a browser window look.', @@ -248,34 +247,6 @@ const componentProps = [ }, ]; -// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars -const componentProps2 = { - // theme: { - // type: 'string | boolean', - // required: false, - // id: 'props-theme-option', - // default: 'neon-bunny', - // description: `

The theme to be used for the code block. Available options include:

- //

- // false - When you are loading the theme's styles yourself. - //

- //

- // Neon Bunny Themes - //
- // neon-bunny - //
- // neon-bunny-carrot - //

- //

- // PrismJS Themes - //

- //

- // Highlight.js Themes - //

- // `, - // } -}; - export const usePropsStore = defineStore('props', { state: () => { diff --git a/stylelint.config.js b/stylelint.config.js index e5ca9bd..7d945fd 100644 --- a/stylelint.config.js +++ b/stylelint.config.js @@ -1,516 +1,4 @@ -const namePattern = '^[a-z]+([a-z0-9-]+[a-z0-9]+)?$'; - module.exports = { - extends: ['stylelint-config-standard'], - plugins: [ - 'stylelint-order', - 'stylelint-scss', - '@stylistic/stylelint-plugin', - ], - customSyntax: 'postcss-scss', - overrides: [ - { - 'files': ['**/*.vue'], - 'customSyntax': 'postcss-html', - }, - ], - rules: { - // ------------------------------------------------------ CSS - - /* - * Stylelint CSS Rules Reference: https://stylelint.io/user-guide/rules/list - */ - - // ---------------------------- Possible errors - // Color - 'color-no-invalid-hex': true, - - // Font family - 'font-family-no-duplicate-names': true, - 'font-family-no-missing-generic-family-keyword': null, - - // Function - 'function-calc-no-unspaced-operator': true, - 'function-linear-gradient-no-nonstandard-direction': true, - - // String - 'string-no-newline': true, - - // Unit - 'unit-no-unknown': true, - - // Property - 'property-no-unknown': true, - - // Keyframe declaration - 'keyframe-declaration-no-important': null, - - // Declaration block - 'declaration-block-no-duplicate-properties': true, - 'declaration-block-no-shorthand-property-overrides': true, - - // Block - 'block-no-empty': true, - - // Selector - 'selector-pseudo-class-no-unknown': null, - 'selector-pseudo-element-no-unknown': [ - true, - { - ignorePseudoElements: ['v-deep'], - }, - ], - 'selector-type-no-unknown': true, - - // Media feature - 'media-feature-name-no-unknown': true, - - // At-rule - 'at-rule-no-unknown': null, - - // Comment - 'comment-no-empty': true, - - // General / Sheet - 'no-descending-specificity': null, - 'no-duplicate-at-import-rules': true, - 'no-duplicate-selectors': true, - 'no-empty-source': null, - 'no-invalid-double-slash-comments': true, - - // ---------------------------- Limit language features - // Color - 'color-named': 'never', - 'color-no-hex': null, - - // Function - 'function-disallowed-list': [], - 'function-no-unknown': null, - 'function-url-no-scheme-relative': true, - 'function-url-scheme-disallowed-list': [], - 'function-url-scheme-allowed-list': null, - 'function-allowed-list': null, - - // Keyframes - 'keyframes-name-pattern': '', - - // Number - 'number-max-precision': 10, - - // Time - 'time-min-milliseconds': 25, - - // Unit - 'unit-disallowed-list': [], - 'unit-allowed-list': null, - - // Shorthand property - 'shorthand-property-no-redundant-values': true, - - // Value - 'value-no-vendor-prefix': true, - - // Custom property - 'custom-property-pattern': '', - - // Property - 'property-disallowed-list': [], - 'property-no-vendor-prefix': null, - 'property-allowed-list': null, - - // Declaration - 'declaration-block-no-redundant-longhand-properties': [ - true, - { - ignoreShorthands: ['/flex/', '/grid/'], - }, - ], - 'declaration-no-important': null, - 'declaration-property-unit-disallowed-list': {}, - 'declaration-property-unit-allowed-list': null, - 'declaration-property-value-disallowed-list': { - border: ['none'], - 'border-top': ['none'], - 'border-right': ['none'], - 'border-bottom': ['none'], - 'border-left': ['none'], - }, - 'declaration-property-value-allowed-list': null, - - // Declaration block - 'declaration-block-single-line-max-declarations': 1, - - // Selector - 'selector-attribute-operator-disallowed-list': [], - 'selector-attribute-operator-allowed-list': null, - 'selector-class-pattern': null, - 'selector-combinator-disallowed-list': [], - 'selector-combinator-allowed-list': null, - 'selector-id-pattern': '', - 'selector-max-attribute': 2, - 'selector-max-class': null, - 'selector-max-combinators': null, - 'selector-max-compound-selectors': null, - 'selector-max-id': 8, - 'selector-max-pseudo-class': null, - 'selector-max-specificity': null, - 'selector-max-type': null, - 'selector-max-universal': 2, - 'selector-nested-pattern': '', - 'selector-no-qualifying-type': null, - 'selector-no-vendor-prefix': true, - 'selector-pseudo-class-disallowed-list': [], - 'selector-pseudo-class-allowed-list': null, - 'selector-pseudo-element-disallowed-list': [], - 'selector-pseudo-element-allowed-list': ['before', 'after', 'marker', 'v-deep'], - - // Media feature - 'media-feature-name-disallowed-list': [], - 'media-feature-name-no-vendor-prefix': true, - 'media-feature-name-value-allowed-list': null, - 'media-feature-name-allowed-list': null, - - // Custom media - 'custom-media-pattern': '', - - // At-rule - 'at-rule-disallowed-list': [], - 'at-rule-no-vendor-prefix': true, - 'at-rule-property-required-list': {}, - 'at-rule-allowed-list': null, - - // Comment - 'comment-word-disallowed-list': [], - - // General / Sheet - 'max-nesting-depth': 12, - 'no-unknown-animations': null, - - // ---------------------------- Stylistic issues - // Color - 'color-hex-length': 'short', - - // Font family - 'font-family-name-quotes': 'always-unless-keyword', - - // Font weight - 'font-weight-notation': null, - - // Function - 'function-name-case': [ - 'lower', - { - ignoreFunctions: ['DXImageTransform.Microsoft.gradient'], - }, - ], - 'function-url-quotes': 'always', - - // Length - 'length-zero-no-unit': true, - - // Value - 'value-keyword-case': 'lower', - - // Custom property - 'custom-property-empty-line-before': 'never', - - // Declaration - 'declaration-empty-line-before': [ - 'always', - { - except: ['after-comment', 'after-declaration', 'first-nested'], - }, - ], - - // Selector - 'selector-attribute-quotes': 'always', - 'selector-pseudo-element-colon-notation': 'double', - 'selector-type-case': 'lower', - - // Rule - 'rule-empty-line-before': [ - 'always', - { - except: ['after-single-line-comment', 'first-nested'], - ignore: ['after-comment'], - }, - ], - - // At-rule - 'at-rule-empty-line-before': null, - - // Comment - 'comment-empty-line-before': 'always', - 'comment-whitespace-inside': 'always', - - - // ------------------------------------------------------ Stylistic - // Selector - '@stylistic/selector-max-empty-lines': 0, - - // Color - '@stylistic/color-hex-case': 'lower', - - // Function - '@stylistic/function-comma-newline-after': null, - '@stylistic/function-comma-newline-before': 'always-multi-line', - '@stylistic/function-comma-space-after': 'always', - '@stylistic/function-comma-space-before': 'never', - '@stylistic/function-max-empty-lines': 0, - '@stylistic/function-parentheses-newline-inside': 'never-multi-line', - '@stylistic/function-parentheses-space-inside': 'never', - '@stylistic/function-whitespace-after': 'always', - - // Number - '@stylistic/number-leading-zero': null, - '@stylistic/number-no-trailing-zeros': true, - - // String - '@stylistic/string-quotes': 'single', - - // Unit - '@stylistic/unit-case': 'lower', - - // Value list - '@stylistic/value-list-comma-newline-after': null, - '@stylistic/value-list-comma-newline-before': 'never-multi-line', - '@stylistic/value-list-comma-space-after': 'always-single-line', - '@stylistic/value-list-comma-space-before': 'never', - '@stylistic/value-list-max-empty-lines': 0, - - // Property - '@stylistic/property-case': 'lower', - - // Declaration - '@stylistic/declaration-bang-space-after': 'never', - '@stylistic/declaration-bang-space-before': 'always', - '@stylistic/declaration-colon-newline-after': null, - '@stylistic/declaration-colon-space-after': 'always', - '@stylistic/declaration-colon-space-before': 'never', - - // Declaration block - '@stylistic/declaration-block-semicolon-newline-after': 'always', - '@stylistic/declaration-block-semicolon-newline-before': 'never-multi-line', - '@stylistic/declaration-block-semicolon-space-after': 'always-single-line', - '@stylistic/declaration-block-semicolon-space-before': 'never', - '@stylistic/declaration-block-trailing-semicolon': 'always', - - // Block - '@stylistic/block-closing-brace-empty-line-before': 'never', - '@stylistic/block-closing-brace-newline-after': 'always', - '@stylistic/block-closing-brace-newline-before': 'always', - '@stylistic/block-closing-brace-space-after': 'always-single-line', - '@stylistic/block-closing-brace-space-before': 'always-single-line', - '@stylistic/block-opening-brace-newline-after': 'always', - '@stylistic/block-opening-brace-newline-before': 'never-single-line', - '@stylistic/block-opening-brace-space-after': 'never-single-line', - '@stylistic/block-opening-brace-space-before': 'always', - - // Selector - '@stylistic/selector-attribute-brackets-space-inside': 'never', - '@stylistic/selector-attribute-operator-space-after': 'never', - '@stylistic/selector-attribute-operator-space-before': 'never', - '@stylistic/selector-combinator-space-after': 'always', - '@stylistic/selector-combinator-space-before': 'always', - '@stylistic/selector-descendant-combinator-no-non-space': true, - '@stylistic/selector-pseudo-class-case': 'lower', - '@stylistic/selector-pseudo-class-parentheses-space-inside': 'never', - '@stylistic/selector-pseudo-element-case': 'lower', - - // Selector list - '@stylistic/selector-list-comma-newline-after': 'always', - '@stylistic/selector-list-comma-newline-before': 'never-multi-line', - '@stylistic/selector-list-comma-space-after': 'never-single-line', - '@stylistic/selector-list-comma-space-before': 'never', - - // Media feature - '@stylistic/media-feature-colon-space-after': 'always', - '@stylistic/media-feature-colon-space-before': 'never', - '@stylistic/media-feature-name-case': 'lower', - '@stylistic/media-feature-parentheses-space-inside': 'never', - '@stylistic/media-feature-range-operator-space-after': 'always', - '@stylistic/media-feature-range-operator-space-before': 'never', - - // Media query list - '@stylistic/media-query-list-comma-newline-after': 'always-multi-line', - '@stylistic/media-query-list-comma-newline-before': 'never-multi-line', - '@stylistic/media-query-list-comma-space-after': 'always', - '@stylistic/media-query-list-comma-space-before': 'never', - - // At-rule - '@stylistic/at-rule-name-case': 'lower', - '@stylistic/at-rule-name-newline-after': null, - '@stylistic/at-rule-name-space-after': 'always', - '@stylistic/at-rule-semicolon-newline-after': 'always', - '@stylistic/at-rule-semicolon-space-before': 'never', - - // General / Sheet - '@stylistic/indentation': 'tab', - '@stylistic/linebreaks': 'unix', - '@stylistic/max-empty-lines': 2, - '@stylistic/max-line-length': null, - '@stylistic/no-eol-whitespace': true, - '@stylistic/no-missing-end-of-source-newline': true, - '@stylistic/no-empty-first-line': true, - '@stylistic/no-extra-semicolons': true, - '@stylistic/unicode-bom': 'never', - - - // ------------------------------------------------------ SCSS - - /* - * Stylelint SCSS Rules Reference: https://github.com/kristerkari/stylelint-scss - */ - - 'scss/load-no-partial-leading-underscore': true, - - // @-each - 'scss/at-each-key-value-single-line': true, - - // @-else - 'scss/at-else-closing-brace-newline-after': 'always-last-in-chain', - 'scss/at-else-closing-brace-space-after': 'always-intermediate', - 'scss/at-else-empty-line-before': 'never', - 'scss/at-else-if-parentheses-space-before': 'always', - - // @-extend - 'scss/at-extend-no-missing-placeholder': null, - - // @-function - 'scss/at-function-named-arguments': null, - 'scss/at-function-parentheses-space-before': 'always', - 'scss/at-function-pattern': namePattern, - - // @-if - 'scss/at-if-closing-brace-newline-after': 'always-last-in-chain', - 'scss/at-if-closing-brace-space-after': 'always-intermediate', - 'scss/at-if-no-null': true, - - // @-import - 'scss/at-import-partial-extension': null, - 'scss/at-import-partial-extension-blacklist': null, - 'scss/at-import-partial-extension-whitelist': null, - - // @-mixin - 'scss/at-mixin-argumentless-call-parentheses': null, - 'scss/at-mixin-named-arguments': null, - 'scss/at-mixin-parentheses-space-before': 'never', - 'scss/at-mixin-pattern': namePattern, - - // @-rule - 'scss/at-rule-conditional-no-parentheses': null, - 'scss/at-rule-no-unknown': [ - true, - { - 'ignoreAtRules': ['tailwind'], - }, - ], - - // $-variable - 'scss/dollar-variable-colon-newline-after': null, - 'scss/dollar-variable-colon-space-after': 'always', - 'scss/dollar-variable-colon-space-before': 'never', - 'scss/dollar-variable-default': null, - 'scss/dollar-variable-empty-line-after': [ - 'always', - { - except: ['last-nested', 'before-dollar-variable'], - ignore: ['inside-single-line-block'], - }, - ], - 'scss/dollar-variable-empty-line-before': null, - 'scss/dollar-variable-first-in-block': null, - 'scss/dollar-variable-no-missing-interpolation': true, - 'scss/dollar-variable-pattern': namePattern, - - // %-placeholder - 'scss/percent-placeholder-pattern': namePattern, - - 'scss/double-slash-comment-empty-line-before': [ - 'always', - { - except: ['first-nested', 'inside-block'], - ignore: ['between-comments', 'stylelint-commands', 'inside-block'], - }, - ], - 'scss/double-slash-comment-inline': [ - 'never', - { - ignore: ['stylelint-commands'], - }, - ], - 'scss/double-slash-comment-whitespace-inside': 'always', - - // Comment - 'scss/comment-no-loud': null, - - // Declaration - 'scss/declaration-nested-properties': 'never', - 'scss/declaration-nested-properties-no-divided-groups': true, - - // Dimension - 'scss/dimension-no-non-numeric-values': null, - - // Function - 'scss/function-color-relative': null, - 'scss/function-quote-no-quoted-strings-inside': null, - 'scss/function-unquote-no-unquoted-strings-inside': null, - - // Map - 'scss/map-keys-quotes': null, - - // Media feature - 'scss/media-feature-value-dollar-variable': null, - - // Operator - 'scss/operator-no-newline-after': true, - 'scss/operator-no-newline-before': true, - 'scss/operator-no-unspaced': true, - - // Partial - 'scss/partial-no-import': null, - - // Selector - 'scss/selector-nest-combinators': null, - 'scss/selector-no-redundant-nesting-selector': true, - 'scss/selector-no-union-class-name': null, - - // General / Sheet - 'scss/no-dollar-variables': null, - 'scss/no-duplicate-dollar-variables': true, - 'scss/no-duplicate-mixins': true, - 'scss/no-global-function-names': null, - - // ------------------------------------------------------ Order - - /* - * Stylelint Order Rules Reference: https://github.com/hudochenkov/stylelint-order - */ - - 'order/order': [ - [ - 'custom-properties', - 'dollar-variables', - { - type: 'at-rule', - name: 'extend', - }, - { - type: 'at-rule', - name: 'include', - hasBlock: false, - }, - { - type: 'at-rule', - name: 'include', - hasBlock: true, - }, - ], - ], - 'order/properties-order': [], - 'order/properties-alphabetical-order': { - order: 'flexible', - }, - }, + extends: ['@wdns/stylelint-config-wdns'], }; diff --git a/tsconfig.app.json b/tsconfig.app.json new file mode 100644 index 0000000..a7592e5 --- /dev/null +++ b/tsconfig.app.json @@ -0,0 +1,89 @@ +{ + "compilerOptions": { + "allowSyntheticDefaultImports": true, + "baseUrl": "./", + "composite": true, + "declaration": true, + "declarationDir": "./dist", + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "importHelpers": true, + "jsx": "preserve", + "lib": [ + "ESNext", + "DOM" + ], + "module": "ESNext", + "moduleResolution": "Node", + "noEmit": true, + "noFallthroughCasesInSwitch": true, + "noImplicitAny": false, + "noUncheckedIndexedAccess": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "outDir": "./dist", + "paths": { + "@/*": [ + "./src/*" + ], + "@components/*": [ + "./src/plugin/components/*" + ], + "@composables/*": [ + "./src/plugin/composables/*" + ], + "@data/*": [ + "./src/plugin/data/*" + ], + "@plugin/*": [ + "./src/plugin/*" + ], + "@root/*": [ + "./*" + ], + "@slots/*": [ + "./src/plugin/slots/*" + ], + "@types/*": [ + "./src/plugin/types/*" + ] + }, + "resolveJsonModule": true, + "rootDir": "./src", + "skipLibCheck": true, + "strict": true, + "target": "ESNext", + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "typeRoots": [ + "./src/plugin/types", + "./src/documentation/types", + "./node_modules/@types", + "./node_modules/vuetify" + ], + "types": [ + "node" + ], + "useDefineForClassFields": true + }, + "include": [ + "env.d.ts", + "src/**/*", + "src/**/*.vue", + "eslint.config.mjs", + "src/App.vue", + "src/main.ts", + "src/playground/**/*.ts", + "src/playground/**/*.vue", + "src/documentation/**/*.ts", + "src/documentation/**/*.tsx", + "src/documentation/**/*.vue", + "src/plugin/**/*.ts", + "src/plugin/**/*.tsx", + "src/plugin/**/*.vue" + ], + "exclude": [ + "src/**/__tests__/*", + "playground", + "src/playground/configs/templates/PlaygroundPage.vue" + ], +} diff --git a/tsconfig.build.json b/tsconfig.build.json new file mode 100644 index 0000000..86d049e --- /dev/null +++ b/tsconfig.build.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "allowImportingTsExtensions": false, + }, + "exclude": [ + "**/*__TEMP.*", + "**/*__TEMP/", + "node_modules", + "playground", + "src/**/*.spec.ts", + "src/**/*.test.ts", + "src/App.vue", + "src/documentation/**/*.ts", + "src/documentation/**/*.tsx", + "src/documentation/**/*.vue", + "src/libraries/**/*.ts", + "src/main.ts", + "src/playground/**/*.ts", + "src/playground/**/*.vue", + "src/plugins/**/*.ts", + "src/stores/**/*.ts", + ], + "extends": "./tsconfig.app.json" +} diff --git a/tsconfig.dist.json b/tsconfig.dist.json deleted file mode 100644 index e439464..0000000 --- a/tsconfig.dist.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": [ - "src/plugin/**/*.ts", - "src/plugin/**/*.vue" - ] -} diff --git a/tsconfig.json b/tsconfig.json index 7d9634e..4cf62c6 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,61 +1,14 @@ { - "compilerOptions": { - "allowSyntheticDefaultImports": true, - "baseUrl": "./", - "declaration": true, - "declarationDir": "./dist", - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "importHelpers": true, - "jsx": "preserve", - "lib": [ - "ESNext", - "DOM" - ], - "module": "ESNext", - "moduleResolution": "Node", - "noEmit": true, - "noFallthroughCasesInSwitch": true, - "noImplicitAny": false, - "noUncheckedIndexedAccess": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "outDir": "./dist", - "paths": { - "@/*": [ - "./src/*" - ] - }, - "resolveJsonModule": true, - "rootDir": "src", - "skipLibCheck": true, - "strict": true, - "target": "ESNext", - "typeRoots": [ - "./src/plugin/types", - "./node_modules/@types" - ], - "types": [ - "node" - ], - "useDefineForClassFields": true - }, - "exclude": [ - "src/**/*.spec.ts", - "src/**/*.test.ts", - "src/**/*.test.ts.snap", - "node_modules" - ], - "include": [ - "src/playground/configs/templates/PlaygroundPage.ts.vue", - "src/playground/PlaygroundPage.ts.vue", - "src/plugin/**/*.ts", - "src/plugin/**/*.tsx", - "src/plugin/**/*.vue" - ], + "files": [], "references": [ { "path": "./tsconfig.node.json" + }, + { + "path": "./tsconfig.app.json" } - ] + ], + "compilerOptions": { + "module": "ESNext" + } } diff --git a/tsconfig.node.json b/tsconfig.node.json index 0469640..84d4640 100644 --- a/tsconfig.node.json +++ b/tsconfig.node.json @@ -1,9 +1,18 @@ { - "compilerOptions": { - "composite": true, - "module": "ESNext", - "moduleResolution": "Node", - "allowSyntheticDefaultImports": true - }, - "include": ["vite.config.mts"] + "include": [ + "vite.config.*", + "vitest.config.*", + ], + "compilerOptions": { + "allowSyntheticDefaultImports": true, + "composite": true, + "module": "ESNext", + "moduleResolution": "Bundler", + "noEmit": true, + "skipLibCheck": true, + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "types": [ + "node" + ] + } } diff --git a/vite.build.config.mts b/vite.build.config.mts index 6f96f6b..81c5086 100644 --- a/vite.build.config.mts +++ b/vite.build.config.mts @@ -8,8 +8,10 @@ import pkg from './package.json'; import terser from '@rollup/plugin-terser'; import typescript from 'rollup-plugin-typescript2'; import vue from '@vitejs/plugin-vue'; + import { viteStaticCopy } from 'vite-plugin-static-copy'; + const scopedPackageName = pkg.name; const packageName = scopedPackageName.split('/')[1]; @@ -40,6 +42,7 @@ export default defineConfig({ }, external: [ ...Object.keys(pkg.dependencies || {}), + /^vuetify($|\/.+)/, ], output: { banner, @@ -54,22 +57,20 @@ export default defineConfig({ imports: [ 'vue', { - vue: ['CSSProperties'] + vue: ['CSSProperties'], } ], vueTemplate: true, }), - vue({ - script: { - defineModel: true, - }, - }), + vue(), dts({ insertTypesEntry: true, + tsconfigPath: 'tsconfig.build.json', }), typescript({ check: true, include: ['./src/plugin/**/*.vue'], + tsconfig: 'tsconfig.build.json', }), cssInjectedByJsPlugin({ topExecutionPriority: false }), viteStaticCopy({ diff --git a/vite.config.mts b/vite.config.mts index 192eef9..3353d1a 100644 --- a/vite.config.mts +++ b/vite.config.mts @@ -1,11 +1,12 @@ import vue from '@vitejs/plugin-vue'; import vuetify, { transformAssetUrls } from 'vite-plugin-vuetify'; -import eslint from 'vite-plugin-eslint'; +import eslint from 'vite-plugin-eslint2'; import stylelint from 'vite-plugin-stylelint'; import { defineConfig } from 'vite'; import { fileURLToPath, URL } from 'node:url'; import AutoImport from 'unplugin-auto-import/vite'; + const baseUrl = '/vue-code-block/'; const playgroundUrl = baseUrl + 'playground/'; @@ -15,9 +16,20 @@ export default defineConfig({ build: { outDir: 'docs', }, + css: { + preprocessorOptions: { + scss: { + api: 'modern-compiler', + importers: [], + }, + }, + transformer: 'postcss', + }, plugins: [ eslint({ fix: true, + exclude: ['node_modules/**', 'vendor/**'], + include: ['src/**/*.{ts,mts,tsx,vue}'], }), stylelint({ cache: false, @@ -48,7 +60,13 @@ export default defineConfig({ resolve: { alias: { '@': fileURLToPath(new URL('./src', import.meta.url)), + '@components': fileURLToPath(new URL('./src/plugin/components/', import.meta.url)), + '@composables': fileURLToPath(new URL('./src/plugin/composables', import.meta.url)), + '@data': fileURLToPath(new URL('./src/plugin/data', import.meta.url)), + '@plugin': fileURLToPath(new URL('./src/plugin', import.meta.url)), '@root': fileURLToPath(new URL('.', import.meta.url)), + '@slots': fileURLToPath(new URL('./src/plugin/slots', import.meta.url)), + '@types': fileURLToPath(new URL('./src/plugin/types', import.meta.url)), }, extensions: [ '.js', @@ -62,10 +80,12 @@ export default defineConfig({ ], }, server: { + host: '127.0.0.1', + port: 3000, hmr: { protocol: 'ws', }, - open: process?.env?.NODE_ENV === 'playground' ? playgroundUrl : false, + open: process?.env?.NODE_ENV === 'playground' ? playgroundUrl : true, }, });