@herb-tools/tailwind-class-sorter 0.8.9 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "type": "module",
3
3
  "name": "@herb-tools/tailwind-class-sorter",
4
4
  "description": "Standalone Tailwind CSS class sorter with Prettier plugin compatibility, extracted from tailwindlabs/prettier-plugin-tailwindcss",
5
- "version": "0.8.9",
5
+ "version": "0.9.0",
6
6
  "license": "MIT",
7
7
  "main": "./dist/tailwind-class-sorter.cjs",
8
8
  "module": "./dist/tailwind-class-sorter.esm.js",
@@ -36,20 +36,20 @@
36
36
  "pretest": "yarn setup-fixtures",
37
37
  "test": "vitest run",
38
38
  "test:watch": "vitest --watch",
39
- "prepublishOnly": "yarn clean && yarn build"
39
+ "prepublishOnly": "yarn clean && yarn build && yarn test"
40
40
  },
41
41
  "dependencies": {
42
42
  "clear-module": "^4.1.2",
43
43
  "escalade": "^3.2.0",
44
44
  "jiti": "^2.5.1",
45
- "postcss": "^8.5.6",
45
+ "postcss": "^8.5.8",
46
46
  "postcss-import": "^16.1.1"
47
47
  },
48
48
  "devDependencies": {
49
- "@types/node": "^25.0.10",
49
+ "@types/node": "^25.2.2",
50
50
  "dedent": "^1.7.0",
51
- "esbuild": "^0.27.1",
52
- "rimraf": "^6.1.2",
51
+ "esbuild": "^0.27.3",
52
+ "rimraf": "^6.1.3",
53
53
  "tailwindcss": "^3.4.17",
54
54
  "tsup": "^8.5.1",
55
55
  "vitest": "^4.0.0"
package/src/config.ts CHANGED
@@ -13,23 +13,23 @@ import { expiringMap } from './expiring-map.js'
13
13
  import { resolveCssFrom, resolveJsFrom } from './resolve'
14
14
  import type { ContextContainer, SortTailwindClassesOptions } from './types'
15
15
 
16
- let sourceToPathMap = new Map<string, string | null>()
17
- let sourceToEntryMap = new Map<string, string | null>()
18
- let pathToContextMap = expiringMap<string | null, ContextContainer>(10_000)
16
+ const sourceToPathMap = new Map<string, string | null>()
17
+ const sourceToEntryMap = new Map<string, string | null>()
18
+ const pathToContextMap = expiringMap<string | null, ContextContainer>(10_000)
19
19
 
20
20
  export async function getTailwindConfig(
21
21
  options: SortTailwindClassesOptions = {},
22
22
  ): Promise<ContextContainer> {
23
- let pkgName = 'tailwindcss'
23
+ const pkgName = 'tailwindcss'
24
24
 
25
- let key = [
25
+ const key = [
26
26
  options.baseDir ?? process.cwd(),
27
27
  options.tailwindStylesheet ?? '',
28
28
  options.tailwindConfig ?? '',
29
29
  pkgName,
30
30
  ].join(':')
31
31
 
32
- let baseDir = getBaseDir(options)
32
+ const baseDir = getBaseDir(options)
33
33
 
34
34
  // Map the source file to it's associated Tailwind config file
35
35
  let configPath = sourceToPathMap.get(key)
@@ -45,14 +45,14 @@ export async function getTailwindConfig(
45
45
  }
46
46
 
47
47
  // Now see if we've loaded the Tailwind config file before (and it's still valid)
48
- let contextKey = `${pkgName}:${configPath}:${entryPoint}`
49
- let existing = pathToContextMap.get(contextKey)
48
+ const contextKey = `${pkgName}:${configPath}:${entryPoint}`
49
+ const existing = pathToContextMap.get(contextKey)
50
50
  if (existing) {
51
51
  return existing
52
52
  }
53
53
 
54
54
  // By this point we know we need to load the Tailwind config file
55
- let result = await loadTailwindConfig(
55
+ const result = await loadTailwindConfig(
56
56
  baseDir,
57
57
  pkgName,
58
58
  configPath,
@@ -85,7 +85,7 @@ async function loadTailwindConfig(
85
85
  let tailwindConfig: RequiredConfig = { content: [] }
86
86
 
87
87
  try {
88
- let pkgPath = resolveJsFrom(baseDir, pkgName)
88
+ const pkgPath = resolveJsFrom(baseDir, pkgName)
89
89
  let pkgJsonPath: string
90
90
 
91
91
  try {
@@ -115,14 +115,14 @@ async function loadTailwindConfig(
115
115
  }
116
116
  }
117
117
 
118
- let pkgDir = path.dirname(pkgJsonPath)
118
+ const pkgDir = path.dirname(pkgJsonPath)
119
119
 
120
120
  try {
121
- let v4 = await loadV4(baseDir, pkgDir, pkgName, entryPoint)
121
+ const v4 = await loadV4(baseDir, pkgDir, pkgName, entryPoint)
122
122
  if (v4) {
123
123
  return v4
124
124
  }
125
- } catch (err) {
125
+ } catch (_error) {
126
126
  // V4 loading failed, will try v3 below
127
127
  }
128
128
 
@@ -136,7 +136,7 @@ async function loadTailwindConfig(
136
136
 
137
137
  // Prior to `tailwindcss@3.3.0` this won't exist so we load it last
138
138
  loadConfig = require(path.join(pkgDir, 'loadConfig'))
139
- } catch (err: any) {
139
+ } catch (_error: any) {
140
140
  // Tailwind isn't installed or loading failed, will use defaults
141
141
  }
142
142
 
@@ -159,7 +159,7 @@ async function loadTailwindConfig(
159
159
 
160
160
  tailwindConfig.content = ['no-op']
161
161
 
162
- let context = createContext(resolveConfig(tailwindConfig))
162
+ const context = createContext(resolveConfig(tailwindConfig))
163
163
 
164
164
  return {
165
165
  context,
@@ -183,13 +183,13 @@ function createLoader<T>({
183
183
  filepath: string
184
184
  onError: (id: string, error: unknown, resourceType: string) => T
185
185
  }) {
186
- let cacheKey = `${+Date.now()}`
186
+ const cacheKey = `${+Date.now()}`
187
187
 
188
188
  async function loadFile(id: string, base: string, resourceType: string) {
189
189
  try {
190
- let resolved = resolveJsFrom(base, id)
190
+ const resolved = resolveJsFrom(base, id)
191
191
 
192
- let url = pathToFileURL(resolved)
192
+ const url = pathToFileURL(resolved)
193
193
  url.searchParams.append('t', cacheKey)
194
194
 
195
195
  return await jiti.import(url.href, { default: true })
@@ -199,7 +199,7 @@ function createLoader<T>({
199
199
  }
200
200
 
201
201
  if (legacy) {
202
- let baseDir = path.dirname(filepath)
202
+ const baseDir = path.dirname(filepath)
203
203
  return (id: string) => loadFile(id, baseDir, 'module')
204
204
  }
205
205
 
@@ -218,9 +218,9 @@ async function loadV4(
218
218
  entryPoint: string | null,
219
219
  ) {
220
220
  // Import Tailwind — if this is v4 it'll have APIs we can use directly
221
- let pkgPath = resolveJsFrom(baseDir, pkgName)
221
+ const pkgPath = resolveJsFrom(baseDir, pkgName)
222
222
 
223
- let tw = await import(pathToFileURL(pkgPath).toString())
223
+ const tw = await import(pathToFileURL(pkgPath).toString())
224
224
 
225
225
  // This is not Tailwind v4
226
226
  if (!tw.__unstable__loadDesignSystem) {
@@ -231,12 +231,12 @@ async function loadV4(
231
231
  entryPoint = entryPoint ?? `${pkgDir}/theme.css`
232
232
 
233
233
  // Create a Jiti instance that can be used to load plugins and config files
234
- let jiti = createJiti(import.meta.url, {
234
+ const jiti = createJiti(import.meta.url, {
235
235
  moduleCache: false,
236
236
  fsCache: false,
237
237
  })
238
238
 
239
- let importBasePath = path.dirname(entryPoint)
239
+ const importBasePath = path.dirname(entryPoint)
240
240
 
241
241
  // Resolve imports in the entrypoint to a flat CSS tree
242
242
  let css = await fs.readFile(entryPoint, 'utf-8')
@@ -256,14 +256,14 @@ async function loadV4(
256
256
  } catch {}
257
257
 
258
258
  if (!supportsImports) {
259
- let resolveImports = postcss([postcssImport()])
260
- let result = await resolveImports.process(css, { from: entryPoint })
259
+ const resolveImports = postcss([postcssImport()])
260
+ const result = await resolveImports.process(css, { from: entryPoint })
261
261
  css = result.css
262
262
  }
263
263
 
264
264
  // Load the design system and set up a compatible context object that is
265
265
  // usable by the rest of the plugin
266
- let design = await tw.__unstable__loadDesignSystem(css, {
266
+ const design = await tw.__unstable__loadDesignSystem(css, {
267
267
  base: importBasePath,
268
268
 
269
269
  // v4.0.0-alpha.25+
@@ -283,7 +283,7 @@ async function loadV4(
283
283
  }),
284
284
 
285
285
  loadStylesheet: async (id: string, base: string) => {
286
- let resolved = resolveCssFrom(base, id)
286
+ const resolved = resolveCssFrom(base, id)
287
287
 
288
288
  return {
289
289
  base: path.dirname(resolved),
@@ -4,11 +4,11 @@ interface ExpiringMap<K, V> {
4
4
  }
5
5
 
6
6
  export function expiringMap<K, V>(duration: number): ExpiringMap<K, V> {
7
- let map = new Map<K, { value: V; expiration: Date }>()
7
+ const map = new Map<K, { value: V; expiration: Date }>()
8
8
 
9
9
  return {
10
10
  get(key: K) {
11
- let result = map.get(key)
11
+ const result = map.get(key)
12
12
  if (!result) return undefined
13
13
  if (result.expiration <= new Date()) {
14
14
  map.delete(key)
@@ -19,7 +19,7 @@ export function expiringMap<K, V>(duration: number): ExpiringMap<K, V> {
19
19
  },
20
20
 
21
21
  set(key: K, value: V) {
22
- let expiration = new Date()
22
+ const expiration = new Date()
23
23
  expiration.setMilliseconds(expiration.getMilliseconds() + duration)
24
24
 
25
25
  map.set(key, {
package/src/resolve.ts CHANGED
@@ -53,10 +53,10 @@ export function maybeResolve(name: string) {
53
53
  }
54
54
 
55
55
  export async function loadIfExists<T>(name: string): Promise<T | null> {
56
- let modpath = maybeResolve(name)
56
+ const modpath = maybeResolve(name)
57
57
 
58
58
  if (modpath) {
59
- let mod = await import(name)
59
+ const mod = await import(name)
60
60
  return mod.default ?? mod
61
61
  }
62
62
 
package/src/sorting.ts CHANGED
@@ -8,7 +8,7 @@ function prefixCandidate(
8
8
  context: TailwindContext,
9
9
  selector: string,
10
10
  ): string {
11
- let prefix = context.tailwindConfig.prefix
11
+ const prefix = context.tailwindConfig.prefix
12
12
  return typeof prefix === 'function' ? prefix(selector) : prefix + selector
13
13
  }
14
14
 
@@ -25,14 +25,14 @@ function getClassOrderPolyfill(
25
25
  // that don't exist on their own. This will result in them "not existing" and
26
26
  // sorting could be weird since you still require them in order to make the
27
27
  // host utitlies work properly. (Thanks Biology)
28
- let parasiteUtilities = new Set([
28
+ const parasiteUtilities = new Set([
29
29
  prefixCandidate(env.context, 'group'),
30
30
  prefixCandidate(env.context, 'peer'),
31
31
  ])
32
32
 
33
- let classNamesWithOrder: [string, bigint | null][] = []
33
+ const classNamesWithOrder: [string, bigint | null][] = []
34
34
 
35
- for (let className of classes) {
35
+ for (const className of classes) {
36
36
  let order: bigint | null =
37
37
  env
38
38
  .generateRules(new Set([className]), env.context)
@@ -56,7 +56,7 @@ function reorderClasses(classList: string[], { env }: { env: SortEnv }) {
56
56
  return classList.map(name => [name, null] as [string, bigint | null])
57
57
  }
58
58
 
59
- let orderedClasses = env.context.getClassOrder
59
+ const orderedClasses = env.context.getClassOrder
60
60
  ? env.context.getClassOrder(classList)
61
61
  : getClassOrderPolyfill(classList, { env })
62
62
 
@@ -109,8 +109,9 @@ export function sortClasses(
109
109
  }
110
110
 
111
111
  let result = ''
112
- let parts = classStr.split(/([\t\r\f\n ]+)/)
113
- let classes = parts.filter((_, i) => i % 2 === 0)
112
+ const parts = classStr.split(/([\t\r\f\n ]+)/)
113
+ const classes = parts.filter((_, i) => i % 2 === 0)
114
+
114
115
  let whitespace = parts.filter((_, i) => i % 2 !== 0)
115
116
 
116
117
  if (classes[classes.length - 1] === '') {
@@ -131,7 +132,7 @@ export function sortClasses(
131
132
  suffix = `${whitespace.pop() ?? ''}${classes.pop() ?? ''}`
132
133
  }
133
134
 
134
- let { classList, removedIndices } = sortClassList(classes, {
135
+ const { classList, removedIndices } = sortClassList(classes, {
135
136
  env,
136
137
  removeDuplicates,
137
138
  })
@@ -173,10 +174,10 @@ export function sortClassList(
173
174
  removeDuplicates = false
174
175
  }
175
176
 
176
- let removedIndices = new Set<number>()
177
+ const removedIndices = new Set<number>()
177
178
 
178
179
  if (removeDuplicates) {
179
- let seenClasses = new Set<string>()
180
+ const seenClasses = new Set<string>()
180
181
 
181
182
  orderedClasses = orderedClasses.filter(([cls, order], index) => {
182
183
  if (seenClasses.has(cls)) {