Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
92ca5ac
feat: add support for node type stripping
nwidynski Mar 18, 2025
8ce69cc
fix: remove typo
nwidynski Mar 19, 2025
01b4813
fix: type error
nwidynski Mar 19, 2025
c9558fe
chore: review comments [no ci]
nwidynski Mar 19, 2025
e84bfbd
chore: review comments [no ci]
nwidynski Mar 19, 2025
9955f1d
chore: review comments
nwidynski Mar 19, 2025
15daf6f
Update src/index.ts
JounQin Mar 19, 2025
644ff4f
chore: review comments & argv detection
nwidynski Mar 19, 2025
a49d367
chore: prettier formatting
nwidynski Mar 19, 2025
8ad85ed
chore: prettier formatting
nwidynski Mar 19, 2025
0c32d51
chore: prettier formatting
nwidynski Mar 19, 2025
2651e64
feat: add test case
nwidynski Mar 19, 2025
246809d
feat: add node version safeguard
nwidynski Mar 19, 2025
f1b8175
chore: prettier formatting
nwidynski Mar 19, 2025
3614dd3
fix: apply flag conditionally
nwidynski Mar 19, 2025
aa8f01c
fix: runner test case
nwidynski Mar 19, 2025
a7f2a2e
fix: node test runner test case
nwidynski Mar 19, 2025
87a0177
chore: prettier formatting
nwidynski Mar 19, 2025
0cf4a10
chore: remove codecov limitation
nwidynski Mar 19, 2025
515779c
Create good-walls-brush.md
JounQin Mar 19, 2025
e569c75
fix: changeset review
nwidynski Mar 19, 2025
66da8b5
chore: prettier formatting
nwidynski Mar 19, 2025
04a2470
fix: duplicate
nwidynski Mar 19, 2025
6ed663e
chore: update readme
nwidynski Mar 19, 2025
7fcfd20
fix: conditionally apply default in test case
nwidynski Mar 20, 2025
0542def
chore: test fix and docment update
JounQin Mar 20, 2025
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
15 changes: 15 additions & 0 deletions .changeset/good-walls-brush.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
"synckit": minor
---

feat: add support for `--experimental-strip-types`
Comment thread
nwidynski marked this conversation as resolved.

Introducing the `node` runner, which will replace `ts-node` as the new default:

- when running on Node 22 with the `--experimental-strip-types`
flag enabled via `NODE_OPTIONS` env or cli args
- or when running on Node 23+ without `--no-experimental-strip-types`
flag enabled via `NODE_OPTIONS` env or cli args

An error will be thrown when attempting to run with `node` on unsupported versions (<22).
On these versions, the default runner remains `ts-node` when available.
1 change: 0 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,5 @@ jobs:

