Skip to content

Commit

Permalink
feat: introduce warning system for deprecated API (#788)
Browse files Browse the repository at this point in the history
  • Loading branch information
antfu authored Sep 26, 2024
1 parent 85a4ff9 commit 4e59b65
Show file tree
Hide file tree
Showing 20 changed files with 171 additions and 35 deletions.
1 change: 1 addition & 0 deletions docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const GUIDES: DefaultTheme.NavItemWithLink[] = [
{ text: 'Synchronous Usage', link: '/guide/sync-usage' },
{ text: 'Custom Themes', link: '/guide/load-theme' },
{ text: 'Custom Languages', link: '/guide/load-lang' },
{ text: 'Future', link: '/guide/future' },
{ text: 'Migration', link: '/guide/migrate' },
{ text: 'Compatibility Build', link: '/guide/compat' },
]
Expand Down
53 changes: 53 additions & 0 deletions docs/guide/future.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Future

We planned to remove some deprecated APIs and optimize the tree-shaking in the future major versions.

The plan is:

- 👉 `v1.x`: Deprecated APIs are still supported, marked on type level only. With optional runtime warnings to opt-in.
- `v2.0`: No breaking changes, but enable runtime deprecated warnings by default.
- `v3.0`: Remove deprecated APIs, breaking changes.

At the current version, since v1.19.0, you can opt-in to the runtime warnings by calling `enableDeprecationWarnings()` at the beginning of your application.

```ts
import { enableDeprecationWarnings, getHighlighter } from 'shiki'

enableDeprecationWarnings() // [!code hl]

// Then calling deprecated usages like below would warn:
// [SHIKI DEPRECATED]: Use `createHighlighter` instead
const shiki = await getHighlighter(/* ... */)
```

This would help you better prepare for the future changes and upgrade smoothly.

## Notable Deprecations

### `getHighlighter` -> `createHighlighter`

There is no functional changes, but more like correcting the naming to avoid confusion. It should be a straightforward find-and-replace.

### WASM Related APIs

Since the introduce of the [engine system](/guide/regex-engines) in v0.16, the WebAssembly related dependencies are no longer a hard requirement. To make tree-shaking easier and decoupled the engines with the core, two packages are extracted `@shikijs/engine-oniguruma` and `@shikijs/engine-javascript`. They are also re-exported from the main package's `shiki/engine/oniguruma` and `shiki/engine/javascript` respectively.

You might need to change your import path:

```ts
import { loadWasm } from 'shiki' // [!code --]
import { loadWasm } from 'shiki/engine/oniguruma' // [!code ++]
```
`loadWasm` field in `getHighlighter` is replaced with `engine` field:
```ts
import { createHighlighter } from 'shiki'
import { createWasmOnigEngine } from 'shiki/engine/oniguruma' // [!code ++]
const shiki = await createHighlighter({
// ...
loadWasm: () => import('shiki/wasm'), // [!code --]
engine: createWasmOnigEngine(() => import('shiki/wasm')), // [!code ++]
})
```
2 changes: 1 addition & 1 deletion docs/packages/twoslash.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ Luckily, Twoslash implemented a virtual file system, which allow you to provide
We make tiny wrappers around the building blocks and provide an easy-to-use API in [`twoslash-cdn`](https://twoslash.netlify.app/packages/cdn). For example:

```js
// TODO: Replace with explicit versions in production
// FIXME: Replace with explicit versions in production
import { createTransformerFactory, rendererRich } from 'https://esm.sh/@shikijs/twoslash@latest/core'
import { codeToHtml } from 'https://esm.sh/shiki@latest'
import { createTwoslashFromCDN } from 'https://esm.sh/twoslash-cdn@latest'
Expand Down
23 changes: 23 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,27 @@ export default antfu(
'ts/explicit-function-return-type': 'off',
},
},
{
files: [
'packages/shiki/**/*.ts',
'packages/core/**/*.ts',
'packages/engine-javascript/**/*.ts',
'packages/engine-oniguruma/**/*.ts',
],
ignores: [
'**/*.test.ts',
],
rules: {
'no-restricted-imports': [
'error',
{
paths: [
{
name: 'shiki',
},
],
},
],
},
},
)
3 changes: 2 additions & 1 deletion packages/core/src/constructors/bundle-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { ShikiError } from '@shikijs/types'

import { createWasmOnigEngine } from '../engines/oniguruma'
import { isSpecialLang, isSpecialTheme } from '../utils'
import { warnDeprecated } from '../warn'
import { createHighlighterCore } from './highlighter'

/**
Expand Down Expand Up @@ -74,7 +75,7 @@ export function createdBundledHighlighter<BundledLangs extends string, BundledTh
let engine: () => Awaitable<RegexEngine>

if (arg2) {
// TODO: next: console.warn('`createdBundledHighlighter` signature with `bundledLanguages` and `bundledThemes` is deprecated. Use the options object signature instead.')
warnDeprecated('`createdBundledHighlighter` signature with `bundledLanguages` and `bundledThemes` is deprecated. Use the options object signature instead.')
bundledLanguages = arg1 as Record<BundledLangs, LanguageInput>
bundledThemes = arg2
engine = () => createWasmOnigEngine(arg3)
Expand Down
5 changes: 3 additions & 2 deletions packages/core/src/constructors/highlighter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import { codeToHtml } from '../highlight/code-to-html'
import { codeToTokens } from '../highlight/code-to-tokens'
import { codeToTokensBase, getLastGrammarState } from '../highlight/code-to-tokens-base'
import { codeToTokensWithThemes } from '../highlight/code-to-tokens-themes'
import { createShikiInternal } from './internal'
import { warnDeprecated } from '../warn'

import { createShikiInternal } from './internal'
import { createShikiInternalSync } from './internal-sync'

/**
Expand Down Expand Up @@ -89,6 +90,6 @@ export const getSingletonHighlighterCore = /* @__PURE__ */ makeSingletonHighligh
*/
/* v8 ignore next 5 */
export function getHighlighterCore(options: HighlighterCoreOptions = {}): Promise<HighlighterCore> {
// TODO: next: console.warn('`getHighlighterCore` is deprecated. Use `createHighlighterCore` or `getSingletonHighlighterCore` instead.')
warnDeprecated('`getHighlighterCore` is deprecated. Use `createHighlighterCore` or `getSingletonHighlighterCore` instead.')
return createHighlighterCore(options)
}
10 changes: 7 additions & 3 deletions packages/core/src/constructors/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,20 @@ import type {
HighlighterCoreOptions,
ShikiInternal,
} from '@shikijs/types'
import { createWasmOnigEngine } from '@shikijs/engine-oniguruma'
import { createWasmOnigEngine, getDefaultWasmLoader } from '@shikijs/engine-oniguruma'

import { getDefaultWasmLoader } from '../engines/oniguruma'
import { resolveLangs, resolveThemes } from '../textmate/getters-resolve'
import { warnDeprecated } from '../warn'
import { createShikiInternalSync } from './internal-sync'

/**
* Get the minimal shiki context for rendering.
*/
export async function createShikiInternal(options: HighlighterCoreOptions = {}): Promise<ShikiInternal> {
if (options.loadWasm) {
warnDeprecated('`loadWasm` option is deprecated. Use `engine: createWasmOnigEngine(loadWasm)` instead.')
}

const [
themes,
langs,
Expand All @@ -35,6 +39,6 @@ export async function createShikiInternal(options: HighlighterCoreOptions = {}):
* @deprecated Use `createShikiInternal` instead.
*/
export function getShikiInternal(options: HighlighterCoreOptions = {}): Promise<ShikiInternal> {
// TODO: next: console.warn('`getShikiInternal` is deprecated. Use `createShikiInternal` instead.')
warnDeprecated('`getShikiInternal` is deprecated. Use `createShikiInternal` instead.')
return createShikiInternal(options)
}
12 changes: 11 additions & 1 deletion packages/core/src/engines/javascript.ts
Original file line number Diff line number Diff line change
@@ -1 +1,11 @@
export * from '@shikijs/engine-javascript'
import type { JavaScriptRegexEngineOptions } from '@shikijs/engine-javascript'
import type { RegexEngine } from '@shikijs/types'
import { createJavaScriptRegexEngine as _createJavaScriptRegexEngine, defaultJavaScriptRegexConstructor } from '@shikijs/engine-javascript'
import { warnDeprecated } from '../warn'

export function createJavaScriptRegexEngine(options?: JavaScriptRegexEngineOptions): RegexEngine {
warnDeprecated('import `createJavaScriptRegexEngine` from `@shikijs/engine-javascript` or `shiki/engine/javascript` instead')
return _createJavaScriptRegexEngine(options)
}

export { defaultJavaScriptRegexConstructor }
28 changes: 12 additions & 16 deletions packages/core/src/engines/oniguruma.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,16 @@
import type { LoadWasmOptions } from '@shikijs/types'
import type { LoadWasmOptions, RegexEngine } from '@shikijs/types'
import {
createWasmOnigEngine as _createWasmOnigEngine,
loadWasm as _loadWasm,
} from '@shikijs/engine-oniguruma'
import { warnDeprecated } from '../warn'

export * from '@shikijs/engine-oniguruma'

let _defaultWasmLoader: LoadWasmOptions | undefined

/**
* Set the default wasm loader for `loadWasm`.
* @internal
*/
export function setDefaultWasmLoader(_loader: LoadWasmOptions): void {
_defaultWasmLoader = _loader
export function createWasmOnigEngine(options?: LoadWasmOptions | null): Promise<RegexEngine> {
warnDeprecated('import `createWasmOnigEngine` from `@shikijs/engine-oniguruma` or `shiki/engine/oniguruma` instead')
return _createWasmOnigEngine(options)
}

/**
* @internal
*/
export function getDefaultWasmLoader(): LoadWasmOptions | undefined {
return _defaultWasmLoader
export function loadWasm(options: LoadWasmOptions): Promise<void> {
warnDeprecated('import `loadWasm` from `@shikijs/engine-oniguruma` or `shiki/engine/oniguruma` instead')
return _loadWasm(options)
}
5 changes: 4 additions & 1 deletion packages/core/src/highlight/code-to-hast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import type {
import { FontStyle } from '@shikijs/vscode-textmate'

import { addClassToHast, getTokenStyleObject, stringifyTokenStyle } from '../utils'
import { warnDeprecated } from '../warn'
import { getTransformers } from './_get-transformers'
import { codeToTokens } from './code-to-tokens'

Expand Down Expand Up @@ -177,7 +178,9 @@ export function tokensToHast(
children: [{ type: 'text', value: token.content }],
}

// TODO: Shiki2: Deprecate `string` type of `htmlStyle`
if (typeof token.htmlStyle === 'string')
warnDeprecated('`htmlStyle` as a string is deprecated. Use an object instead.')

const style = stringifyTokenStyle(token.htmlStyle || getTokenStyleObject(token))
if (style)
tokenNode.properties.style = style
Expand Down
3 changes: 2 additions & 1 deletion packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export { createShikiInternalSync } from './constructors/internal-sync'

// Engines
export { createJavaScriptRegexEngine, defaultJavaScriptRegexConstructor } from './engines/javascript'
export { createWasmOnigEngine, loadWasm, setDefaultWasmLoader } from './engines/oniguruma'
export { createWasmOnigEngine, loadWasm } from './engines/oniguruma'

// Low-level Highlighting
export { codeToHast, tokensToHast } from './highlight/code-to-hast'
Expand All @@ -22,6 +22,7 @@ export { transformerDecorations } from './transformer-decorations'

// Utils and Misc
export * from './utils'
export { enableDeprecationWarnings, warnDeprecated } from './warn'

// Types
export * from '@shikijs/types'
Expand Down
21 changes: 21 additions & 0 deletions packages/core/src/warn.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
let emitDeprecation = false

/**
* Enable runtime warning for deprecated APIs, for the future versions of Shiki.
*
* Disabled by default, will be enabled in Shiki v2.
*
* @experimental The accuracy of the warning messages is not yet guaranteed.
*/
export function enableDeprecationWarnings(value = true): void {
emitDeprecation = value
}

/**
* @internal
*/
export function warnDeprecated(message: string): void {
if (emitDeprecation)
// eslint-disable-next-line no-console
console.trace(`[SHIKI DEPRECATE]: ${message}`)
}
4 changes: 4 additions & 0 deletions packages/core/src/wasm-inlined.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
import { warnDeprecated } from './warn'

export { default } from '@shikijs/engine-oniguruma/wasm-inlined'
export * from '@shikijs/engine-oniguruma/wasm-inlined'

warnDeprecated('Import from `@shikijs/engine-oniguruma/wasm-inlined` instead')
17 changes: 17 additions & 0 deletions packages/engine-oniguruma/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,23 @@ import { loadWasm, OnigScanner, OnigString } from './oniguruma'

export { loadWasm }

let _defaultWasmLoader: LoadWasmOptions | undefined

/**
* Set the default wasm loader for `loadWasm`.
* @internal
*/
export function setDefaultWasmLoader(_loader: LoadWasmOptions): void {
_defaultWasmLoader = _loader
}

/**
* @internal
*/
export function getDefaultWasmLoader(): LoadWasmOptions | undefined {
return _defaultWasmLoader
}

export async function createWasmOnigEngine(options?: LoadWasmOptions | null): Promise<RegexEngine> {
if (options)
await loadWasm(options)
Expand Down
4 changes: 2 additions & 2 deletions packages/shiki/src/bundle-full.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type {} from 'hast'
import type { BundledLanguage } from './assets/langs-bundle-full'
import type { BundledTheme } from './themes'
import { bundledLanguages } from './assets/langs-bundle-full'
import { createdBundledHighlighter, createSingletonShorthands, createWasmOnigEngine } from './core'
import { createdBundledHighlighter, createSingletonShorthands, createWasmOnigEngine, warnDeprecated } from './core'
import { bundledThemes } from './themes'
import { getWasmInlined } from './wasm-dynamic'

Expand Down Expand Up @@ -53,6 +53,6 @@ export const {
* @deprecated Use `createHighlighter` or `getSingletonHighlighter` instead.
*/
export const getHighlighter: CreateHighlighterFactory<BundledLanguage, BundledTheme> = /* @__PURE__ */ (options) => {
// TODO: next: console.warn('`getHighlighter` is deprecated. Use `createHighlighter` or `getSingletonHighlighter` instead.')
warnDeprecated('`getHighlighter` is deprecated. Use `createHighlighter` or `getSingletonHighlighter` instead.')
return createHighlighter(options)
}
4 changes: 2 additions & 2 deletions packages/shiki/src/bundle-web.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import type {} from 'hast'
import type { BundledLanguage } from './assets/langs-bundle-web'
import type { BundledTheme } from './themes'
import { bundledLanguages } from './assets/langs-bundle-web'
import { createdBundledHighlighter, createSingletonShorthands, createWasmOnigEngine } from './core'

import { createdBundledHighlighter, createSingletonShorthands, createWasmOnigEngine, warnDeprecated } from './core'
import { bundledThemes } from './themes'
import { getWasmInlined } from './wasm-dynamic'

Expand Down Expand Up @@ -55,6 +55,6 @@ export const {
* @deprecated Use `createHighlighter` or `getSingletonHighlighter` instead.
*/
export const getHighlighter: CreateHighlighterFactory<BundledLanguage, BundledTheme> = /* @__PURE__ */ (options) => {
// TODO: next: console.warn('`getHighlighter` is deprecated. Use `createHighlighter` or `getSingletonHighlighter` instead.')
warnDeprecated('`getHighlighter` is deprecated. Use `createHighlighter` or `getSingletonHighlighter` instead.')
return createHighlighter(options)
}
2 changes: 1 addition & 1 deletion packages/shiki/src/core-unwasm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* We add the wasm file as the dependency so users don't need to call `loadWasm` manually.
*/

import { setDefaultWasmLoader } from '@shikijs/core'
import { setDefaultWasmLoader } from '@shikijs/engine-oniguruma'

setDefaultWasmLoader(() => import('shiki/wasm'))

Expand Down
4 changes: 2 additions & 2 deletions packages/shiki/src/wasm.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export { default } from '@shikijs/core/wasm-inlined'
export * from '@shikijs/core/wasm-inlined'
export { default } from '@shikijs/engine-oniguruma/wasm-inlined'
export * from '@shikijs/engine-oniguruma/wasm-inlined'
2 changes: 1 addition & 1 deletion packages/shiki/test/cf.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { LanguageRegistration } from 'shiki'
import type { LanguageRegistration } from '@shikijs/types'
import { createHighlighterCore, loadWasm } from 'shiki/core'

import js from 'shiki/langs/javascript.mjs'
Expand Down
3 changes: 2 additions & 1 deletion packages/types/src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,10 @@ export interface HighlighterCoreOptions<Sync extends boolean = false> {
*/
warnings?: boolean

// TODO: Deprecate this option after docs for engines are updated.
/**
* Load wasm file from a custom path or using a custom function.
*
* @deprecated Use `engine: createWasmOnigEngine(loadWasm)` instead.
*/
loadWasm?: Sync extends true ? never : LoadWasmOptions
}
Expand Down

0 comments on commit 4e59b65

Please sign in to comment.