Skip to content

Commit 9e3b8d4

Browse files
committed
feat: drop support for Deno 1.x and add support for Deno 2.0+, backported from 3.18.0-beta.0
1 parent a36d886 commit 9e3b8d4

18 files changed

+391
-173
lines changed

.github/workflows/test.yml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,3 +122,25 @@ jobs:
122122
run: npx vitest
123123
env:
124124
NODE_OPTIONS: --unhandled-rejections=strict
125+
126+
deno:
127+
name: Test Deno
128+
runs-on: ubuntu-latest
129+
strategy:
130+
fail-fast: false
131+
matrix:
132+
deno-version: [ 2.0.x, 2.1.x, 2.2.x ]
133+
134+
steps:
135+
- name: Checkout repository
136+
uses: actions/checkout@v4
137+
with:
138+
fetch-depth: 1
139+
140+
- name: Set up Deno ${{ matrix.deno-version }}
141+
uses: denoland/setup-deno@v1
142+
with:
143+
deno-version: ${{ matrix.deno-version }}
144+
145+
- name: Run Deno tests
146+
run: deno test --config=deno.json --allow-env

CHANGELOG.md

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
# Changelog
22

3-
43
## Pre release note: ✨ Ansis v4 - Smaller package, and cleaner API
54

65
Ansis v4 drops legacy baggage and unused artifacts.
76
This release brings a stable and more compact ANSI library.
87

8+
## 4.0.0-beta.21 (2025-04-24)
9+
10+
### ⚠️ BREAKING CHANGE
11+
12+
- feat: drop support for Deno 1.x (EOL - 9 Oct 2024) and add support for Deno 2.0+, #37
13+
Backported from 3.18.0-beta.0
14+
915
## 4.0.0-beta.20 (2025-04-21)
1016

1117
### ⚠️ BREAKING CHANGE
@@ -74,10 +80,10 @@ This change ensures compatibility with the latest version of Ansis, where `AnsiC
7480

7581
The following legacy method aliases have been removed:
7682

77-
| ❌ Removed Method | ✅ Use Instead |
78-
|-------------------|----------------|
79-
| `ansi256(code)` | `fg(code)` |
80-
| `bgAnsi256(code)` | `bg(code)` |
83+
| ❌ Removed Method | ✅ Use Instead |
84+
|-------------------|---------------|
85+
| `ansi256(code)` | `fg(code)` |
86+
| `bgAnsi256(code)` | `bg(code)` |
8187

8288
These aliases were originally added for compatibility with Chalk.
8389
Starting with this release, Ansis focuses on a cleaner and compact API, free from duplicated methods and legacy layers.
@@ -325,6 +331,11 @@ Deprecated.
325331

326332
---
327333

334+
## 3.18.0-beta.0 (2025-04-22)
335+
336+
- feat: drop support for Deno 1.x (EOL - 9 Oct 2024) and add support for Deno 2.0+, #37
337+
- test: add Deno tests on GitHub
338+
328339
## 3.17.0 (2025-03-02)
329340

330341
- feat: add support for `typescript` < `5.6` to fix TS2526 error:

README.md

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,6 @@ Ansis is focused on [small size](#compare-size) and [speed](#benchmark) while pr
4545
- Reliable [CLI testing](#cli-testing) by forcing specific [color levels](#color-levels): no color, 16, 256 or truecolor
4646
- [Replacement](#why-ansis) for [`chalk`](#replacing-chalk) [`ansi-colors`](#replacing-ansi-colors) [`colorette`](#replacing-colorette) [`picocolors`](#replacing-picocolors)
4747

48-
<!-- - Chromium-based browsers can display truecolor text in console.
49-
- Browsers that do not support ANSI codes will display black/white text in console. -->
50-
5148
## 🛠️ Usage
5249

5350
```js

deno.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"lint": {
3+
"files": {
4+
"exclude": ["node_modules", "test"]
5+
}
6+
},
7+
"test": {
8+
"include": ["test-deno/cases/**/*.test.js"],
9+
"exclude": ["node_modules"]
10+
}
11+
}

deno.lock

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

