All files / css / bundle.ts

100.00% Branches 8/8
100.00% Lines 37/37
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
x1
x3
x3
 
x3
x3
x3
x3
 
x1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
x3
x3
x13
x13
x13
x39
x13
x13
x13
x13
x13
x13
x13
x13
x13
x13
x13
x13
x14
x14
x13
x42
x14
x13
x14
x14
x14
x22
x13
 
 
 
 































































// Imports
import { minify as csso } from "csso"
import stylelint from "stylelint"
import type { ConfigRuleSettings } from "stylelint"
import plugin from "stylelint-order"
import recommended from "stylelint-config-recommended"
import ordering from "stylelint-config-idiomatic-order"
import { SEPARATOR } from "@std/path/constants"

/**
 * Bundle CSS.
 *
 * Minification is supported through {@link https://github.com/css/csso | CSSO}.
 *
 * Linting and formatting is supported through {@link https://github.com/stylelint/stylelint | Stylelint}.
 *
 * A banner option can be provided to prepend a comment to the output, which can be useful for licensing information.
 *
 * @example
 * ```ts
 * // From file
 * import { bundle } from "./bundle.ts"
 * console.log(await bundle(new URL("testing/test_bundle.css", import.meta.url)))
 * ```
 *
 * @example
 * ```ts
 * // From string
 * import { bundle } from "./bundle.ts"
 * console.log(await bundle(`body { color: salmon; }`))
 * ```
 *
 * @module
 */
export async function bundle(input: URL | string, { minify = false, banner = "", rules = {} as Rules } = {}): Promise<string> {
  const code = input instanceof URL ? await fetch(input).then((response) => response.text()) : input
  const { results: [{ warnings }], ...result } = await stylelint.lint({
    config: {
      plugins: [plugin],
      rules: {
        ...recommended.rules,
        ...ordering.rules,
        ...rules,
      },
      fix: true,
    },
    code,
    cacheLocation: `/dev/null/${SEPARATOR}`,
  })
  result.code ??= ""
  if (warnings.length) {
    throw new TypeError(`Failed to bundle css:\n${warnings.map(({ severity, rule, text }) => `[${severity.toUpperCase()}] ${rule}: ${text}`).join("\n")}`)
  }
  if (minify) {
    result.code = csso(result.code!, { comments: false }).css
  }
  if (banner) {
    banner = `/**\n${banner.split("\n").map((line) => ` * ${line}`).join("\n")}\n */`
    result.code = `${banner}\n${result.code}`
  }
  return result.code!
}

/** Rules */
// deno-lint-ignore ban-types no-explicit-any
type Rules = ConfigRuleSettings<any, Object>