Skip to content

Commit 1875279

Browse files
authored
✨ Output directory option (#24)
Adds output directory option (-o or --outdir) as alternative to generating the declaration files alongside the source file. Adds outdir unit tests and pipeline tests. Refactors pipeline tests to improve failure diagnosis. +semver:minor
1 parent 24713e7 commit 1875279

File tree

4 files changed

+103
-26
lines changed

4 files changed

+103
-26
lines changed

.github/workflows/pipeline.yaml

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -77,21 +77,58 @@ jobs:
7777
- name: Build
7878
run: npm run build
7979

80-
- name: Run css-typed (the test)
81-
# `node dist/main.js` is executing local `css-typed` as if installed (same as `bin`)
82-
# Use `-I '//.*'` to ignore the first line (comment) which has generated path and timestamp
83-
# Test `--localsConvention` in both positions
80+
# Run tests
81+
#
82+
# - Run from $RUNNER_TEMP for auto-cleanup.
83+
# - `./dist/main.js` is executing local `css-typed` as if installed (same as `bin`).
84+
# But it is `$GITHUB_WORKSPACE/dist/main.js` b/c we `cd $RUNNER_TEMP`.
85+
# - Use `diff` to compare the files.
86+
# Use `-I '//.*'` to ignore the first line (comment) which has generated path and timestamp.
87+
88+
- name: "Test 1: Default case"
8489
run: |
85-
cp src/fixtures/casing/casing.css "$RUNNER_TEMP/casing.css"
90+
cp src/fixtures/casing/casing.css $RUNNER_TEMP/casing.css
91+
cp src/fixtures/casing/dashesOnly.d.css.ts $RUNNER_TEMP/expected.d.css.ts
8692
87-
node dist/main.js "$RUNNER_TEMP/*.css"
88-
diff --strip-trailing-cr -uI '//.*' src/fixtures/casing/dashesOnly.d.css.ts "$RUNNER_TEMP/casing.d.css.ts"
93+
cd $RUNNER_TEMP
94+
$GITHUB_WORKSPACE/dist/main.js '*.css'
95+
diff --strip-trailing-cr -uI '//.*' expected.d.css.ts casing.d.css.ts
8996
90-
node dist/main.js "$RUNNER_TEMP/*.css" --localsConvention camelCaseOnly
91-
diff --strip-trailing-cr -uI '//.*' src/fixtures/casing/camelCaseOnly.d.css.ts "$RUNNER_TEMP/casing.d.css.ts"
97+
- name: "Test 2: localsConvention, second position"
98+
run: |
99+
cp src/fixtures/casing/casing.css $RUNNER_TEMP/casing.css
100+
cp src/fixtures/casing/camelCaseOnly.d.css.ts $RUNNER_TEMP/expected.d.css.ts
101+
102+
cd $RUNNER_TEMP
103+
$GITHUB_WORKSPACE/dist/main.js '*.css' --localsConvention camelCaseOnly
104+
diff --strip-trailing-cr -uI '//.*' expected.d.css.ts casing.d.css.ts
105+
106+
- name: "Test 3: localsConvention, first position"
107+
run: |
108+
cp src/fixtures/casing/casing.css $RUNNER_TEMP/casing.css
109+
cp src/fixtures/casing/camelCaseOnly.d.css.ts $RUNNER_TEMP/expected.d.css.ts
110+
111+
cd $RUNNER_TEMP
112+
$GITHUB_WORKSPACE/dist/main.js --localsConvention camelCaseOnly '*.css'
113+
diff --strip-trailing-cr -uI '//.*' expected.d.css.ts casing.d.css.ts
114+
115+
- name: "Test 4: relative outdir"
116+
run: |
117+
cp src/fixtures/casing/casing.css $RUNNER_TEMP/casing.css
118+
cp src/fixtures/casing/dashesOnly.d.css.ts $RUNNER_TEMP/expected.d.css.ts
119+
120+
cd $RUNNER_TEMP
121+
$GITHUB_WORKSPACE/dist/main.js '*.css' --outdir generated
122+
diff --strip-trailing-cr -uI '//.*' expected.d.css.ts generated/casing.d.css.ts
123+
124+
- name: "Test 5: absolute outdir"
125+
run: |
126+
cp src/fixtures/casing/casing.css $RUNNER_TEMP/casing.css
127+
cp src/fixtures/casing/dashesOnly.d.css.ts $RUNNER_TEMP/expected.d.css.ts
92128
93-
node dist/main.js --localsConvention camelCaseOnly "$RUNNER_TEMP/*.css"
94-
diff --strip-trailing-cr -uI '//.*' src/fixtures/casing/camelCaseOnly.d.css.ts "$RUNNER_TEMP/casing.d.css.ts"
129+
cd $RUNNER_TEMP
130+
$GITHUB_WORKSPACE/dist/main.js '*.css' -o $GITHUB_WORKSPACE/generated
131+
diff --strip-trailing-cr -uI '//.*' expected.d.css.ts $GITHUB_WORKSPACE/generated/casing.d.css.ts
95132
96133
Publish:
97134
if: ${{ github.ref == 'refs/heads/main' }}

src/logic.test.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { readFileSync } from "node:fs";
22
import path from "node:path";
3+
import * as process from "node:process";
34

45
import { describe, expect, it } from "vitest";
56

@@ -37,13 +38,21 @@ describe(`css-typed`, () => {
3738
});
3839
});
3940

