Skip to content

Commit 4eae464

Browse files
authored
✨ Support configuration file (#28)
Adds support for specifying options via a configuration file instead of command line arguments. Uses `lilconfig` and `js-yaml` under the hood. | | Before | After | Diff | | :------: | -----: | :-----: | :--: | | packed | 4.4 kB | 5.5 kB | +25% | | unpacked | 9.5 kB | 12.6 kB | +33% | The increase in download size brings the package back to pre-bundling size. Note: This includes increased README documentation size. dist/main.js: 3.2kb Improves pipeline tests with bash unofficial strict mode, which fixes potential for false positives. Removes `-latest` from OS variable in test matrix. +semver:minor
1 parent c0ae91c commit 4eae464

13 files changed

+247
-37
lines changed

.github/workflows/pipeline.yaml

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -45,20 +45,19 @@ jobs:
4545

4646
Test:
4747
name: Test (${{ matrix.node }} | ${{ matrix.platform.os }})
48-
needs: Build # Verify ci-build first
4948
defaults:
5049
run:
5150
shell: bash
52-
runs-on: ${{ matrix.platform.os }}
51+
runs-on: ${{ matrix.platform.os }}-latest
5352
strategy:
5453
matrix:
5554
node:
5655
- 20.x
5756
- 22.x
5857
platform:
59-
- os: ubuntu-latest
60-
- os: macos-latest
61-
- os: windows-latest
58+
- os: ubuntu
59+
- os: macos
60+
- os: windows
6261
fail-fast: false
6362

6463
steps:
@@ -77,29 +76,52 @@ jobs:
7776
- name: Build
7877
run: npm run build
7978

80-
- name: "Test 1: Default case"
79+
- name: "Test 1: default case"
8180
run: |
82-
scripts/test.sh foo
81+
scripts/test.sh foo '' '*.css'
8382
8483
- name: "Test 2: localsConvention, first position"
8584
if: success() || failure()
8685
run: |
87-
scripts/test.sh casing/casing "--localsConvention camelCaseOnly" casing/camelCaseOnly
86+
scripts/test.sh casing/casing '' '--localsConvention camelCaseOnly *.css' casing/camelCaseOnly
8887
8988
- name: "Test 3: localsConvention, second position"
9089
if: success() || failure()
9190
run: |
92-
scripts/test.sh casing/casing "" casing/camelCaseOnly "--localsConvention camelCaseOnly"
91+
scripts/test.sh casing/casing '' '*.css --localsConvention camelCaseOnly' casing/camelCaseOnly
9392
9493
- name: "Test 4: relative outdir"
9594
if: success() || failure()
9695
run: |
97-
scripts/test.sh foo "--outdir generated" "" "" generated/
96+
scripts/test.sh foo '' '--outdir generated *.css' '' generated/
9897
9998
- name: "Test 5: absolute outdir"
10099
if: success() || failure()
101100
run: |
102-
scripts/test.sh foo "-o $GITHUB_WORKSPACE/generated" "" "" "$GITHUB_WORKSPACE"/generated/
101+
scripts/test.sh foo "" "-o $GITHUB_WORKSPACE/generated *.css" "" "$GITHUB_WORKSPACE"/generated/
102+
# Note: This test uses double quotes, which expands differently.
103+
104+
- name: "Test 6: json file config"
105+
if: success() || failure()
106+
run: |
107+
scripts/test.sh foo csstypedrc.json
108+
109+
- name: "Test 7: yaml file config"
110+
if: success() || failure()
111+
run: |
112+
scripts/test.sh foo csstypedrc.yaml '' '' generated/
113+
114+
- name: "Test 8: custom config path"
115+
if: success() || failure()
116+
run: |
117+
scripts/test.sh foo css-typed-rc.yaml '-c .config/css-typed-rc.yaml'
118+
119+
- name: "Test 9: mjs file config"
120+
if: matrix.platform.os != 'windows' && (success() || failure())
121+
# Do not run on Windows due to Windows-only ESM import bug.
122+
# This _could_ be an issue with css-typed, but could be a test/deps issue.
123+
run: |
124+
scripts/test.sh foo custom-config-path.config.mjs '-c .config/custom-config-path.config.mjs'
103125
104126
Publish:
105127
if: ${{ github.ref == 'refs/heads/main' }}

README.md

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ TypeScript declaration generator for CSS files.
1212
<summary><strong>Table of Contents</strong></summary>
1313

1414
- [Usage](#usage)
15+
- [Options](#options)
1516
- [Recipes](#recipes)
1617
- [Motivation](#motivation)
1718
- [Contributing](#contributing)
@@ -69,9 +70,35 @@ echo '*.d.css.ts' >> .gitignore
6970
The following table lists the options `css-typed` supports.
7071
Also run `css-typed -h` on the command line.
7172

72-
| CLI option | Default | Description |
73-
| :------------------: | :----------: | :----------------------------- |
74-
| `--localsConvention` | `dashesOnly` | Style of exported class names. |
73+
| CLI option | Default | Description |
74+
| :------------------: | :----------: | :------------------------------------- |
75+
| `-c` or `--config` | Heuristics | Custom path to the configuration file. |
76+
| `--localsConvention` | `dashesOnly` | Style of exported class names. |
77+
78+
### config
79+
80+
`css-typed` supports loading options from a configuration file instead of using command line arguments.
81+
To load from a custom path, use the `-c` or `--config` option.
82+
By default, `css-typed` looks in the following locations.
83+
Extensionless "rc" files can have JSON or YAML format.
84+
85+
- Package file: `css-typed` property in `package.json` or `package.yaml`
86+
- Root rc files: `.csstypedrc` with no extension or one of `json`, `yaml`, `yml`, `js`, `cjs`, or `mjs`
87+
- Config folder rc files: `.config/csstypedrc` with no extension or one of `json`, `yaml`, `yml`, `js`, `cjs`, or `mjs`
88+
- Root config files: `css-typed.config` with an extension of `js`, `cjs`, or `mjs`
89+
90+
<details>
91+
<summary>Look under the hood</summary>
92+
93+
Under the hood, `css-typed` uses [lilconfig] to load configuration files.
94+
It supports YAML files via [js-yaml].
95+
96+
See [src/config.ts](src/config.ts) for the implementation.
97+
98+
</details>
99+
100+
[lilconfig]: https://www.npmjs.com/package/lilconfig
101+
[js-yaml]: https://www.npmjs.com/package/js-yaml
75102

76103
### localsConvention
77104

@@ -89,11 +116,12 @@ The default matches CSS naming practices (`kebab-case`).
89116

90117
> **IMPORTANT**
91118
>
92-
> Note that the non-`*Only` values MAY have TypeScript bugs.
119+
> Note that `camelCase` and `dashes` MAY have TypeScript bugs.
93120
> TypeScript 5.6 may help with the named exports for these.
94121
>
95122
> If you encounter a bug, please file an issue.
96-
> In the mean-time, consider using `camelCaseOnly` instead (or `dashesOnly` which is the default).
123+
> In the mean-time, consider using `camelCaseOnly` instead.
124+
> (Or `dashesOnly` which is the default.)
97125
98126
## Recipes
99127

@@ -145,6 +173,7 @@ declare module "*.module.css" {
145173
Both depend on [css-modules-loader-core], which appears [abandoned][174].
146174

147175
Therefore, I wrote my own (very basic) implementation.
176+
See [§Implementation details](#implementation-details) for more information.
148177

149178
[typescript-plugin-css-modules]: https://www.npmjs.com/package/typescript-plugin-css-modules
150179
[typed-css-modules]: https://www.npmjs.com/package/typed-css-modules
@@ -161,7 +190,17 @@ See [CONTRIBUTING.md](./CONTRIBUTING.md).
161190
This (very basic) implementation uses [glob] for file matching and [css-tree] for CSS parsing.
162191
It extracts CSS classes (`ClassSelector` in CSS Tree’s AST) and exports them as `string` constants (named exports).
163192

193+
The CSS-file class name is modified for JS export according to the [localsConvention](#localsconvention) option.
194+
The implementation matches PostCSS.
195+
164196
I chose CSS Tree after a brief search because it had a nice API, good documentation, and supported CSS nesting (a requirement for my original use case).
165197

198+
`css-typed` uses [Commander.js][commander] for command line parsing and [lilconfig] for configuration file loading.
199+
200+
The “brand” image/logo combines the public CSS 3 and TypeScript logos with a basic plus icon in between.
201+
See [css-typed.svg](images/css-typed.svg).
202+
166203
[glob]: https://www.npmjs.com/package/glob
167204
[css-tree]: https://www.npmjs.com/package/css-tree
205+
[commander]: https://www.npmjs.com/package/commander
206+
[lilconfig]: https://www.npmjs.com/package/lilconfig

fixtures/config/css-typed-rc.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pattern: "*.css"

fixtures/config/csstypedrc.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{ "pattern": "*.css" }

fixtures/config/csstypedrc.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
pattern: "*.css"
2+
outdir: generated
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export default { pattern: `*.css` };

package-lock.json

Lines changed: 27 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,14 @@
5050
"commander": "^12.1.0",
5151
"css-tree": "^2.3.1",
5252
"glob": "^11.0.0",
53+
"js-yaml": "^4.1.0",
54+
"lilconfig": "^3.1.2",
5355
"lodash.camelcase": "^4.3.0"
5456
},
5557
"devDependencies": {
5658
"@connorjs/tsconfig": "~0.3.0",
5759
"@types/css-tree": "^2.3.8",
60+
"@types/js-yaml": "^4.0.9",
5861
"@types/lodash.camelcase": "^4.3.9",
5962
"@types/node": "^20.14.14",
6063
"esbuild": "~0.23.0",
@@ -64,6 +67,7 @@
6467
"lint-staged": "^15.2.8",
6568
"npm-run-all": "^4.1.5",
6669
"prettier": "^3.3.3",
70+
"type-fest": "^4.23.0",
6771
"typescript": "^5.5.4",
6872
"vitest": "^2.0.5"
6973
}

scripts/build.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,4 @@ const result = await build({
2424
});
2525

2626
const analysis = await analyzeMetafile(result.metafile);
27-
console.log(analysis);
27+
console.info(analysis);

scripts/test.sh

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,40 @@
11
#!/usr/bin/env bash
2+
set -eo pipefail # Removed `-u` which failed on macos for `options`
3+
IFS=$' ' # We want space splitting for this script
24

35
# $1 is the input name, relative to `fixtures`. Required.
46
input=$1
57

6-
# $2 is the standard (before) options. Defaults to "".
7-
IFS=" " read -r -a beforeOpts <<< "${2:-}"
8+
# $2 is the config file name, relative to `fixtures/config`. Defaults to $1.yaml.
9+
config=${2:-$1.yaml}
810

9-
# $3 is the output name, relative to `fixtures`. Defaults to $1.
10-
output=${3:-$1}
11+
# $3 is the options. Defaults to "".
12+
read -r -a options <<< "${3:-}"
1113

12-
# $4 is the after options. Use an array. Defaults to "".
13-
IFS=" " read -r -a afterOpts <<< "${4:-}"
14+
# $4 is the output name, relative to `fixtures`. Defaults to $1.
15+
output=${4:-$1}
1416

1517
# $5 is the path prefix for output. Defaults to "".
1618
prefix=${5:-}
1719

1820
# Run from $RUNNER_TEMP for auto-cleanup.
1921
cp fixtures/${input}.css $RUNNER_TEMP/test.css
2022
cp fixtures/${output}.d.css.ts $RUNNER_TEMP/expected.d.css.ts
23+
24+
rm -rf "${RUNNER_TEMP:?}/.config"
25+
if [ -f fixtures/config/${config} ]; then
26+
mkdir -p $RUNNER_TEMP/.config
27+
cp fixtures/config/${config} $RUNNER_TEMP/.config/${config}
28+
fi
29+
2130
pushd $RUNNER_TEMP > /dev/null || exit
2231

2332
# `./dist/main.js` is executing local `css-typed` as if installed (same as `bin`).
2433
# But it is `$GITHUB_WORKSPACE/dist/main.js` b/c we `cd $RUNNER_TEMP`.
25-
echo "css-typed ${beforeOpts[*]} \"*.css\" ${afterOpts[*]}"
34+
echo "css-typed " "${options[@]}"
2635
# shellcheck disable=SC2068
27-
$GITHUB_WORKSPACE/dist/main.js ${beforeOpts[@]} "*.css" ${afterOpts[@]}
36+
$GITHUB_WORKSPACE/dist/main.js ${options[@]}
2837

2938
# Use `diff` to compare the files.
3039
# Use `-I '//.*'` to ignore the first line (comment) which has generated path and timestamp.
31-
diff --color=auto --strip-trailing-cr -uI "//.*" expected.d.css.ts ${prefix}test.d.css.ts
40+
diff --color=always --strip-trailing-cr -uI "//.*" expected.d.css.ts ${prefix}test.d.css.ts

0 commit comments

Comments
 (0)