diff --git a/.github/workflows/pipeline.yaml b/.github/workflows/pipeline.yaml index 62c49f2..f1c78d7 100644 --- a/.github/workflows/pipeline.yaml +++ b/.github/workflows/pipeline.yaml @@ -77,58 +77,29 @@ jobs: - name: Build run: npm run build - # Run tests - # - # - Run from $RUNNER_TEMP for auto-cleanup. - # - `./dist/main.js` is executing local `css-typed` as if installed (same as `bin`). - # But it is `$GITHUB_WORKSPACE/dist/main.js` b/c we `cd $RUNNER_TEMP`. - # - Use `diff` to compare the files. - # Use `-I '//.*'` to ignore the first line (comment) which has generated path and timestamp. - - name: "Test 1: Default case" run: | - cp src/fixtures/casing/casing.css $RUNNER_TEMP/casing.css - cp src/fixtures/casing/dashesOnly.d.css.ts $RUNNER_TEMP/expected.d.css.ts - - cd $RUNNER_TEMP - $GITHUB_WORKSPACE/dist/main.js '*.css' - diff --strip-trailing-cr -uI '//.*' expected.d.css.ts casing.d.css.ts + scripts/test.sh foo - - name: "Test 2: localsConvention, second position" + - name: "Test 2: localsConvention, first position" + if: success() || failure() run: | - cp src/fixtures/casing/casing.css $RUNNER_TEMP/casing.css - cp src/fixtures/casing/camelCaseOnly.d.css.ts $RUNNER_TEMP/expected.d.css.ts - - cd $RUNNER_TEMP - $GITHUB_WORKSPACE/dist/main.js '*.css' --localsConvention camelCaseOnly - diff --strip-trailing-cr -uI '//.*' expected.d.css.ts casing.d.css.ts + scripts/test.sh casing/casing "--localsConvention camelCaseOnly" casing/camelCaseOnly - - name: "Test 3: localsConvention, first position" + - name: "Test 3: localsConvention, second position" + if: success() || failure() run: | - cp src/fixtures/casing/casing.css $RUNNER_TEMP/casing.css - cp src/fixtures/casing/camelCaseOnly.d.css.ts $RUNNER_TEMP/expected.d.css.ts - - cd $RUNNER_TEMP - $GITHUB_WORKSPACE/dist/main.js --localsConvention camelCaseOnly '*.css' - diff --strip-trailing-cr -uI '//.*' expected.d.css.ts casing.d.css.ts + scripts/test.sh casing/casing "" casing/camelCaseOnly "--localsConvention camelCaseOnly" - name: "Test 4: relative outdir" + if: success() || failure() run: | - cp src/fixtures/casing/casing.css $RUNNER_TEMP/casing.css - cp src/fixtures/casing/dashesOnly.d.css.ts $RUNNER_TEMP/expected.d.css.ts - - cd $RUNNER_TEMP - $GITHUB_WORKSPACE/dist/main.js '*.css' --outdir generated - diff --strip-trailing-cr -uI '//.*' expected.d.css.ts generated/casing.d.css.ts + scripts/test.sh foo "--outdir generated" "" "" generated/ - name: "Test 5: absolute outdir" + if: success() || failure() run: | - cp src/fixtures/casing/casing.css $RUNNER_TEMP/casing.css - cp src/fixtures/casing/dashesOnly.d.css.ts $RUNNER_TEMP/expected.d.css.ts - - cd $RUNNER_TEMP - $GITHUB_WORKSPACE/dist/main.js '*.css' -o $GITHUB_WORKSPACE/generated - diff --strip-trailing-cr -uI '//.*' expected.d.css.ts $GITHUB_WORKSPACE/generated/casing.d.css.ts + scripts/test.sh foo "-o $GITHUB_WORKSPACE/generated" "" "" "$GITHUB_WORKSPACE"/generated/ Publish: if: ${{ github.ref == 'refs/heads/main' }} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3656338..06df752 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,6 @@ # Contributing -The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [[RFC2119]] [[RFC8174]] when, and only when, they appear in all capitals, as shown here. +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [[RFC2119]] [[RFC8174]] when, and only when, they appear in all capitals, as shown here. ## Getting started @@ -20,9 +20,12 @@ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "S The [src](./src) directory contains the main and test sources. -- [main.js](./src/main.js) represents the entry point (the CLI tool). -- [generate-declaration.js](./src/generate-declaration.js) represents the unit-tested JS logic. -- [fixtures](./src/fixtures) directory contains files for data-file-driven unit tests. +- [main.ts](./src/main.ts) represents the entry point (the CLI tool). +- [logic.ts](./src/logic.ts) represents the unit-tested logic. + +The [fixtures](fixtures) directory contains files for data-file-driven unit tests. + +The [scripts](./scripts) directory contains the esbuild build script and pipeline test helper script. ## Expectations @@ -31,7 +34,7 @@ All contributions MUST adhere to the following expectations. 1. Every change MUST have unit tests. 2. Every change MUST have a GitHub issue linked. 3. Any configuration option change SHOULD be discussed in a GitHub issue first. -4. The PR build (see [pipeline.yaml](./.github/workflows/pipeline.yaml)) MUST succeed. +4. The PR build and test (see [pipeline.yaml](./.github/workflows/pipeline.yaml)) MUST succeed. 5. I will squash-merge the changeset into `main` upon approval. [RFC2119]: https://www.rfc-editor.org/rfc/rfc2119 diff --git a/eslint.config.js b/eslint.config.js index d291fbc..5c9aa5d 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -4,6 +4,6 @@ export default [ ...connorjsConfig, { // Ignore declaration files used for tests. These represent generated files. - ignores: [`src/fixtures/**/*.d.css.ts`], + ignores: [`fixtures/**/*.d.css.ts`], }, ]; diff --git a/src/fixtures/casing/camelCase.d.css.ts b/fixtures/casing/camelCase.d.css.ts similarity index 88% rename from src/fixtures/casing/camelCase.d.css.ts rename to fixtures/casing/camelCase.d.css.ts index 7c003cd..f5cc596 100644 --- a/src/fixtures/casing/camelCase.d.css.ts +++ b/fixtures/casing/camelCase.d.css.ts @@ -1,4 +1,4 @@ -// Generated from `src/fixtures/casing/casing.css` by css-typed at $TIME +// Generated from `fixtures/casing/casing.css` by css-typed at $TIME const lowercase: string; const UPPERCASE: string; diff --git a/src/fixtures/casing/camelCaseOnly.d.css.ts b/fixtures/casing/camelCaseOnly.d.css.ts similarity index 78% rename from src/fixtures/casing/camelCaseOnly.d.css.ts rename to fixtures/casing/camelCaseOnly.d.css.ts index 64319e4..d75f79d 100644 --- a/src/fixtures/casing/camelCaseOnly.d.css.ts +++ b/fixtures/casing/camelCaseOnly.d.css.ts @@ -1,4 +1,4 @@ -// Generated from `src/fixtures/casing/casing.css` by css-typed at $TIME +// Generated from `fixtures/casing/casing.css` by css-typed at $TIME export const lowercase: string; export const uppercase: string; diff --git a/src/fixtures/casing/casing.css b/fixtures/casing/casing.css similarity index 100% rename from src/fixtures/casing/casing.css rename to fixtures/casing/casing.css diff --git a/src/fixtures/casing/dashes.d.css.ts b/fixtures/casing/dashes.d.css.ts similarity index 85% rename from src/fixtures/casing/dashes.d.css.ts rename to fixtures/casing/dashes.d.css.ts index df6d7e5..22cde24 100644 --- a/src/fixtures/casing/dashes.d.css.ts +++ b/fixtures/casing/dashes.d.css.ts @@ -1,4 +1,4 @@ -// Generated from `src/fixtures/casing/casing.css` by css-typed at $TIME +// Generated from `fixtures/casing/casing.css` by css-typed at $TIME const lowercase: string; const UPPERCASE: string; diff --git a/src/fixtures/casing/dashesOnly.d.css.ts b/fixtures/casing/dashesOnly.d.css.ts similarity index 78% rename from src/fixtures/casing/dashesOnly.d.css.ts rename to fixtures/casing/dashesOnly.d.css.ts index 20ac081..423956b 100644 --- a/src/fixtures/casing/dashesOnly.d.css.ts +++ b/fixtures/casing/dashesOnly.d.css.ts @@ -1,4 +1,4 @@ -// Generated from `src/fixtures/casing/casing.css` by css-typed at $TIME +// Generated from `fixtures/casing/casing.css` by css-typed at $TIME export const lowercase: string; export const UPPERCASE: string; diff --git a/src/fixtures/casing/none.d.css.ts b/fixtures/casing/none.d.css.ts similarity index 82% rename from src/fixtures/casing/none.d.css.ts rename to fixtures/casing/none.d.css.ts index 0575758..60ac1e9 100644 --- a/src/fixtures/casing/none.d.css.ts +++ b/fixtures/casing/none.d.css.ts @@ -1,4 +1,4 @@ -// Generated from `src/fixtures/casing/casing.css` by css-typed at $TIME +// Generated from `fixtures/casing/casing.css` by css-typed at $TIME const lowercase: string; const UPPERCASE: string; diff --git a/src/fixtures/foo.css b/fixtures/foo.css similarity index 100% rename from src/fixtures/foo.css rename to fixtures/foo.css diff --git a/src/fixtures/foo.d.css.ts b/fixtures/foo.d.css.ts similarity index 63% rename from src/fixtures/foo.d.css.ts rename to fixtures/foo.d.css.ts index b9a41fc..6d879f4 100644 --- a/src/fixtures/foo.d.css.ts +++ b/fixtures/foo.d.css.ts @@ -1,4 +1,4 @@ -// Generated from `src/fixtures/foo.css` by css-typed at $TIME +// Generated from `fixtures/foo.css` by css-typed at $TIME export const foo: string; export const bar: string; diff --git a/src/fixtures/foo.module.css b/fixtures/foo.module.css similarity index 100% rename from src/fixtures/foo.module.css rename to fixtures/foo.module.css diff --git a/src/fixtures/foo.module.d.css.ts b/fixtures/foo.module.d.css.ts similarity index 61% rename from src/fixtures/foo.module.d.css.ts rename to fixtures/foo.module.d.css.ts index a3398ee..16ee154 100644 --- a/src/fixtures/foo.module.d.css.ts +++ b/fixtures/foo.module.d.css.ts @@ -1,4 +1,4 @@ -// Generated from `src/fixtures/foo.module.css` by css-typed at $TIME +// Generated from `fixtures/foo.module.css` by css-typed at $TIME export const foo: string; export const bar: string; diff --git a/src/fixtures/no-declaration-file.css b/fixtures/no-declaration-file.css similarity index 100% rename from src/fixtures/no-declaration-file.css rename to fixtures/no-declaration-file.css diff --git a/package-lock.json b/package-lock.json index dba8318..24fb7e6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,7 +24,7 @@ "@types/lodash.camelcase": "^4.3.9", "@types/node": "^20.14.14", "esbuild": "~0.23.0", - "eslint-config-connorjs": "^1.0.0", + "eslint-config-connorjs": "^1.1.0", "husky": "^9.1.4", "is-ci": "^3.0.1", "lint-staged": "^15.2.8", @@ -2552,9 +2552,9 @@ } }, "node_modules/eslint-config-connorjs": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-connorjs/-/eslint-config-connorjs-1.0.0.tgz", - "integrity": "sha512-KLdWBsS0udknT/Yqo7nJjwkVDYFsZgPPVkJupwLJMTDl8P8Oa27z502cHgl6N2/HPV6x/UYxC7Z20p8m09/Gow==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-connorjs/-/eslint-config-connorjs-1.1.0.tgz", + "integrity": "sha512-mQFdBDe/8q0bCI3HBaVvf1sm/Sk6yT9owoZxx8O1dguDVGwtBH3yykMpckBLYbU/6jz5zvr9r/V0RA+4z++wXw==", "dev": true, "dependencies": { "@eslint/js": "^8.57.0", diff --git a/package.json b/package.json index 33e2160..0722196 100644 --- a/package.json +++ b/package.json @@ -26,12 +26,14 @@ "engines": { "node": ">=18" }, + "engineStrict": true, "bin": { "css-typed": "dist/main.js" }, "files": [ "dist" ], + "main": "dist/main.js", "scripts": { "build": "node scripts/build.js", "ci-build": "npm-run-all -l -p eslint prettier test tsc -s build", @@ -43,7 +45,6 @@ "test": "vitest run", "tsc": "tsc" }, - "engineStrict": true, "dependencies": { "@commander-js/extra-typings": "^12.1.0", "commander": "^12.1.0", @@ -57,7 +58,7 @@ "@types/lodash.camelcase": "^4.3.9", "@types/node": "^20.14.14", "esbuild": "~0.23.0", - "eslint-config-connorjs": "^1.0.0", + "eslint-config-connorjs": "^1.1.0", "husky": "^9.1.4", "is-ci": "^3.0.1", "lint-staged": "^15.2.8", @@ -65,6 +66,5 @@ "prettier": "^3.3.3", "typescript": "^5.5.4", "vitest": "^2.0.5" - }, - "main": "dist/main.js" + } } diff --git a/scripts/test.sh b/scripts/test.sh new file mode 100755 index 0000000..8d7e62e --- /dev/null +++ b/scripts/test.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash + +# $1 is the input name, relative to `fixtures`. Required. +input=$1 + +# $2 is the standard (before) options. Defaults to "". +IFS=" " read -r -a beforeOpts <<< "${2:-}" + +# $3 is the output name, relative to `fixtures`. Defaults to $1. +output=${3:-$1} + +# $4 is the after options. Use an array. Defaults to "". +IFS=" " read -r -a afterOpts <<< "${4:-}" + +# $5 is the path prefix for output. Defaults to "". +prefix=${5:-} + +# Run from $RUNNER_TEMP for auto-cleanup. +cp fixtures/${input}.css $RUNNER_TEMP/test.css +cp fixtures/${output}.d.css.ts $RUNNER_TEMP/expected.d.css.ts +pushd $RUNNER_TEMP > /dev/null || exit + +# `./dist/main.js` is executing local `css-typed` as if installed (same as `bin`). +# But it is `$GITHUB_WORKSPACE/dist/main.js` b/c we `cd $RUNNER_TEMP`. +echo "css-typed ${beforeOpts[*]} \"*.css\" ${afterOpts[*]}" +# shellcheck disable=SC2068 +$GITHUB_WORKSPACE/dist/main.js ${beforeOpts[@]} "*.css" ${afterOpts[@]} + +# Use `diff` to compare the files. +# Use `-I '//.*'` to ignore the first line (comment) which has generated path and timestamp. +diff --color=auto --strip-trailing-cr -uI "//.*" expected.d.css.ts ${prefix}test.d.css.ts diff --git a/src/logic.test.ts b/src/logic.test.ts index 9737a02..be1ab81 100644 --- a/src/logic.test.ts +++ b/src/logic.test.ts @@ -1,4 +1,3 @@ -import { readFileSync } from "node:fs"; import path from "node:path"; import * as process from "node:process"; @@ -31,10 +30,8 @@ describe(`css-typed`, () => { const inputPath = fixtureFile(inputFilename); const outputPath = fixtureFile(outputFilename); - const expected = readFileSync(outputPath, { encoding: `utf8` }); - const generated = await generateDeclaration(inputPath, `$TIME`, options); - expect(generated).toStrictEqual(expected); + await expect(generated).toMatchFileSnapshot(outputPath); }); }); @@ -57,5 +54,5 @@ describe(`css-typed`, () => { }); function fixtureFile(filename: string) { - return path.join(import.meta.dirname, `fixtures`, filename); + return path.join(import.meta.dirname, `..`, `fixtures`, filename); }