41+
const cwd = process.cwd();
42+
const cwdParent = path.resolve(cwd, `..`);
4043
describe(`dtsPath`, () => {
4144
it.each([
42-
[`foo.css`, `foo.d.css.ts`],
43-
[`foo.module.css`, `foo.module.d.css.ts`],
44-
])(`%s should create file %s`, (input, expected) => {
45-
expect(dtsPath(input)).toStrictEqual(expected);
46-
});
45+
[`src/foo.css`, `${cwd}/src/foo.d.css.ts`, undefined],
46+
[`src/foo.module.css`, `${cwd}/src/foo.module.d.css.ts`, ``],
47+
[`src/foo.css`, `${cwd}/generated/src/foo.d.css.ts`, `generated`],
48+
[`src/foo.css`, `/absolute/src/foo.d.css.ts`, `/absolute`],
49+
[`src/foo.css`, `${cwdParent}/relative/src/foo.d.css.ts`, `../relative`],
50+
])(
51+
`[%s] should create file [%s] with outdir [%s]`,
52+
(input, expected, outdir) => {
53+
expect(path.join(...dtsPath(input, outdir))).toStrictEqual(expected);
54+
},
55+
);
4756
});
4857
});
4958

src/logic.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,19 @@ function dashesCamelCase(s: string) {
154154
);
155155
}
156156

157-
export function dtsPath(stylesheetPath: string) {
158-
const { dir, name, ext } = path.parse(stylesheetPath);
159-
return path.join(dir, `${name}.d${ext}.ts`);
157+
export function dtsPath(
158+
stylesheetPath: string,
159+
outdir: string | undefined,
160+
): [string, string] {
161+
const { dir: originalDirectory, name, ext } = path.parse(stylesheetPath);
162+
163+
let directory;
164+
if (outdir) {
165+
const relative = path.relative(process.cwd(), originalDirectory);
166+
directory = path.join(outdir, relative);
167+
} else {
168+
directory = originalDirectory;
169+
}
170+
171+
return [path.resolve(directory), `${name}.d${ext}.ts`];
160172
}

src/main.ts

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#!/usr/bin/env node
22

3-
import { writeFile } from "node:fs/promises";
3+
import { existsSync } from "node:fs";
4+
import { mkdir, writeFile } from "node:fs/promises";
5+
import path from "node:path";
46

57
import { Command, Option } from "@commander-js/extra-typings";
68
import { glob } from "glob";
@@ -24,14 +26,18 @@ await new Command()
2426
.choices(localsConventionChoices)
2527
.default(`dashesOnly` as const),
2628
)
29+
.option(
30+
`-o, --outdir <outDirectory>`,
31+
`Root directory for generated CSS declaration files.`,
32+
)
2733
.action(async function (pattern, options) {
2834
const files = await glob(pattern);
2935

3036
const time = new Date().toISOString();
3137
await Promise.all(
3238
files.map((file) =>
3339
generateDeclaration(file, time, options).then((ts) =>
34-
writeDeclarationFile(file, ts),
40+
writeDeclarationFile(file, options.outdir, ts),
3541
),
3642
),
3743
);
@@ -41,12 +47,25 @@ await new Command()
4147
/**
4248
* Writes the TypeScript declaration content to file. Handles the output path.
4349
*
44-
* @param path - Path to the original stylesheet file. NOT the path to write.
50+
* @param file - Path to the original stylesheet file. NOT the path to write.
51+
* @param outdir - Output directory to which to write.
4552
* @param ts - The TypeScript declaration content to write.
4653
* @returns Empty promise indicating when writing has completed.
4754
*/
48-
async function writeDeclarationFile(path: string, ts: string | undefined) {
49-
if (!ts) return undefined;
50-
await writeFile(dtsPath(path), ts, `utf8`);
51-
return undefined;
55+
async function writeDeclarationFile(
56+
file: string,
57+
outdir: string | undefined,
58+
ts: string | undefined,
59+
) {
60+
if (!ts) {
61+
return undefined;
62+
}
63+
64+
const [directoryToWrite, fileToWrite] = dtsPath(file, outdir);
65+
if (!existsSync(directoryToWrite)) {
66+
await mkdir(directoryToWrite, { recursive: true });
67+
}
68+
69+
const pathToWrite = path.join(directoryToWrite, fileToWrite);
70+
await writeFile(pathToWrite, ts, { encoding: `utf8` });
5271
}

0 commit comments

Comments
 (0)