Skip to content

Commit

Permalink
Fix bundle and log extractor config errors
Browse files Browse the repository at this point in the history
  • Loading branch information
barvian committed May 26, 2024
1 parent 269b2e5 commit fde4a15
Show file tree
Hide file tree
Showing 12 changed files with 261 additions and 80 deletions.
7 changes: 4 additions & 3 deletions plugin/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "fluid-tailwind",
"version": "0.3.2",
"version": "0.3.3",
"author": "Maxwell Barvian",
"main": "dist/index.js",
"module": "dist/index.mjs",
Expand All @@ -24,7 +24,7 @@
],
"scripts": {
"test": "bun test",
"build": "tsup src/index.ts --format esm,cjs --no-splitting --clean && tsc -p tsconfig.build.json --emitDeclarationOnly",
"build": "tsup-node src/index.ts --format esm,cjs --no-splitting --clean && tsc -p tsconfig.build.json --emitDeclarationOnly",
"dev": "pnpm run build --watch",
"prepublishOnly": "pnpm run build && pnpm run test"
},
Expand All @@ -37,8 +37,9 @@
},
"devDependencies": {
"@tailwindcss/container-queries": "^0.1.1",
"@types/bun": "^1.0.5",
"@types/bun": "^1.1.3",
"@types/dlv": "^1.1.4",
"bun": "^1.1.10",
"dlv": "^1.1.3",
"jest-diff": "^29.7.0",
"postcss": "^8.4.17",
Expand Down
15 changes: 12 additions & 3 deletions plugin/src/extractor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ type ExtractorOptions = {

let defaultPatterns

/** @internal */
export const DEFAULT_PREFIX = '', DEFAULT_SEPARATOR = ':', // these aren't available in `tailwindcss/defaultConfig`
PASSED_PREFIX = Symbol(), PASSED_SEPARATOR = Symbol(), IS_FLUID_EXTRACT = Symbol()

// This is the default extractor from 'tailwindcss-priv/src/lib/defaultExtractor'
// with two extra chars to support the ~ prefix
function extract(content: string): ReturnType<ExtractorFn>
Expand All @@ -28,7 +32,7 @@ function extract(contentOrOptions: string | ExtractorOptions): ReturnType<Extrac

const patterns = Array.from(buildRegExps(contentOrOptions))

return (content: string) => {
return Object.assign((content: string) => {
let results: string[] = []

for (const pattern of patterns) {
Expand All @@ -38,12 +42,17 @@ function extract(contentOrOptions: string | ExtractorOptions): ReturnType<Extrac
}

return results
}
}, {
[IS_FLUID_EXTRACT]: true,
[PASSED_PREFIX]: contentOrOptions.prefix ?? DEFAULT_PREFIX,
[PASSED_SEPARATOR]: contentOrOptions.separator ?? DEFAULT_SEPARATOR
})
}
extract[IS_FLUID_EXTRACT] = true

export default extract

function* buildRegExps({ separator = ':', prefix: _prefix = '' }: ExtractorOptions = {}) {
function* buildRegExps({ separator = DEFAULT_SEPARATOR, prefix: _prefix = DEFAULT_PREFIX }: ExtractorOptions = {}) {
const prefix = _prefix !== ''
? regex.optional(regex.pattern([/-?/, regex.escape(_prefix)]))
: ''
Expand Down
69 changes: 44 additions & 25 deletions plugin/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import plugin from 'tailwindcss/plugin'
import { corePlugins } from 'tailwindcss-priv/src/corePlugins'
import type {
CSSRuleObject,
ExtractorFn,
KeyValuePair,
PluginAPI,
ResolvableTo,
Expand All @@ -20,9 +21,16 @@ import { Length, type RawValue } from './util/css'
import * as expr from './util/expr'
import { addVariant, addVariantWithModifier, matchVariant } from './util/tailwind'
import { tuple } from './util/set'
import { FluidError } from './util/errors'
import { FluidError, error } from './util/errors'
import type { Container } from 'postcss'
import type { Config } from 'tailwindcss'
import {
IS_FLUID_EXTRACT,
DEFAULT_PREFIX,
DEFAULT_SEPARATOR,
PASSED_PREFIX,
PASSED_SEPARATOR
} from './extractor'

export type FluidThemeConfig = ResolvableTo<ResolvedFluidThemeConfig>

Expand Down Expand Up @@ -89,8 +97,8 @@ function getFluidAPI(
}
)

// Add negative version if supported
if (!options?.supportsNegativeValues) return

orig(
{
[`~-${context.prefix}${util}`](start, { modifier: end }) {
Expand Down Expand Up @@ -132,14 +140,29 @@ function getFluidAPI(
}
}

let inFluidPlugin = false
const fluid = plugin.withOptions((options: PluginOptions = {}) => (api: PluginAPI) => {
if (inFluidPlugin) return // prevent recursion when adding fluid versions of config.plugins
inFluidPlugin = true

const IS_FLUID_PLUGIN = Symbol()
const fluidPlugin = (options: PluginOptions = {}, api: PluginAPI) => {
const { config, theme, corePlugins: corePluginEnabled, matchUtilities } = api
const context = getContext(config, theme, options)
const { screens, containers, prefix } = context
const { screens, containers, prefix, separator } = context

// Make sure they remembered to pass in extractor correctly
const extractor = config('content.extract.DEFAULT') as ExtractorFn
if (!extractor || !(IS_FLUID_EXTRACT in extractor)) {
error('extractor-missing')
}
if (
prefix !== DEFAULT_PREFIX &&
(!(PASSED_PREFIX in extractor) || extractor[PASSED_PREFIX] !== prefix)
) {
error('extractor-option-mismatch', 'prefix', prefix)
}
if (
separator !== DEFAULT_SEPARATOR &&
(!(PASSED_SEPARATOR in extractor) || extractor[PASSED_SEPARATOR] !== separator)
) {
error('extractor-option-mismatch', 'separator', separator)
}

// Add new fluid text utility to handle potentially complex theme values
// ---
Expand Down Expand Up @@ -260,24 +283,18 @@ const fluid = plugin.withOptions((options: PluginOptions = {}) => (api: PluginAP
const plugins = config('plugins') as Config['plugins']
plugins?.forEach((plug, i) => {
if (!plug) return

if (typeof plug === 'function') {
if ('__isOptionsFunction' in plug && plug.__isOptionsFunction) {
plug(undefined).handler(fluidPluginAPI)
} else {
plug(fluidPluginAPI)
}
} else {
plug.handler(fluidPluginAPI)
}
const handler =
typeof plug === 'function'
? '__isOptionsFunction' in plug && plug.__isOptionsFunction
? plug(undefined).handler
: plug
: plug.handler
if (!(IS_FLUID_PLUGIN in handler)) handler(fluidPluginAPI)
})

// Screen variants
// ---

// We need this for error output, so retrieve and cache
const separator = config('separator') ?? ':'

// Handle the rewrites and potential errors:
const rewrite = (
container: Container,
Expand Down Expand Up @@ -375,10 +392,12 @@ const fluid = plugin.withOptions((options: PluginOptions = {}) => (api: PluginAP
}
}
)

inFluidPlugin = false
})

}
// Make sure it's named fluid, b/c it shows up in IntelliSense:
const fluid = plugin.withOptions((options: PluginOptions = {}) =>
// Organized this way to attach the symbol correctly, and also assuage Prettier:
Object.assign((api: PluginAPI) => fluidPlugin(options, api), { [IS_FLUID_PLUGIN]: true })
)
export default fluid

/**
Expand Down
10 changes: 9 additions & 1 deletion plugin/src/util/context.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import type { KeyValuePair, PluginAPI, PrefixConfig, ThemeConfig } from 'tailwindcss/types/config'
import type {
KeyValuePair,
PluginAPI,
PrefixConfig,
SeparatorConfig,
ThemeConfig
} from 'tailwindcss/types/config'
import mapObject, { mapObjectSkip } from 'map-obj'
import { error } from './errors'
import { Length } from './css'
Expand All @@ -20,6 +26,7 @@ export default function getContext(
{ checkSC144 = true }: PluginOptions = {}
) {
const prefix: PrefixConfig = config('prefix')
const separator: SeparatorConfig = config('separator')
const themeConfig: ResolvedFluidThemeConfig = theme('fluid') ?? {}

// Filter breakpoints by simple valid lengths
Expand Down Expand Up @@ -95,6 +102,7 @@ export default function getContext(
},
theme,
prefix,
separator,
checkSC144
}
}
Expand Down
5 changes: 4 additions & 1 deletion plugin/src/util/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ export const codes = {
'no-change-bp': (val: Length) => `Start and end breakpoints are both ${val.cssText}`,
'bp-not-found': (key: string, name: string) => `Could not find \`theme.${key}.${name}\``,
'no-utility': () => 'Fluid variants can only be used with fluid utilities',
'fails-sc-144': (failingBp: Length) => `Fails WCAG SC 1.4.4 at i.e. ${failingBp.cssText}`
'fails-sc-144': (failingBp: Length) => `Fails WCAG SC 1.4.4 at i.e. ${failingBp.cssText}`,
'extractor-missing': () => `fluid-tailwind: Fluid extractor not found in your Tailwind config`,
'extractor-option-mismatch': (opt: string, val: string) =>
`fluid-tailwind: You must pass in your \`config.${opt}\` to the fluid extractor, i.e. \`extract({ ${opt}: \'${val}\' })\``
} satisfies Record<string, (...args: any[]) => string>

export class FluidError extends Error {
Expand Down
66 changes: 66 additions & 0 deletions plugin/tests/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,72 @@ it(`respects defaultScreens config`, async () => {
`)
})

it(`errors if extractor wasn't used`, () => {
return expect(
run({
content: {
files: [{ raw: html`` }]
}
})
).rejects.toThrow('fluid-tailwind: Fluid extractor not found in your Tailwind config')
})

it(`errors if custom prefix wasn't passed into extractor`, () => {
return expect(
run({
prefix: 'tw-',
content: {
files: [{ raw: html`` }],
extract
}
})
).rejects.toThrow(
`fluid-tailwind: You must pass in your \`config.prefix\` to the fluid extractor, i.e. \`extract({ prefix: 'tw-' })\``
)
})

it(`errors if different prefix was passed into extractor`, () => {
return expect(
run({
prefix: 'tw-',
content: {
files: [{ raw: html`` }],
extract: extract({ prefix: 'tw' })
}
})
).rejects.toThrow(
`fluid-tailwind: You must pass in your \`config.prefix\` to the fluid extractor, i.e. \`extract({ prefix: 'tw-' })\``
)
})

it(`errors if custom separator wasn't passed into extractor`, () => {
return expect(
run({
separator: '_',
content: {
files: [{ raw: html`` }],
extract
}
})
).rejects.toThrow(
`fluid-tailwind: You must pass in your \`config.separator\` to the fluid extractor, i.e. \`extract({ separator: '_' })\``
)
})

it(`errors if different separator was passed into extractor`, () => {
return expect(
run({
separator: '_',
content: {
files: [{ raw: html`` }],
extract: extract({ separator: '|' })
}
})
).rejects.toThrow(
`fluid-tailwind: You must pass in your \`config.separator\` to the fluid extractor, i.e. \`extract({ separator: '_' })\``
)
})

it(`supports custom separator and prefix`, async () => {
const result = await run({
content: {
Expand Down
2 changes: 0 additions & 2 deletions plugin/tests/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ export async function run(config: Config, input = `@tailwind utilities;@tailwind
files: config.content,
extract
}
} else {
config.content.extract ??= extract
}

config.corePlugins ??= {}
Expand Down
1 change: 1 addition & 0 deletions plugin/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"declaration": true,
"module": "ES2022",
"moduleResolution": "Bundler",
"stripInternal": true,
"outDir": "dist",
"lib": ["es2022"]
},
Expand Down
Loading

0 comments on commit fde4a15

Please sign in to comment.