mirror of
https://github.com/ArthurDanjou/ui.git
synced 2026-01-14 12:14:41 +01:00
feat(module): HMR support with @nuxtjs/tailwindcss (#1665)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { createResolver } from '@nuxt/kit'
|
||||
import colors from 'tailwindcss/colors'
|
||||
import module from '../src/module'
|
||||
import { excludeColors } from '../src/colors'
|
||||
import { excludeColors } from '../src/runtime/utils/colors'
|
||||
import pkg from '../package.json'
|
||||
|
||||
const { resolve } = createResolver(import.meta.url)
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
"@iconify-json/heroicons": "^1.1.20",
|
||||
"@nuxt/kit": "^3.11.2",
|
||||
"@nuxtjs/color-mode": "^3.4.0",
|
||||
"@nuxtjs/tailwindcss": "^6.11.4",
|
||||
"@nuxtjs/tailwindcss": "^6.12.0",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"@tailwindcss/aspect-ratio": "^0.4.2",
|
||||
"@tailwindcss/container-queries": "^0.1.1",
|
||||
|
||||
17294
pnpm-lock.yaml
generated
17294
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
102
src/module.ts
102
src/module.ts
@@ -1,14 +1,12 @@
|
||||
import { createRequire } from 'node:module'
|
||||
import { defineNuxtModule, installModule, addComponentsDir, addImportsDir, createResolver, addPlugin } from '@nuxt/kit'
|
||||
import { defaultExtractor as createDefaultExtractor } from 'tailwindcss/lib/lib/defaultExtractor.js'
|
||||
import { iconsPlugin, getIconCollections, type CollectionNames, type IconsPluginOptions } from '@egoist/tailwindcss-icons'
|
||||
import type { CollectionNames, IconsPluginOptions } from '@egoist/tailwindcss-icons'
|
||||
import { name, version } from '../package.json'
|
||||
import createTemplates from './templates'
|
||||
import { generateSafelist, excludeColors, customSafelistExtractor } from './colors'
|
||||
import * as config from './runtime/ui.config'
|
||||
import type { DeepPartial, Strategy } from './runtime/types/utils'
|
||||
import installTailwind from './tailwind'
|
||||
|
||||
const defaultExtractor = createDefaultExtractor({ tailwindConfig: { separator: ':' } })
|
||||
const _require = createRequire(import.meta.url)
|
||||
const defaultColors = _require('tailwindcss/colors.js')
|
||||
|
||||
@@ -88,107 +86,13 @@ export default defineNuxtModule<ModuleOptions>({
|
||||
nuxt.options.css.push(resolve(runtimeDir, 'ui.css'))
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
nuxt.hook('tailwindcss:config', function (tailwindConfig) {
|
||||
tailwindConfig.theme = tailwindConfig.theme || {}
|
||||
tailwindConfig.theme.extend = tailwindConfig.theme.extend || {}
|
||||
tailwindConfig.theme.extend.colors = tailwindConfig.theme.extend.colors || {}
|
||||
|
||||
const globalColors: any = {
|
||||
...(tailwindConfig.theme.colors || defaultColors),
|
||||
...tailwindConfig.theme.extend?.colors
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
globalColors.primary = tailwindConfig.theme.extend.colors.primary = {
|
||||
50: 'rgb(var(--color-primary-50) / <alpha-value>)',
|
||||
100: 'rgb(var(--color-primary-100) / <alpha-value>)',
|
||||
200: 'rgb(var(--color-primary-200) / <alpha-value>)',
|
||||
300: 'rgb(var(--color-primary-300) / <alpha-value>)',
|
||||
400: 'rgb(var(--color-primary-400) / <alpha-value>)',
|
||||
500: 'rgb(var(--color-primary-500) / <alpha-value>)',
|
||||
600: 'rgb(var(--color-primary-600) / <alpha-value>)',
|
||||
700: 'rgb(var(--color-primary-700) / <alpha-value>)',
|
||||
800: 'rgb(var(--color-primary-800) / <alpha-value>)',
|
||||
900: 'rgb(var(--color-primary-900) / <alpha-value>)',
|
||||
950: 'rgb(var(--color-primary-950) / <alpha-value>)',
|
||||
DEFAULT: 'rgb(var(--color-primary-DEFAULT) / <alpha-value>)'
|
||||
}
|
||||
|
||||
if (globalColors.gray) {
|
||||
// @ts-ignore
|
||||
globalColors.cool = tailwindConfig.theme.extend.colors.cool = defaultColors.gray
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
globalColors.gray = tailwindConfig.theme.extend.colors.gray = {
|
||||
50: 'rgb(var(--color-gray-50) / <alpha-value>)',
|
||||
100: 'rgb(var(--color-gray-100) / <alpha-value>)',
|
||||
200: 'rgb(var(--color-gray-200) / <alpha-value>)',
|
||||
300: 'rgb(var(--color-gray-300) / <alpha-value>)',
|
||||
400: 'rgb(var(--color-gray-400) / <alpha-value>)',
|
||||
500: 'rgb(var(--color-gray-500) / <alpha-value>)',
|
||||
600: 'rgb(var(--color-gray-600) / <alpha-value>)',
|
||||
700: 'rgb(var(--color-gray-700) / <alpha-value>)',
|
||||
800: 'rgb(var(--color-gray-800) / <alpha-value>)',
|
||||
900: 'rgb(var(--color-gray-900) / <alpha-value>)',
|
||||
950: 'rgb(var(--color-gray-950) / <alpha-value>)'
|
||||
}
|
||||
|
||||
const colors = excludeColors(globalColors)
|
||||
|
||||
// @ts-ignore
|
||||
nuxt.options.appConfig.ui = {
|
||||
primary: 'green',
|
||||
gray: 'cool',
|
||||
colors,
|
||||
strategy: 'merge'
|
||||
}
|
||||
|
||||
tailwindConfig.safelist = tailwindConfig.safelist || []
|
||||
tailwindConfig.safelist.push(...generateSafelist(options.safelistColors || [], colors))
|
||||
})
|
||||
|
||||
createTemplates(nuxt)
|
||||
|
||||
// Modules
|
||||
|
||||
await installModule('nuxt-icon')
|
||||
await installModule('@nuxtjs/color-mode', { classSuffix: '' })
|
||||
await installModule('@nuxtjs/tailwindcss', {
|
||||
exposeConfig: true,
|
||||
config: {
|
||||
darkMode: 'class',
|
||||
plugins: [
|
||||
require('@tailwindcss/forms')({ strategy: 'class' }),
|
||||
require('@tailwindcss/aspect-ratio'),
|
||||
require('@tailwindcss/typography'),
|
||||
require('@tailwindcss/container-queries'),
|
||||
require('@headlessui/tailwindcss'),
|
||||
iconsPlugin(Array.isArray(options.icons) || options.icons === 'all' ? { collections: getIconCollections(options.icons) } : typeof options.icons === 'object' ? options.icons as IconsPluginOptions : {})
|
||||
],
|
||||
content: {
|
||||
files: [
|
||||
resolve(runtimeDir, 'components/**/*.{vue,mjs,ts}'),
|
||||
resolve(runtimeDir, 'ui.config/**/*.{mjs,js,ts}')
|
||||
],
|
||||
transform: {
|
||||
vue: (content) => {
|
||||
return content.replaceAll(/(?:\r\n|\r|\n)/g, ' ')
|
||||
}
|
||||
},
|
||||
extract: {
|
||||
vue: (content) => {
|
||||
return [
|
||||
...defaultExtractor(content),
|
||||
// @ts-ignore
|
||||
...customSafelistExtractor(options.prefix, content, nuxt.options.appConfig.ui.colors, options.safelistColors)
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
await installTailwind(options, nuxt, resolve)
|
||||
|
||||
// Plugins
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { omit } from './runtime/utils/lodash'
|
||||
import { omit } from './lodash'
|
||||
import { kebabCase, camelCase, upperFirst } from 'scule'
|
||||
import type { Config as TWConfig } from 'tailwindcss'
|
||||
import defaultColors from 'tailwindcss/colors.js'
|
||||
|
||||
const colorsToExclude = [
|
||||
'inherit',
|
||||
@@ -15,7 +17,7 @@ const colorsToExclude = [
|
||||
'cool'
|
||||
]
|
||||
|
||||
const safelistByComponent = {
|
||||
const safelistByComponent: Record<string, (colors: string) => TWConfig['safelist']> = {
|
||||
alert: (colorsAsRegex) => [{
|
||||
pattern: new RegExp(`bg-(${colorsAsRegex})-50`)
|
||||
}, {
|
||||
@@ -217,13 +219,61 @@ const safelistComponentAliasesMap = {
|
||||
|
||||
const colorsAsRegex = (colors: string[]): string => colors.join('|')
|
||||
|
||||
export const excludeColors = (colors: object): string[] => {
|
||||
type ColorConfig = Exclude<TWConfig['theme']['colors'], Function>
|
||||
|
||||
export const excludeColors = (colors: ColorConfig | typeof defaultColors): string[] => {
|
||||
return Object.entries(omit(colors, colorsToExclude))
|
||||
.filter(([, value]) => typeof value === 'object')
|
||||
.map(([key]) => kebabCase(key))
|
||||
}
|
||||
|
||||
export const generateSafelist = (colors: string[], globalColors) => {
|
||||
export const setGlobalColors = (theme: TWConfig['theme']) => {
|
||||
const globalColors: ColorConfig = {
|
||||
...(theme.colors || defaultColors),
|
||||
...theme.extend?.colors
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
globalColors.primary = theme.extend.colors.primary = {
|
||||
50: 'rgb(var(--color-primary-50) / <alpha-value>)',
|
||||
100: 'rgb(var(--color-primary-100) / <alpha-value>)',
|
||||
200: 'rgb(var(--color-primary-200) / <alpha-value>)',
|
||||
300: 'rgb(var(--color-primary-300) / <alpha-value>)',
|
||||
400: 'rgb(var(--color-primary-400) / <alpha-value>)',
|
||||
500: 'rgb(var(--color-primary-500) / <alpha-value>)',
|
||||
600: 'rgb(var(--color-primary-600) / <alpha-value>)',
|
||||
700: 'rgb(var(--color-primary-700) / <alpha-value>)',
|
||||
800: 'rgb(var(--color-primary-800) / <alpha-value>)',
|
||||
900: 'rgb(var(--color-primary-900) / <alpha-value>)',
|
||||
950: 'rgb(var(--color-primary-950) / <alpha-value>)',
|
||||
DEFAULT: 'rgb(var(--color-primary-DEFAULT) / <alpha-value>)'
|
||||
}
|
||||
|
||||
if (globalColors.gray) {
|
||||
// @ts-ignore
|
||||
globalColors.cool = theme.extend.colors.cool =
|
||||
defaultColors.gray
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
globalColors.gray = theme.extend.colors.gray = {
|
||||
50: 'rgb(var(--color-gray-50) / <alpha-value>)',
|
||||
100: 'rgb(var(--color-gray-100) / <alpha-value>)',
|
||||
200: 'rgb(var(--color-gray-200) / <alpha-value>)',
|
||||
300: 'rgb(var(--color-gray-300) / <alpha-value>)',
|
||||
400: 'rgb(var(--color-gray-400) / <alpha-value>)',
|
||||
500: 'rgb(var(--color-gray-500) / <alpha-value>)',
|
||||
600: 'rgb(var(--color-gray-600) / <alpha-value>)',
|
||||
700: 'rgb(var(--color-gray-700) / <alpha-value>)',
|
||||
800: 'rgb(var(--color-gray-800) / <alpha-value>)',
|
||||
900: 'rgb(var(--color-gray-900) / <alpha-value>)',
|
||||
950: 'rgb(var(--color-gray-950) / <alpha-value>)'
|
||||
}
|
||||
|
||||
return excludeColors(globalColors)
|
||||
}
|
||||
|
||||
export const generateSafelist = (colors: string[], globalColors: string[]) => {
|
||||
const baseSafelist = Object.keys(safelistByComponent).flatMap(component => safelistByComponent[component](colorsAsRegex(colors)))
|
||||
|
||||
// Ensure `red` color is safelisted for form elements so that `error` prop of `UFormGroup` always works
|
||||
@@ -242,7 +292,8 @@ export const generateSafelist = (colors: string[], globalColors) => {
|
||||
]
|
||||
}
|
||||
|
||||
export const customSafelistExtractor = (prefix, content: string, colors: string[], safelistColors: string[]) => {
|
||||
type SafelistFn = Exclude<NonNullable<Extract<TWConfig['content'], { extract?: unknown }>['extract']>, Record<string, unknown>>
|
||||
export const customSafelistExtractor = (prefix: string, content: string, colors: string[], safelistColors: string[]): ReturnType<SafelistFn> => {
|
||||
const classes: string[] = []
|
||||
const regex = /<([A-Za-z][A-Za-z0-9]*(?:-[A-Za-z][A-Za-z0-9]*)*)\s+(?![^>]*:color\b)[^>]*\bcolor=["']([^"']+)["'][^>]*>/gs
|
||||
|
||||
@@ -268,7 +319,7 @@ export const customSafelistExtractor = (prefix, content: string, colors: string[
|
||||
name = name.replace(prefix, '').toLowerCase()
|
||||
|
||||
const matchClasses = safelistByComponent[name](color).flatMap(group => {
|
||||
return ['', ...(group.variants || [])].flatMap(variant => {
|
||||
return typeof group === 'string' ? '' : ['', ...(group.variants || [])].flatMap(variant => {
|
||||
const matches = group.pattern.source.match(/\(([^)]+)\)/g)
|
||||
|
||||
return matches.map(match => {
|
||||
87
src/tailwind.ts
Normal file
87
src/tailwind.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
import { join } from 'pathe'
|
||||
import { defu } from 'defu'
|
||||
import { addTemplate, createResolver, installModule, useNuxt } from '@nuxt/kit'
|
||||
|
||||
import { setGlobalColors } from './runtime/utils/colors'
|
||||
import type { ModuleOptions } from './module'
|
||||
|
||||
export default async function installTailwind (
|
||||
moduleOptions: ModuleOptions,
|
||||
nuxt = useNuxt(),
|
||||
resolve = createResolver(import.meta.url).resolve
|
||||
) {
|
||||
const runtimeDir = resolve('./runtime')
|
||||
|
||||
// 1. register hook
|
||||
// @ts-ignore
|
||||
nuxt.hook('tailwindcss:config', function (tailwindConfig) {
|
||||
tailwindConfig.theme = tailwindConfig.theme || {}
|
||||
tailwindConfig.theme.extend = tailwindConfig.theme.extend || {}
|
||||
tailwindConfig.theme.extend.colors =
|
||||
tailwindConfig.theme.extend.colors || {}
|
||||
|
||||
const colors = setGlobalColors(tailwindConfig.theme)
|
||||
|
||||
// @ts-ignore
|
||||
nuxt.options.appConfig.ui = {
|
||||
primary: 'green',
|
||||
gray: 'cool',
|
||||
colors,
|
||||
strategy: 'merge'
|
||||
}
|
||||
})
|
||||
|
||||
// 2. add config template
|
||||
const configTemplate = addTemplate({
|
||||
filename: 'nuxtui-tailwind.config.cjs',
|
||||
write: true,
|
||||
getContents: ({ nuxt }) => `
|
||||
const { defaultExtractor: createDefaultExtractor } = require('tailwindcss/lib/lib/defaultExtractor.js')
|
||||
const { customSafelistExtractor, generateSafelist } = require(${JSON.stringify(resolve(runtimeDir, 'utils', 'colors'))})
|
||||
const { iconsPlugin, getIconCollections } = require('@egoist/tailwindcss-icons')
|
||||
|
||||
const defaultExtractor = createDefaultExtractor({ tailwindConfig: { separator: ':' } })
|
||||
|
||||
module.exports = {
|
||||
plugins: [
|
||||
require('@tailwindcss/forms')({ strategy: 'class' }),
|
||||
require('@tailwindcss/aspect-ratio'),
|
||||
require('@tailwindcss/typography'),
|
||||
require('@tailwindcss/container-queries'),
|
||||
require('@headlessui/tailwindcss'),
|
||||
iconsPlugin(${Array.isArray(moduleOptions.icons) || moduleOptions.icons === 'all' ? `{ collections: getIconCollections(${JSON.stringify(moduleOptions.icons)}) }` : typeof moduleOptions.icons === 'object' ? JSON.stringify(moduleOptions.icons) : {}})
|
||||
],
|
||||
content: {
|
||||
files: [
|
||||
${JSON.stringify(resolve(runtimeDir, 'components/**/*.{vue,mjs,ts}'))},
|
||||
${JSON.stringify(resolve(runtimeDir, 'ui.config/**/*.{mjs,js,ts}'))}
|
||||
],
|
||||
transform: {
|
||||
vue: (content) => {
|
||||
return content.replaceAll(/(?:\\r\\n|\\r|\\n)/g, ' ')
|
||||
}
|
||||
},
|
||||
extract: {
|
||||
vue: (content) => {
|
||||
return [
|
||||
...defaultExtractor(content),
|
||||
...customSafelistExtractor(${JSON.stringify(moduleOptions.prefix)}, content, ${JSON.stringify(nuxt.options.appConfig.ui.colors)}, ${JSON.stringify(moduleOptions.safelistColors)})
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
safelist: generateSafelist(${JSON.stringify(moduleOptions.safelistColors || [])}, ${JSON.stringify(nuxt.options.appConfig.ui.colors)}),
|
||||
}
|
||||
`
|
||||
})
|
||||
|
||||
// 3. install module
|
||||
await installModule('@nuxtjs/tailwindcss', defu({
|
||||
exposeConfig: true,
|
||||
config: { darkMode: 'class' },
|
||||
configPath: [
|
||||
configTemplate.dst,
|
||||
join(nuxt.options.rootDir, 'tailwind.config')
|
||||
]
|
||||
}, nuxt.options.tailwindcss))
|
||||
}
|
||||
@@ -56,76 +56,3 @@ describe('nuxt', () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('tailwindcss config', () => {
|
||||
it.each([
|
||||
/* format:
|
||||
name,
|
||||
tailwindcss config, safelistColors,
|
||||
expected safelistPatterns (add "!" before a pattern to negate it)
|
||||
*/
|
||||
[
|
||||
'default safelist',
|
||||
{}, [],
|
||||
['bg-(primary)-50', 'bg-(red)-500'] // these both should be in the safelist
|
||||
],
|
||||
[
|
||||
'safelisting single new color',
|
||||
{}, ['myColor'],
|
||||
'bg-(myColor|primary)-50'
|
||||
],
|
||||
[
|
||||
'reducing amount of theme colors',
|
||||
{ theme: { colors: { plainBlue: '#00F' } } }, ['plainBlue'],
|
||||
['bg-(plainBlue|primary)-50', '!', /orange/] // the word "orange" should _not_ be found in any safelist pattern
|
||||
]
|
||||
])('%s', async (_description, tailwindcss, safelistColors, safelistPatterns) => {
|
||||
const { tailwindConfig } = await getTailwindCSSConfig({
|
||||
ui: {
|
||||
safelistColors
|
||||
},
|
||||
tailwindcss: {
|
||||
config: tailwindcss
|
||||
}
|
||||
})
|
||||
expect.extend({
|
||||
toBeRegExp: (received, expected) => {
|
||||
if (typeof expected === 'string' || expected instanceof String) {
|
||||
return {
|
||||
message: () => `expected ${received} to be exact regex ${expected}`,
|
||||
pass: received.toString() === RegExp(expected as string).toString()
|
||||
}
|
||||
} else if (expected instanceof RegExp) {
|
||||
return {
|
||||
message: () => `expected ${received} to be a regex like ${expected.toString()}`,
|
||||
pass: received.toString().match(expected)
|
||||
}
|
||||
}
|
||||
return {
|
||||
message: () => `expected ${received} to be a regex`,
|
||||
pass: false
|
||||
}
|
||||
}
|
||||
})
|
||||
safelistPatterns = safelistPatterns instanceof Array ? safelistPatterns : [safelistPatterns]
|
||||
|
||||
let negate = false
|
||||
for (const safelistPattern of safelistPatterns) {
|
||||
if (safelistPattern === '!') {
|
||||
// negate next!
|
||||
negate = true
|
||||
continue
|
||||
}
|
||||
if (negate) {
|
||||
expect(tailwindConfig.safelist).not.toContainEqual({
|
||||
pattern: expect.toBeRegExp(safelistPattern)
|
||||
})
|
||||
} else {
|
||||
expect(tailwindConfig.safelist).toContainEqual({
|
||||
pattern: expect.toBeRegExp(safelistPattern)
|
||||
})
|
||||
}
|
||||
negate = false
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
88
test/colors.spec.ts
Normal file
88
test/colors.spec.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { excludeColors, generateSafelist, setGlobalColors } from '../src/runtime/utils/colors'
|
||||
import defaultColors from 'tailwindcss/colors'
|
||||
import type { Config as TWConfig } from 'tailwindcss'
|
||||
|
||||
describe('excludeColors', () => {
|
||||
it('should exclude colors from the list', () => {
|
||||
const twColors = {
|
||||
red: defaultColors.red,
|
||||
zinc: defaultColors.zinc,
|
||||
blue: defaultColors.blue
|
||||
}
|
||||
expect(excludeColors(twColors).join(',')).toBe('red,blue')
|
||||
})
|
||||
})
|
||||
|
||||
describe('generateSafelist', () => {
|
||||
it.each([
|
||||
/* format:
|
||||
name,
|
||||
tailwindcss config, safelistColors,
|
||||
expected safelistPatterns (add "!" before a pattern to negate it)
|
||||
*/
|
||||
[
|
||||
'default safelist',
|
||||
{}, [],
|
||||
['bg-(primary)-50', 'bg-(red)-500'] // these both should be in the safelist
|
||||
],
|
||||
[
|
||||
'safelisting single new color',
|
||||
{}, ['myColor'],
|
||||
'bg-(myColor|primary)-50'
|
||||
],
|
||||
[
|
||||
'reducing amount of theme colors',
|
||||
{ theme: { colors: { plainBlue: '#00F' } } }, ['plainBlue'],
|
||||
['bg-(plainBlue|primary)-50', '!', /orange/] // the word "orange" should _not_ be found in any safelist pattern
|
||||
]
|
||||
])('%s', async (_description, tailwindConfig: Partial<TWConfig>, safelistColors, safelistPatterns) => {
|
||||
safelistColors.push('primary')
|
||||
tailwindConfig.theme = tailwindConfig.theme || {}
|
||||
tailwindConfig.theme.extend = tailwindConfig.theme.extend || {}
|
||||
tailwindConfig.theme.extend.colors =
|
||||
tailwindConfig.theme.extend.colors || {}
|
||||
const safelistConfig = generateSafelist(safelistColors, setGlobalColors(tailwindConfig.theme))
|
||||
|
||||
expect.extend({
|
||||
toBeRegExp: (received, expected) => {
|
||||
if (typeof expected === 'string' || expected instanceof String) {
|
||||
return {
|
||||
message: () => `expected ${received} to be exact regex ${expected}`,
|
||||
pass: received.toString() === RegExp(expected as string).toString()
|
||||
}
|
||||
} else if (expected instanceof RegExp) {
|
||||
return {
|
||||
message: () => `expected ${received} to be a regex like ${expected.toString()}`,
|
||||
pass: received.toString().match(expected)
|
||||
}
|
||||
}
|
||||
return {
|
||||
message: () => `expected ${received} to be a regex`,
|
||||
pass: false
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
safelistPatterns = safelistPatterns instanceof Array ? safelistPatterns : [safelistPatterns]
|
||||
|
||||
let negate = false
|
||||
for (const safelistPattern of safelistPatterns) {
|
||||
if (safelistPattern === '!') {
|
||||
// negate next!
|
||||
negate = true
|
||||
continue
|
||||
}
|
||||
if (negate) {
|
||||
expect(safelistConfig).not.toContainEqual({
|
||||
pattern: expect.toBeRegExp(safelistPattern)
|
||||
})
|
||||
} else {
|
||||
expect(safelistConfig).toContainEqual({
|
||||
pattern: expect.toBeRegExp(safelistPattern)
|
||||
})
|
||||
}
|
||||
negate = false
|
||||
}
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user