Browse Source

Add TS types

pull/1197/head
Vitaly Puzrin 2 months ago
parent
commit
3bc8e2dc52
  1. 1
      CHANGELOG.md
  2. 5
      docs/migration/15.0.md
  3. 1594
      package-lock.json
  4. 26
      package.json
  5. 18
      src/index.ts
  6. 2
      src/markdownit.ts
  7. 2
      src/renderer.ts
  8. 86
      support/build-dist.mjs
  9. 10
      test/build/types.cts
  10. 6
      test/build/types.mts

1
CHANGELOG.md

@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Exposed parser internals classes as static properties on `markdownit`.
- Bundled TypeScript declarations. Remove `@types/markdown-it` if you used it.
- Added the `markdown-it/browser` export with bundled ESM and UMD builds.
- Added colored CLI help on supported terminals via `argparse` 3.
- Added reference labels to link/image tokens, #938.

5
docs/migration/15.0.md

@ -26,6 +26,11 @@ const md = new MarkdownIt({ linkify: true })
md.linkify.set({ fuzzyLink: true })
```
### TypeScript types
Types are now bundled. Remove `@types/markdown-it` if you used it.
### Browser builds
Browser exports and bundle paths have changed. See

1594
package-lock.json

File diff suppressed because it is too large

26
package.json

@ -23,14 +23,27 @@
],
"main": "./dist/markdown-it.cjs.js",
"module": "./dist/markdown-it.mjs",
"types": "./dist/markdown-it.d.cts",
"exports": {
".": {
"import": "./dist/markdown-it.mjs",
"require": "./dist/markdown-it.cjs.js"
"import": {
"types": "./dist/markdown-it.d.mts",
"default": "./dist/markdown-it.mjs"
},
"require": {
"types": "./dist/markdown-it.d.cts",
"default": "./dist/markdown-it.cjs.js"
}
},
"./browser": {
"import": "./dist/browser/markdown-it.esm.min.mjs",
"require": "./dist/browser/markdown-it.umd.min.js",
"import": {
"types": "./dist/markdown-it.d.mts",
"default": "./dist/browser/markdown-it.esm.min.mjs"
},
"require": {
"types": "./dist/markdown-it.d.cts",
"default": "./dist/browser/markdown-it.umd.min.js"
},
"default": "./dist/browser/markdown-it.umd.min.js"
},
"./package.json": "./package.json"
@ -40,14 +53,14 @@
},
"scripts": {
"lint": "eslint .",
"type-check": "tsc --noEmit",
"type-check": "tsc --noEmit && tsc --noEmit --strict --skipLibCheck --ignoreConfig --module node20 --moduleResolution node16 test/build/*.[cm]ts",
"test": "npm run lint && npm run build && npm run type-check && npm run test:cmspec && npm run test:markdown-it && npm run test:build",
"test:cmspec": "node --test --test-reporter=dot test/cmspec/**/*.test.mjs",
"test:markdown-it": "node --test test/markdown-it/**/*.test.mjs",
"test:build": "node --test test/build/**/*.test.mjs",
"pathological:track-ref": "node support/track-ref-pathological.mjs",
"pathological:update-hash": "node support/track-ref-pathological.mjs --update",
"coverage": "npm run build && c8 --include 'src/**' -r text -r html -r lcov node --test --test-reporter=dot",
"coverage": "npm run build && c8 --include 'src/**' -r text -r html -r lcov node --test --test-reporter=dot test/**/*.test.mjs",
"doc": "typedoc",
"doc:publish": "npm run doc && gh-pages -d apidoc -f",
"demo": "npm run lint && node support/build_demo.mjs",
@ -87,6 +100,7 @@
"markdown-it-sub": "^2.0.0",
"markdown-it-sup": "^2.0.0",
"neostandard": "^0.13.0",
"rolldown-plugin-dts": "^0.27.14",
"tinybench": "^6.0.2",
"typedoc": "^0.28.19",
"typedoc-plugin-missing-exports": "^4.1.4",

18
src/index.ts

@ -1,5 +1,5 @@
import { callable } from './common/utils.ts'
import MarkdownItClass from './markdownit.ts'
import MarkdownIt from './markdownit.ts'
/**
* Default package export.
@ -11,6 +11,18 @@ import MarkdownItClass from './markdownit.ts'
*
* @category Main
*/
const MarkdownIt = callable(MarkdownItClass)
const MarkdownItCallable = callable(MarkdownIt)
export default MarkdownIt
export default MarkdownItCallable
export type { default as MarkdownIt, MarkdownItPreset } from './markdownit.ts'
export type { Delimiter, Env, MarkdownItOptions } from './types.ts'
export type { default as Token } from './token.ts'
export type { default as Ruler } from './ruler.ts'
export type { default as Renderer, RendererRule } from './renderer.ts'
export type { default as ParserCore } from './parser_core.ts'
export type { default as StateCore } from './rules_core/state_core.ts'
export type { default as ParserBlock } from './parser_block.ts'
export type { default as StateBlock } from './rules_block/state_block.ts'
export type { default as ParserInline } from './parser_inline.ts'
export type { default as StateInline } from './rules_inline/state_inline.ts'

2
src/markdownit.ts

@ -31,7 +31,7 @@ type MarkdownItPresetName = keyof typeof config
/**
* Parser preset containing options and enabled rules for each parser component.
*/
interface MarkdownItPreset {
export interface MarkdownItPreset {
options?: Required<MarkdownItOptions>
components?: {
core?: {

2
src/renderer.ts

@ -3,7 +3,7 @@ import type Token from './token.ts'
import type { Env, MarkdownItOptions } from './types.ts'
/** Function that renders a token at a given position in a token stream. */
type RendererRule = (
export type RendererRule = (
tokens: Token[],
idx: number,
options: Required<MarkdownItOptions>,

86
support/build-dist.mjs

@ -1,5 +1,8 @@
import { rm } from 'node:fs/promises'
import { readFile, rm, writeFile } from 'node:fs/promises'
import { createRequire } from 'node:module'
import { rolldown } from 'rolldown'
import { dts } from 'rolldown-plugin-dts'
import ts from 'typescript'
import { build } from 'vite'
const require = createRequire(import.meta.url)
@ -103,3 +106,84 @@ await build({
}
}
})
// ESM types, bundled into a single .d.mts
const dtsBundle = await rolldown({
input: 'src/index.ts',
external: Object.keys(pkg.dependencies),
checks: { pluginTimings: false },
plugins: dts({
emitDtsOnly: true,
generator: 'tsc'
})
})
try {
await dtsBundle.write({
dir: 'dist',
format: 'es',
codeSplitting: false,
entryFileNames: chunk => chunk.name.endsWith('.d')
? 'markdown-it.d.mts'
: 'types-empty.js'
})
} finally {
await dtsBundle.close()
}
// CJS types: a wrapper with `export =`, to match `module.exports = MarkdownIt`.
// Named exports are not allowed next to `export =`, so exported types are moved
// into a namespace with the same name, merged into the exported value.
const dtsSource = ts.createSourceFile(
'markdown-it.d.mts',
await readFile('dist/markdown-it.d.mts', 'utf8'),
ts.ScriptTarget.Latest,
true
)
const typeParams = new Map()
for (const node of dtsSource.statements) {
if (ts.isClassDeclaration(node) || ts.isInterfaceDeclaration(node) || ts.isTypeAliasDeclaration(node)) {
typeParams.set(node.name.text, node.typeParameters ?? [])
}
}
const specifiers = dtsSource.statements
.filter(node => ts.isExportDeclaration(node) && node.exportClause && ts.isNamedExports(node.exportClause))
.flatMap(node => [...node.exportClause.elements])
const members = []
let hasDefault = false
for (const spec of specifiers) {
const exported = spec.name.text
const local = (spec.propertyName ?? spec.name).text
if (exported === 'default') {
hasDefault = true
continue
}
if (!spec.isTypeOnly) {
throw new Error(`Value export "${exported}" can not be re-exposed in CJS declarations`)
}
// Generic types must be re-declared with their parameters
const params = typeParams.get(local) ?? []
const declared = params.length ? `<${params.map(p => p.getText()).join(', ')}>` : ''
const used = params.length ? `<${params.map(p => p.name.text).join(', ')}>` : ''
members.push(` type ${exported}${declared} = import('./markdown-it.mjs').${exported}${used}`)
}
if (!hasDefault) throw new Error('No default export found in generated declarations')
await writeFile('dist/markdown-it.d.cts', [
'declare namespace MarkdownIt {',
...members,
'}',
"declare const MarkdownIt: typeof import('./markdown-it.mjs').default",
'export = MarkdownIt',
''
].join('\n'))

10
test/build/types.cts

@ -0,0 +1,10 @@
// Types are checked as a package consumer would see them, via `exports` map.
import md = require('markdown-it')
const parser = new md({ html: true })
const legacy = md('commonmark')
const tokens: md.Token[] = parser.parse('abc', {})
const Token: typeof md.Token = md.Token
const ruler: md.Ruler<[md.StateCore], void> = parser.core.ruler
export { parser, legacy, tokens, Token, ruler }

6
test/build/types.mts

@ -0,0 +1,6 @@
import MarkdownIt, { type Token } from 'markdown-it'
const parser = new MarkdownIt({ html: true })
const tokens: Token[] = parser.parse('abc', {})
export { parser, tokens }
Loading…
Cancel
Save