Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,59 @@ jobs:
uses: ./.github/workflows/ci.yml
secrets:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
release:
name: Release
needs: [ci]
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
permissions:
id-token: write
contents: write
issues: write
pull-requests: write
steps:
- name: Clone repository
# No deploy key: publishing to JSR is OIDC (id-token: write, package
# linked to this repo), and @semantic-release/git pushes the release
# commit with the checkout-persisted GITHUB_TOKEN, which `contents:
# write` covers. That push only needs an SSH deploy key if `main`
# becomes a protected branch — GITHUB_TOKEN cannot push past protection.
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
fetch-depth: 0
# semantic-release runs on Node, not Deno: @sebbo2002/semantic-release-jsr
# shell-spawns its own downloaded `deno publish`, and that spawn's args are
# mangled by Deno's child_process Node-compat layer (the publish fails with
# "unrecognized subcommand"). Under real Node the plugin works as designed.
- name: Setup Node.js
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: 22
- name: Setup Deno
uses: denoland/setup-deno@667a34cdef165d8d2b2e98dde39547c9daac7282 # v2
with:
deno-version: v2.x
# No shared dependency cache on the publish job: it is the only job
# with id-token: write, so it must not restore a cache another job
# could have populated.
cache: false
- name: Install dependencies
run: deno ci
- name: Run semantic-release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Every package is pinned to an exact version so a malicious new release
# of a popular package can't execute here while id-token: write is active
# — npx has no lockfile, so the version IS the pin. @sebbo2002/semantic-
# release-jsr stays 3.2.1 because 4.0.0 ships a CJS bundle semantic-release
# rejects as EPLUGINSCONF (https://github.com/sebbo2002/semantic-release-jsr/issues/153).
# Bump these deliberately.
run: >-
npx
--package semantic-release@25.0.5
--package @semantic-release/changelog@6.0.3
--package @semantic-release/exec@7.1.0
--package @semantic-release/git@10.0.1
--package @semantic-release/github@12.0.8
--package @sebbo2002/semantic-release-jsr@3.2.1
semantic-release
49 changes: 49 additions & 0 deletions .github/workflows/pr-title.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
name: PR Title

on:
# pull_request_target runs in the base-repo context (needed to read fork PR
# titles), but this workflow checks out NO code, writes NO cache, and holds
# only pull-requests: read — so it can't be used to poison a cache or reach
# secrets. Keep it that way: never add actions/checkout or a build step here.
pull_request_target:
types:
- opened
- edited
- synchronize

permissions:
pull-requests: read

jobs:
validate:
name: Validate PR title
runs-on: ubuntu-latest
steps:
- name: Check PR title follows Conventional Commits
uses: amannn/action-semantic-pull-request@e32d7e603df1aa1ba07e981f2a23455dee596825 # v5
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
types: |
feat
fix
docs
style
refactor
perf
test
build
ci
chore
requireScope: false
subjectPattern: ^.+$
subjectPatternError: |
PR title must follow Conventional Commits format:
<type>(<scope>): <description>

Examples:
feat: add server-side caching
fix(router): handle undefined params
feat!: breaking change

See CONTRIBUTING.md for details.
16 changes: 0 additions & 16 deletions .github/workflows/publish.yml

This file was deleted.

64 changes: 64 additions & 0 deletions .releaserc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
{
"branches": ["main"],
"tagFormat": "${version}",
"plugins": [
[
"@semantic-release/commit-analyzer",
{
"preset": "angular",
"releaseRules": [
{ "breaking": true, "release": "major" },
{ "revert": true, "release": "patch" },
{ "type": "feat", "release": "minor" },
{ "type": "fix", "release": "patch" },
{ "type": "perf", "release": "patch" },
{ "type": "revert", "release": "patch" },
{ "type": "build", "scope": "deps", "release": "patch" },
{ "type": "chore", "scope": "deps", "release": "patch" }
]
}
],
[
"@semantic-release/release-notes-generator",
{
"preset": "angular",
"writerOpts": {
"commitsSort": ["subject", "scope"]
}
}
],
[
"@semantic-release/changelog",
{
"changelogFile": "CHANGELOG.md"
}
],
[
"@semantic-release/exec",
{
"prepareCmd": "deno fmt CHANGELOG.md"
}
],
[
"@sebbo2002/semantic-release-jsr",
{
"configFile": "deno.json",
"allowDirty": true,
"publishArgs": ["--no-check"]
}
],
[
"@semantic-release/github",
{
"assets": []
}
],
[
"@semantic-release/git",
{
"assets": ["CHANGELOG.md", "deno.lock", "deno.json"],
"message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
}
]
]
}
24 changes: 16 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -417,34 +417,42 @@ The 3 preprocessors available are:
- Less: A backwards-compatible language extension for CSS.
- Stylus: An expressive, dynamic, and robust CSS language.

To use a preprocessor, you will have to import the wrapper for the preprocessor
from this esbuild plugin. These wrappers are stored in separate files so that
the preprocessors are only imported when you use them.
To use a preprocessor, import its wrapper from this plugin **and add the
preprocessor itself to your own dependencies**, then pass the module in:

- `@udibo/esbuild-plugin-postcss/sass`: Exports the `sassPreprocessor` function.
- `@udibo/esbuild-plugin-postcss/less`: Exports the `lessPreprocessor` function.
- `@udibo/esbuild-plugin-postcss/stylus`: Exports the `stylusPreprocessor`
function.