package.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "ansis",
3-
"version": "4.0.0-beta.20",
3+
"version": "4.0.0-beta.21",
44
"description": "A small and fast library for applying ANSI colors in terminal or browser console",
55
"keywords": [
66
"ansi",
@@ -65,7 +65,11 @@
6565
"test:esm": "node ./test/package/esm/test.mjs",
6666
"test:tsc": "vitest run ./test/ts-compiler.test.js",
6767
"test:levels": "vitest run ./test/color-levels.test.js",
68-
"test:stackoverflow": "node ./test/rangeErrorTest.js",
68+
"test:range": "node ./test/rangeErrorTest.js",
69+
"test:deno": "deno test --allow-env",
70+
"test-deno:manual": "deno test ./test-deno/manual/nocolor-deny.test.js",
71+
"test-deno:debug": "deno ./test-deno/manual/deno-debug.js",
72+
"test-deno:truecolor": "deno test ./test-deno/cases/truecolor.test.js --allow-env",
6973
"test:coverage": "vitest run --coverage",
7074
"publish:public": "(npm run build) && npm publish ./dist --access public",
7175
"publish:beta": "(npm run build) && npm publish ./dist --tag beta"

package.npm.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name":"ansis",
3-
"version":"4.0.0-beta.20",
3+
"version":"4.0.0-beta.21",
44
"description":"ANSI color lib",
55
"keywords":["ansi","color","cli"],
66
"license":"ISC",

src/color-support.js

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
import { LEVEL_UNDEFINED, LEVEL_BW, LEVEL_16COLORS, LEVEL_256COLORS, LEVEL_TRUECOLOR } from './color-levels.js';
22
import { keys, separator } from './misc.js';
33

4+
// Contains stringified keys of environment variables.
5+
let envKeys;
6+
47
/**
5-
* Detect color level.
8+
* Auto detect color level.
69
*
710
* Truecolor is supported by:
811
* - some CI (e.g. GitHub CI)
@@ -17,9 +20,8 @@ import { keys, separator } from './misc.js';
1720
* @param {boolean} isWin
1821
* @return {number}
1922
*/
20-
let detectColors = (env, isTTY, isWin) => {
23+
let autoDetectLevel = (env, isTTY, isWin) => {
2124
let term = env.TERM;
22-
let envKeys = separator + keys(env).join(separator);
2325

2426
// Note: the order of checks is important!
2527

@@ -112,26 +114,24 @@ export const getLevel = (mockThis) => {
112114
* @param {RegExp} regex The RegEx to match all possible flags.
113115
* @return {boolean}
114116
*/
115-
let hasFlag = (regex) => argv?.some((value) => regex.test(value));
116-
117-
let _this = mockThis ?? globalThis;
118-
let Deno = _this.Deno;
119-
let isDeno = !!Deno;
120-
let proc = _this.process || Deno || {};
117+
let hasFlag = (regex) => argv.some((value) => regex.test(value));
121118

122-
// Node -> `argv`, Deno -> `args`
123-
let argv = proc.argv ?? proc.args;
119+
// Note: In Deno 2.0+, the `process` is available globally
120+
let thisRef = mockThis ?? globalThis;
121+
let proc = thisRef.process ?? {};
122+
let argv = proc.argv ?? [];
124123
let env = proc.env ?? {};
125124
let colorLevel = LEVEL_UNDEFINED;
126125

127-
if (isDeno) {
128-
try {
129-
// Deno requires the permission for the access to env, use the `--allow-env` flag: deno run --allow-env ./app.js
130-
env = env.toObject();
131-
} catch (e) {
132-
// Deno: if interactive permission is not granted, do nothing, no colors
133-
colorLevel = LEVEL_BW;
134-
}
126+
try {
127+
// keys(env) triggers a Deno permission request; throws if access is denied
128+
// stringify environment variable keys to check for specific ones using a RegExp
129+
envKeys = separator + keys(env).join(separator);
130+
} catch (error) {
131+
// if the permission is not granted, environment variables have no effect, even variables like FORCE_COLOR will be ignored
132+
// env now points to a new empty object to avoid Deno requests for every env access in code below
133+
env = {};
134+
colorLevel = LEVEL_BW;
135135
}
136136

137137
// PM2 does not set process.stdout.isTTY, but colors may be supported (depends on actual terminal)
@@ -141,7 +141,7 @@ export const getLevel = (mockThis) => {
141141
// runtime values supported colors: `nodejs`, `edge`, `experimental-edge`
142142

143143
// whether the output is supported
144-
let isTTY = isPM2 || env.NEXT_RUNTIME?.includes('edge') || (isDeno ? Deno.isatty(1) : !!proc.stdout?.isTTY);
144+
let isTTY = isPM2 || env.NEXT_RUNTIME?.includes('edge') || !!proc.stdout?.isTTY;
145145

146146
// enforce a specific color support:
147147
// FORCE_COLOR=false // disables colors
@@ -160,7 +160,7 @@ export const getLevel = (mockThis) => {
160160
let forceColorValue = env[FORCE_COLOR];
161161

162162
// mapping FORCE_COLOR values to color level values
163-
let forceLevel = {
163+
let forcedLevel = {
164164
false: LEVEL_BW,
165165
0: LEVEL_BW,
166166
1: LEVEL_16COLORS,
@@ -169,25 +169,29 @@ export const getLevel = (mockThis) => {
169169
}[forceColorValue] ?? LEVEL_UNDEFINED;
170170

171171
// if FORCE_COLOR is present and is neither 'false' nor '0', OR has one of the flags: --color --color=true --color=always
172-
let isForceEnabled = (FORCE_COLOR in env && forceLevel) || hasFlag(/^--color=?(true|always)?$/);
172+
let isForced = (FORCE_COLOR in env && forcedLevel) || hasFlag(/^--color=?(true|always)?$/);
173173

174-
if (isForceEnabled) colorLevel = forceLevel;
174+
if (isForced) colorLevel = forcedLevel;
175175

176176
// if colorLevel === LEVEL_UNDEFINED, attempt to detect color level, returns 0, 1, 2 or 3
177-
if (!~colorLevel) colorLevel = detectColors(env, isTTY, (isDeno ? Deno.build.os : proc.platform) === 'win32');
177+
if (!~colorLevel) colorLevel = autoDetectLevel(env, isTTY, proc.platform === 'win32');
178178

179179
// if force disabled: FORCE_COLOR=0 or FORCE_COLOR=false
180-
if (!forceLevel
180+
if (!forcedLevel
181181
|| !!env.NO_COLOR
182182
// --no-color --color=false --color=never
183183
|| hasFlag(/^--(no-color|color=(false|never))$/)) return LEVEL_BW;
184184

185185
// Detect browser support
186-
if (!!_this.window?.chrome) return LEVEL_TRUECOLOR;
186+
if (!!thisRef.window?.chrome) return LEVEL_TRUECOLOR;
187187

188188
// API Rule: If color output is force enabled but the color level is detected as B&W (e.g., TERM is dumb),
189189
// enable truecolor since color depth support does not matter.
190190

191191
// Optimisation: `!colorLevel` is equivalent to `colorLevel === LEVEL_BW`
192-
return isForceEnabled && !colorLevel ? LEVEL_TRUECOLOR : colorLevel;
192+
//return isForced && !colorLevel ? LEVEL_TRUECOLOR : colorLevel;
193+
194+
// If color output is forced but the environment doesn't support it, allow truecolor anyway,
195+
// for example, when saving CLI output snapshots to a file.
196+
return isForced && !colorLevel ? LEVEL_TRUECOLOR : colorLevel;
193197
};

test-deno/cases/ansi256.test.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { assertEquals } from 'https://deno.land/std/assert/mod.ts';
2+
3+
// import env variables to simulate ANSI 256 color space
4+
import '../../test/env/ansi256-colors.js';
5+
6+
//import ansis, { hex, fg } from '../../src/index.mjs'; // for debugging only
7+
import ansis, { hex, fg } from 'ansis';
8+
9+
Deno.test('isSupported()', () => {
10+
const received = ansis.isSupported();
11+
const expected = true;
12+
assertEquals(received, expected);
13+
});
14+
15+
Deno.test('ansi 256', () => {
16+
const received = fg(40)`foo`;
17+
const expected = '\x1b[38;5;40mfoo\x1b[39m';
18+
assertEquals(received, expected);
19+
});
20+
21+
Deno.test('fallback to 256', () => {
22+
const received = hex('#00c200')`foo`;
23+
const expected = '\x1b[38;5;40mfoo\x1b[39m';
24+
assertEquals(received, expected);
25+
});

test-deno/cases/nocolor.test.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { assertEquals } from 'https://deno.land/std/assert/mod.ts';
2+
3+
// NOTE:
4+
// Test it with the `--allow-env` flag
5+
6+
// import env variables to simulate no color, it works only with `--allow-env` flag
7+
import '../../test/env/no-color.js';
8+
9+
//import ansis, { hex } from '../../src/index.mjs';
10+
import ansis, { hex } from 'ansis';
11+
12+
Deno.test('isSupported()', () => {
13+
const received = ansis.isSupported();
14+
const expected = false;
15+
assertEquals(received, expected);
16+
});
17+
18+
Deno.test('no color', () => {
19+
const received = hex('#00c200')`foo`;
20+
const expected = 'foo';
21+
assertEquals(received, expected);
22+
});

test-deno/cases/truecolor.test.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { assertEquals } from 'https://deno.land/std/assert/mod.ts';
2+
3+
// import env variables to simulate ANSI 256 color space
4+
import '../../test/env/truecolor.js';
5+
6+
//import ansis, { hex} from '../../src/index.mjs'; // for debugging only
7+
import ansis, { hex } from 'ansis';
8+
9+
Deno.test('isSupported()', () => {
10+
const received = ansis.isSupported();
11+
const expected = true;
12+
assertEquals(received, expected);
13+
});
14+
15+
Deno.test('truecolor', () => {
16+
const received = hex('#00c200')`foo`;
17+
const expected = '\x1b[38;2;0;194;0mfoo\x1b[39m';
18+
assertEquals(received, expected);
19+
});

test-deno/manual/deno-debug.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// NOTE:
2+
// Test it w/o the `--allow-env` flag to simulate deny permissions,
3+
// that should leads to no color output.
4+
5+
import ansis, { hex } from '../../src/index.mjs';
6+
//import ansis, { hex } from 'ansis';
7+
8+
console.log(hex('#a167fa')('\n>> Deno color output\n'));

0 commit comments

Comments
 (0)