- name: Codecov
uses: codecov/codecov-action@v5
if: ${{ matrix.node == 18 || matrix.node == 18.18 }}
with:
token: ${{ secrets.CODECOV_TOKEN }}
17 changes: 13 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ Perform async work synchronously in Node.js using `worker_threads` with first-cl
- [Options](#options)
- [Envs](#envs)
- [TypeScript](#typescript)
- [`ts-node`](#ts-node)
- [`node` (Default, Node 23+)](#node-default-node-23)
- [`ts-node` (Default)](#ts-node-default)
- [`esbuild-register`](#esbuild-register)
- [`esbuild-runner`](#esbuild-runner)
- [`swc`](#swc)
Expand Down Expand Up @@ -53,7 +54,7 @@ import { createSyncFn } from 'synckit'

// the worker path must be absolute
const syncFn = createSyncFn(require.resolve('./worker'), {
tsRunner: 'tsx', // optional, can be `'ts-node' | 'esbuild-register' | 'esbuild-runner' | 'tsx'`
tsRunner: 'tsx', // optional, can be `'node' | 'ts-node' | 'esbuild-register' | 'esbuild-runner' | 'tsx'`
})

// do whatever you want, you will get the result synchronously!
Expand Down Expand Up @@ -126,9 +127,17 @@ export interface GlobalShim {

### TypeScript

#### `ts-node`
#### `node` (Default, Node 23+)

If you want to use `ts-node` for worker file (a `.ts` file), it is supported out of box!
On recent Node versions, you may select this runner to execute your worker file (a `.ts` file) in the native runtime.

As of Node v23, this feature is supported out of the box. To enable it in the current LTS, you can pass the [`--experimental-strip-types`](https://nodejs.org/docs/latest-v22.x/api/typescript.html#type-stripping) flag to the process. Visit the [documentation](https://nodejs.org/docs/latest/api/typescript.html#type-stripping) to learn more.

When `synckit` detects the process to be running with this flag, it will execute the worker file with the `node` runner by default.

#### `ts-node` (Default)

Prior to Node v23, you may want to use `ts-node` to execute your worker file (a `.ts` file).

If you want to use a custom tsconfig as project instead of default `tsconfig.json`, use `TS_NODE_PROJECT` env. Please view [ts-node](https://github.com/TypeStrong/ts-node#tsconfig) for more details.

Expand Down
49 changes: 40 additions & 9 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
export * from './types.js'

export const TsRunner = {
// https://nodejs.org/docs/latest/api/typescript.html#type-stripping
Node: 'node',
// https://github.com/TypeStrong/ts-node
TsNode: 'ts-node',
// https://github.com/egoist/esbuild-register
Expand All @@ -55,7 +57,23 @@
SYNCKIT_TS_RUNNER,
} = process.env

const IS_NODE_20 = Number(process.versions.node.split('.')[0]) >= 20
export const MTS_SUPPORTED_NODE_VERSION = 16
export const LOADER_SUPPORTED_NODE_VERSION = 20
export const STRIP_TYPES_DEFAULT_NODE_VERSION = 23
export const STRIP_TYPES_SUPPORTED_NODE_VERSION = 22

const NODE_VERSION = Number.parseFloat(process.versions.node)
const STRIP_TYPES_FLAG = '--experimental-strip-types'
const NO_STRIP_TYPES_FLAG = '--no-experimental-strip-types'
const IS_TYPE_STRIPPING_ENABLED =
(NODE_VERSION >= STRIP_TYPES_DEFAULT_NODE_VERSION &&
!(

Check warning on line 70 in src/index.ts

View check run for this annotation

Codecov / codecov/patch

src/index.ts#L70

Added line #L70 was not covered by tests
NODE_OPTIONS?.includes(NO_STRIP_TYPES_FLAG) ||
process.argv.includes(NO_STRIP_TYPES_FLAG)

Check warning on line 72 in src/index.ts

View check run for this annotation

Codecov / codecov/patch

src/index.ts#L72

Added line #L72 was not covered by tests
)) ||
(NODE_VERSION >= STRIP_TYPES_SUPPORTED_NODE_VERSION &&
(NODE_OPTIONS?.includes(STRIP_TYPES_FLAG) ||
process.argv.includes(STRIP_TYPES_FLAG)))

export const DEFAULT_TIMEOUT = SYNCKIT_TIMEOUT ? +SYNCKIT_TIMEOUT : undefined

Expand All @@ -80,8 +98,6 @@
},
]

export const MTS_SUPPORTED_NODE_VERSION = 16
Comment thread
nwidynski marked this conversation as resolved.

let syncFnCache: Map<string, AnyFn> | undefined

export interface SynckitOptions {
Expand Down Expand Up @@ -205,11 +221,27 @@
}
}

if (tsRunner == null && isPkgAvailable(TsRunner.TsNode)) {
tsRunner = TsRunner.TsNode
if (tsRunner == null) {
if (IS_TYPE_STRIPPING_ENABLED) {
tsRunner = TsRunner.Node

Check warning on line 226 in src/index.ts

View check run for this annotation

Codecov / codecov/patch

src/index.ts#L226

Added line #L226 was not covered by tests
} else if (isPkgAvailable(TsRunner.TsNode)) {
tsRunner = TsRunner.TsNode
}
}

switch (tsRunner) {
case TsRunner.Node: {
Comment thread
nwidynski marked this conversation as resolved.
if (NODE_VERSION < STRIP_TYPES_SUPPORTED_NODE_VERSION) {
throw new Error(
'type stripping is not supported in this node version',
)
}
execArgv =

Check warning on line 239 in src/index.ts

View check run for this annotation

Codecov / codecov/patch

src/index.ts#L239

Added line #L239 was not covered by tests
NODE_VERSION >= STRIP_TYPES_DEFAULT_NODE_VERSION
? execArgv.filter(arg => arg !== NO_STRIP_TYPES_FLAG)
: [STRIP_TYPES_FLAG, ...execArgv]
break

Check warning on line 243 in src/index.ts

View check run for this annotation

Codecov / codecov/patch

src/index.ts#L241-L243

Added lines #L241 - L243 were not covered by tests
}
case TsRunner.TsNode: {
if (tsUseEsm) {
if (!execArgv.includes('--loader')) {
Expand Down Expand Up @@ -283,7 +315,7 @@
// https://github.com/un-ts/synckit/issues/123
resolvedPnpLoaderPath = pathToFileURL(pnpLoaderPath).toString()

if (!IS_NODE_20) {
if (NODE_VERSION < LOADER_SUPPORTED_NODE_VERSION) {
execArgv = [
'--experimental-loader',
resolvedPnpLoaderPath,
Expand Down Expand Up @@ -452,8 +484,7 @@

if (/\.[cm]ts$/.test(finalWorkerPath)) {
const isTsxSupported =
!tsUseEsm ||
Number.parseFloat(process.versions.node) >= MTS_SUPPORTED_NODE_VERSION
!tsUseEsm || NODE_VERSION >= MTS_SUPPORTED_NODE_VERSION
/* istanbul ignore if */
if (!finalTsRunner) {
throw new Error('No ts runner specified, ts worker path is not supported')
Expand Down Expand Up @@ -604,7 +635,7 @@

const { workerPort, sharedBuffer, pnpLoaderPath } = workerData as WorkerData

if (pnpLoaderPath && IS_NODE_20) {
if (pnpLoaderPath && NODE_VERSION >= LOADER_SUPPORTED_NODE_VERSION) {
module.register(pnpLoaderPath)
}

Expand Down
45 changes: 44 additions & 1 deletion test/ts-runner.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@ import { jest } from '@jest/globals'
import { _dirname, nodeVersion, tsUseEsmSupported } from './helpers.js'
import type { AsyncWorkerFn } from './types.js'

import { MTS_SUPPORTED_NODE_VERSION, TsRunner } from 'synckit'
import {
MTS_SUPPORTED_NODE_VERSION,
STRIP_TYPES_DEFAULT_NODE_VERSION,
STRIP_TYPES_SUPPORTED_NODE_VERSION,
TsRunner,
} from 'synckit'

beforeEach(() => {
jest.resetModules()
Expand Down Expand Up @@ -130,6 +135,44 @@ test(TsRunner.TSX, async () => {
expect(syncFn(5)).toBe(5)
})

test(TsRunner.Node, async () => {
const { createSyncFn } = await import('synckit')

if (nodeVersion < STRIP_TYPES_SUPPORTED_NODE_VERSION) {
// eslint-disable-next-line jest/no-conditional-expect
expect(() =>
createSyncFn<AsyncWorkerFn>(workerMtsPath, {
tsRunner: TsRunner.Node,
}),
).toThrow('type stripping is not supported in this node version')
return
}

let syncFn = createSyncFn<AsyncWorkerFn>(workerJsPath, {
tsRunner:
nodeVersion >= STRIP_TYPES_DEFAULT_NODE_VERSION
? undefined
: TsRunner.Node,
})
expect(syncFn(1)).toBe(1)
expect(syncFn(2)).toBe(2)
expect(syncFn(5)).toBe(5)

if (!tsUseEsmSupported) {
return
}

syncFn = createSyncFn<AsyncWorkerFn>(workerMtsPath, {
tsRunner:
nodeVersion >= STRIP_TYPES_DEFAULT_NODE_VERSION
? undefined
: TsRunner.Node,
})
expect(syncFn(1)).toBe(1)
expect(syncFn(2)).toBe(2)
expect(syncFn(5)).toBe(5)
})

test('unknown ts runner', async () => {
const { createSyncFn } = await import('synckit')

Expand Down
Loading