Each of these functions take a single argument, which is the options for the
preprocessor. To learn more about the preprocessors and the options for them,
see the [Sass documentation](https://sass-lang.com/documentation/js-api),
`sass`, `less`, and `stylus` are deliberately **not** dependencies of this
package. JSR resolves a package's dependencies as one flat set across every
export, so importing them here would install all three for everyone — including
projects that only build plain CSS. Passing the module in keeps them optional:
you depend on exactly the preprocessors you use, at whatever version you choose.

Each function takes the preprocessor module first, then its options. To learn
more about the preprocessors and their options, see the
[Sass documentation](https://sass-lang.com/documentation/js-api),
[Less documentation](https://lessjs.org/api/), and
[Stylus documentation](https://stylus-lang.com/docs/js-api).
[Stylus documentation](https://stylus-lang.com/docs/js-api). Annotate `options`
with the preprocessor's own option type to keep full type checking, for example
`sassPreprocessor<sass.Options<"async">>(sass, { ... })`.

You can use one or more of these preprocessors in the preprocessors array. Below
is an example of using the Sass preprocessor with the default options for it.

```ts
import esbuild from "esbuild";
import * as sass from "sass";
import { postCSSPlugin } from "@udibo/esbuild-plugin-postcss";
import { sassPreprocessor } from "@udibo/esbuild-plugin-postcss/sass";

esbuild.build({
plugins: [
postCSSPlugin({
preprocessors: [
sassPreprocessor(),
sassPreprocessor(sass),
],
}),
],
Expand Down
6 changes: 4 additions & 2 deletions less.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import {
import * as path from "@std/path";
import { describe, it } from "@std/testing/bdd";

import less from "less";

import { lessPreprocessor } from "./less.ts";
import { build } from "./test-utils.ts";
import { postCSSPlugin } from "./postcss.ts";
Expand All @@ -19,7 +21,7 @@ describe("less", () => {
["./main.less"],
{
plugins: [postCSSPlugin({
preprocessors: [lessPreprocessor()],
preprocessors: [lessPreprocessor(less)],
})],
},
);
Expand All @@ -46,7 +48,7 @@ describe("less", () => {
["./main.ts"],
{
plugins: [postCSSPlugin({
preprocessors: [lessPreprocessor()],
preprocessors: [lessPreprocessor(less)],
})],
bundle: true,
},
Expand Down
32 changes: 27 additions & 5 deletions less.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,57 @@
/**
* The Less preprocessor for the esbuild PostCSS Plugin.
*
* `less` is deliberately **not** a dependency of this package — you pass the
* module in. JSR resolves a package's dependencies as one flat set across every
* export, so a `less` import here would be installed by every consumer,
* including those that only ever build plain CSS.
*
* @module
*/

import type { Preprocessor, PreprocessorResults } from "./postcss.ts";
import less from "less";

/**
* The part of the `less` module this preprocessor calls. The real module
* satisfies it — `import less from "less"` and hand it over.
*/
export interface LessModule {
render(
input: string,
options: object,
): Promise<{ css: string }>;
}

/**
* Creates a Less preprocessor for the esbuild PostCSS Plugin.
*
* Add `less` to your own dependencies and pass it in. Type `options` with
* Less's own option type to keep full checking.
*
* ```ts
* import esbuild from "esbuild";
* import less from "less";
* import { postCSSPlugin } from "@udibo/esbuild-plugin-postcss";
* import { lessPreprocessor } from "@udibo/esbuild-plugin-postcss/less";
*
* esbuild.build({
* plugins: [postCSSPlugin({
* preprocessors: [lessPreprocessor()],
* preprocessors: [lessPreprocessor(less)],
* })],
* entryPoints: ["./src/index.less"],
* outdir: "./dist",
* bundle: true,
* });
* ```
*
* @param options - The options for the less preprocessor.
* @param less - The `less` module.
* @param options - The options for the less preprocessor. `filename` is set per
* file, so supplying it has no effect.
* @returns The less preprocessor.
*/
export function lessPreprocessor(
options?: Omit<less.Options, "filename">,
export function lessPreprocessor<Options extends object = object>(
less: LessModule,
options?: Options,
): Preprocessor {
return {
filter: /\.less$/,
Expand Down
10 changes: 6 additions & 4 deletions sass.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import {
import * as path from "@std/path";
import { describe, it } from "@std/testing/bdd";

import * as sass from "sass";

import { sassPreprocessor } from "./sass.ts";
import { build } from "./test-utils.ts";
import { postCSSPlugin } from "./postcss.ts";
Expand All @@ -20,7 +22,7 @@ describe("sass", () => {
["./a.scss"],
{
plugins: [postCSSPlugin({
preprocessors: [sassPreprocessor()],
preprocessors: [sassPreprocessor(sass)],
})],
},
);
Expand All @@ -47,7 +49,7 @@ describe("sass", () => {
["./a.ts"],
{
plugins: [postCSSPlugin({
preprocessors: [sassPreprocessor()],
preprocessors: [sassPreprocessor(sass)],
})],
bundle: true,
},
Expand Down Expand Up @@ -77,7 +79,7 @@ describe("sass", () => {
["./b.sass"],
{
plugins: [postCSSPlugin({
preprocessors: [sassPreprocessor()],
preprocessors: [sassPreprocessor(sass)],
})],
},
);
Expand All @@ -104,7 +106,7 @@ describe("sass", () => {
["./b.ts"],
{
plugins: [postCSSPlugin({
preprocessors: [sassPreprocessor()],
preprocessors: [sassPreprocessor(sass)],
})],
bundle: true,
},
Expand Down
Loading
Loading