index.js 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323
  1. /**
  2. * @typedef {import('trough').Pipeline} Pipeline
  3. *
  4. * @typedef {import('unist').Node} Node
  5. *
  6. * @typedef {import('vfile').Compatible} Compatible
  7. * @typedef {import('vfile').Value} Value
  8. *
  9. * @typedef {import('../index.js').CompileResultMap} CompileResultMap
  10. * @typedef {import('../index.js').Data} Data
  11. * @typedef {import('../index.js').Settings} Settings
  12. */
  13. /**
  14. * @typedef {CompileResultMap[keyof CompileResultMap]} CompileResults
  15. * Acceptable results from compilers.
  16. *
  17. * To register custom results, add them to
  18. * {@link CompileResultMap `CompileResultMap`}.
  19. */
  20. /**
  21. * @template {Node} [Tree=Node]
  22. * The node that the compiler receives (default: `Node`).
  23. * @template {CompileResults} [Result=CompileResults]
  24. * The thing that the compiler yields (default: `CompileResults`).
  25. * @callback Compiler
  26. * A **compiler** handles the compiling of a syntax tree to something else
  27. * (in most cases, text) (TypeScript type).
  28. *
  29. * It is used in the stringify phase and called with a {@link Node `Node`}
  30. * and {@link VFile `VFile`} representation of the document to compile.
  31. * It should return the textual representation of the given tree (typically
  32. * `string`).
  33. *
  34. * > 👉 **Note**: unified typically compiles by serializing: most compilers
  35. * > return `string` (or `Uint8Array`).
  36. * > Some compilers, such as the one configured with
  37. * > [`rehype-react`][rehype-react], return other values (in this case, a
  38. * > React tree).
  39. * > If you’re using a compiler that doesn’t serialize, expect different
  40. * > result values.
  41. * >
  42. * > To register custom results in TypeScript, add them to
  43. * > {@link CompileResultMap `CompileResultMap`}.
  44. *
  45. * [rehype-react]: https://github.com/rehypejs/rehype-react
  46. * @param {Tree} tree
  47. * Tree to compile.
  48. * @param {VFile} file
  49. * File associated with `tree`.
  50. * @returns {Result}
  51. * New content: compiled text (`string` or `Uint8Array`, for `file.value`) or
  52. * something else (for `file.result`).
  53. */
  54. /**
  55. * @template {Node} [Tree=Node]
  56. * The node that the parser yields (default: `Node`)
  57. * @callback Parser
  58. * A **parser** handles the parsing of text to a syntax tree.
  59. *
  60. * It is used in the parse phase and is called with a `string` and
  61. * {@link VFile `VFile`} of the document to parse.
  62. * It must return the syntax tree representation of the given file
  63. * ({@link Node `Node`}).
  64. * @param {string} document
  65. * Document to parse.
  66. * @param {VFile} file
  67. * File associated with `document`.
  68. * @returns {Tree}
  69. * Node representing the given file.
  70. */
  71. /**
  72. * @typedef {(
  73. * Plugin<Array<any>, any, any> |
  74. * PluginTuple<Array<any>, any, any> |
  75. * Preset
  76. * )} Pluggable
  77. * Union of the different ways to add plugins and settings.
  78. */
  79. /**
  80. * @typedef {Array<Pluggable>} PluggableList
  81. * List of plugins and presets.
  82. */
  83. // Note: we can’t use `callback` yet as it messes up `this`:
  84. // <https://github.com/microsoft/TypeScript/issues/55197>.
  85. /**
  86. * @template {Array<unknown>} [PluginParameters=[]]
  87. * Arguments passed to the plugin (default: `[]`, the empty tuple).
  88. * @template {Node | string | undefined} [Input=Node]
  89. * Value that is expected as input (default: `Node`).
  90. *
  91. * * If the plugin returns a {@link Transformer `Transformer`}, this
  92. * should be the node it expects.
  93. * * If the plugin sets a {@link Parser `Parser`}, this should be
  94. * `string`.
  95. * * If the plugin sets a {@link Compiler `Compiler`}, this should be the
  96. * node it expects.
  97. * @template [Output=Input]
  98. * Value that is yielded as output (default: `Input`).
  99. *
  100. * * If the plugin returns a {@link Transformer `Transformer`}, this
  101. * should be the node that that yields.
  102. * * If the plugin sets a {@link Parser `Parser`}, this should be the
  103. * node that it yields.
  104. * * If the plugin sets a {@link Compiler `Compiler`}, this should be
  105. * result it yields.
  106. * @typedef {(
  107. * (this: Processor, ...parameters: PluginParameters) =>
  108. * Input extends string ? // Parser.
  109. * Output extends Node | undefined ? undefined | void : never :
  110. * Output extends CompileResults ? // Compiler.
  111. * Input extends Node | undefined ? undefined | void : never :
  112. * Transformer<
  113. * Input extends Node ? Input : Node,
  114. * Output extends Node ? Output : Node
  115. * > | undefined | void
  116. * )} Plugin
  117. * Single plugin.
  118. *
  119. * Plugins configure the processors they are applied on in the following
  120. * ways:
  121. *
  122. * * they change the processor, such as the parser, the compiler, or by
  123. * configuring data
  124. * * they specify how to handle trees and files
  125. *
  126. * In practice, they are functions that can receive options and configure the
  127. * processor (`this`).
  128. *
  129. * > 👉 **Note**: plugins are called when the processor is *frozen*, not when
  130. * > they are applied.
  131. */
  132. /**
  133. * Tuple of a plugin and its configuration.
  134. *
  135. * The first item is a plugin, the rest are its parameters.
  136. *
  137. * @template {Array<unknown>} [TupleParameters=[]]
  138. * Arguments passed to the plugin (default: `[]`, the empty tuple).
  139. * @template {Node | string | undefined} [Input=undefined]
  140. * Value that is expected as input (optional).
  141. *
  142. * * If the plugin returns a {@link Transformer `Transformer`}, this
  143. * should be the node it expects.
  144. * * If the plugin sets a {@link Parser `Parser`}, this should be
  145. * `string`.
  146. * * If the plugin sets a {@link Compiler `Compiler`}, this should be the
  147. * node it expects.
  148. * @template [Output=undefined] (optional).
  149. * Value that is yielded as output.
  150. *
  151. * * If the plugin returns a {@link Transformer `Transformer`}, this
  152. * should be the node that that yields.
  153. * * If the plugin sets a {@link Parser `Parser`}, this should be the
  154. * node that it yields.
  155. * * If the plugin sets a {@link Compiler `Compiler`}, this should be
  156. * result it yields.
  157. * @typedef {(
  158. * [
  159. * plugin: Plugin<TupleParameters, Input, Output>,
  160. * ...parameters: TupleParameters
  161. * ]
  162. * )} PluginTuple
  163. */
  164. /**
  165. * @typedef Preset
  166. * Sharable configuration.
  167. *
  168. * They can contain plugins and settings.
  169. * @property {PluggableList | undefined} [plugins]
  170. * List of plugins and presets (optional).
  171. * @property {Settings | undefined} [settings]
  172. * Shared settings for parsers and compilers (optional).
  173. */
  174. /**
  175. * @template {VFile} [File=VFile]
  176. * The file that the callback receives (default: `VFile`).
  177. * @callback ProcessCallback
  178. * Callback called when the process is done.
  179. *
  180. * Called with either an error or a result.
  181. * @param {Error | undefined} [error]
  182. * Fatal error (optional).
  183. * @param {File | undefined} [file]
  184. * Processed file (optional).
  185. * @returns {undefined}
  186. * Nothing.
  187. */
  188. /**
  189. * @template {Node} [Tree=Node]
  190. * The tree that the callback receives (default: `Node`).
  191. * @callback RunCallback
  192. * Callback called when transformers are done.
  193. *
  194. * Called with either an error or results.
  195. * @param {Error | undefined} [error]
  196. * Fatal error (optional).
  197. * @param {Tree | undefined} [tree]
  198. * Transformed tree (optional).
  199. * @param {VFile | undefined} [file]
  200. * File (optional).
  201. * @returns {undefined}
  202. * Nothing.
  203. */
  204. /**
  205. * @template {Node} [Output=Node]
  206. * Node type that the transformer yields (default: `Node`).
  207. * @callback TransformCallback
  208. * Callback passed to transforms.
  209. *
  210. * If the signature of a `transformer` accepts a third argument, the
  211. * transformer may perform asynchronous operations, and must call it.
  212. * @param {Error | undefined} [error]
  213. * Fatal error to stop the process (optional).
  214. * @param {Output | undefined} [tree]
  215. * New, changed, tree (optional).
  216. * @param {VFile | undefined} [file]
  217. * New, changed, file (optional).
  218. * @returns {undefined}
  219. * Nothing.
  220. */
  221. /**
  222. * @template {Node} [Input=Node]
  223. * Node type that the transformer expects (default: `Node`).
  224. * @template {Node} [Output=Input]
  225. * Node type that the transformer yields (default: `Input`).
  226. * @callback Transformer
  227. * Transformers handle syntax trees and files.
  228. *
  229. * They are functions that are called each time a syntax tree and file are
  230. * passed through the run phase.
  231. * When an error occurs in them (either because it’s thrown, returned,
  232. * rejected, or passed to `next`), the process stops.
  233. *
  234. * The run phase is handled by [`trough`][trough], see its documentation for
  235. * the exact semantics of these functions.
  236. *
  237. * > 👉 **Note**: you should likely ignore `next`: don’t accept it.
  238. * > it supports callback-style async work.
  239. * > But promises are likely easier to reason about.
  240. *
  241. * [trough]: https://github.com/wooorm/trough#function-fninput-next
  242. * @param {Input} tree
  243. * Tree to handle.
  244. * @param {VFile} file
  245. * File to handle.
  246. * @param {TransformCallback<Output>} next
  247. * Callback.
  248. * @returns {(
  249. * Promise<Output | undefined | void> |
  250. * Promise<never> | // For some reason this is needed separately.
  251. * Output |
  252. * Error |
  253. * undefined |
  254. * void
  255. * )}
  256. * If you accept `next`, nothing.
  257. * Otherwise:
  258. *
  259. * * `Error` — fatal error to stop the process
  260. * * `Promise<undefined>` or `undefined` — the next transformer keeps using
  261. * same tree
  262. * * `Promise<Node>` or `Node` — new, changed, tree
  263. */
  264. /**
  265. * @template {Node | undefined} ParseTree
  266. * Output of `parse`.
  267. * @template {Node | undefined} HeadTree
  268. * Input for `run`.
  269. * @template {Node | undefined} TailTree
  270. * Output for `run`.
  271. * @template {Node | undefined} CompileTree
  272. * Input of `stringify`.
  273. * @template {CompileResults | undefined} CompileResult
  274. * Output of `stringify`.
  275. * @template {Node | string | undefined} Input
  276. * Input of plugin.
  277. * @template Output
  278. * Output of plugin (optional).
  279. * @typedef {(
  280. * Input extends string
  281. * ? Output extends Node | undefined
  282. * ? // Parser.
  283. * Processor<
  284. * Output extends undefined ? ParseTree : Output,
  285. * HeadTree,
  286. * TailTree,
  287. * CompileTree,
  288. * CompileResult
  289. * >
  290. * : // Unknown.
  291. * Processor<ParseTree, HeadTree, TailTree, CompileTree, CompileResult>
  292. * : Output extends CompileResults
  293. * ? Input extends Node | undefined
  294. * ? // Compiler.
  295. * Processor<
  296. * ParseTree,
  297. * HeadTree,
  298. * TailTree,
  299. * Input extends undefined ? CompileTree : Input,
  300. * Output extends undefined ? CompileResult : Output
  301. * >
  302. * : // Unknown.
  303. * Processor<ParseTree, HeadTree, TailTree, CompileTree, CompileResult>
  304. * : Input extends Node | undefined
  305. * ? Output extends Node | undefined
  306. * ? // Transform.
  307. * Processor<
  308. * ParseTree,
  309. * HeadTree extends undefined ? Input : HeadTree,
  310. * Output extends undefined ? TailTree : Output,
  311. * CompileTree,
  312. * CompileResult
  313. * >
  314. * : // Unknown.
  315. * Processor<ParseTree, HeadTree, TailTree, CompileTree, CompileResult>
  316. * : // Unknown.
  317. * Processor<ParseTree, HeadTree, TailTree, CompileTree, CompileResult>
  318. * )} UsePlugin
  319. * Create a processor based on the input/output of a {@link Plugin plugin}.
  320. */
  321. /**
  322. * @template {CompileResults | undefined} Result
  323. * Node type that the transformer yields.
  324. * @typedef {(
  325. * Result extends Value | undefined ?
  326. * VFile :
  327. * VFile & {result: Result}
  328. * )} VFileWithOutput
  329. * Type to generate a {@link VFile `VFile`} corresponding to a compiler result.
  330. *
  331. * If a result that is not acceptable on a `VFile` is used, that will
  332. * be stored on the `result` field of {@link VFile `VFile`}.
  333. */
  334. import {bail} from 'bail'
  335. import extend from 'extend'
  336. import {ok as assert} from 'devlop'
  337. import isPlainObj from 'is-plain-obj'
  338. import {trough} from 'trough'
  339. import {VFile} from 'vfile'
  340. import {CallableInstance} from './callable-instance.js'
  341. // To do: next major: drop `Compiler`, `Parser`: prefer lowercase.
  342. // To do: we could start yielding `never` in TS when a parser is missing and
  343. // `parse` is called.
  344. // Currently, we allow directly setting `processor.parser`, which is untyped.
  345. const own = {}.hasOwnProperty
  346. /**
  347. * @template {Node | undefined} [ParseTree=undefined]
  348. * Output of `parse` (optional).
  349. * @template {Node | undefined} [HeadTree=undefined]
  350. * Input for `run` (optional).
  351. * @template {Node | undefined} [TailTree=undefined]
  352. * Output for `run` (optional).
  353. * @template {Node | undefined} [CompileTree=undefined]
  354. * Input of `stringify` (optional).
  355. * @template {CompileResults | undefined} [CompileResult=undefined]
  356. * Output of `stringify` (optional).
  357. * @extends {CallableInstance<[], Processor<ParseTree, HeadTree, TailTree, CompileTree, CompileResult>>}
  358. */
  359. export class Processor extends CallableInstance {
  360. /**
  361. * Create a processor.
  362. */
  363. constructor() {
  364. // If `Processor()` is called (w/o new), `copy` is called instead.
  365. super('copy')
  366. /**
  367. * Compiler to use (deprecated).
  368. *
  369. * @deprecated
  370. * Use `compiler` instead.
  371. * @type {(
  372. * Compiler<
  373. * CompileTree extends undefined ? Node : CompileTree,
  374. * CompileResult extends undefined ? CompileResults : CompileResult
  375. * > |
  376. * undefined
  377. * )}
  378. */
  379. this.Compiler = undefined
  380. /**
  381. * Parser to use (deprecated).
  382. *
  383. * @deprecated
  384. * Use `parser` instead.
  385. * @type {(
  386. * Parser<ParseTree extends undefined ? Node : ParseTree> |
  387. * undefined
  388. * )}
  389. */
  390. this.Parser = undefined
  391. // Note: the following fields are considered private.
  392. // However, they are needed for tests, and TSC generates an untyped
  393. // `private freezeIndex` field for, which trips `type-coverage` up.
  394. // Instead, we use `@deprecated` to visualize that they shouldn’t be used.
  395. /**
  396. * Internal list of configured plugins.
  397. *
  398. * @deprecated
  399. * This is a private internal property and should not be used.
  400. * @type {Array<PluginTuple<Array<unknown>>>}
  401. */
  402. this.attachers = []
  403. /**
  404. * Compiler to use.
  405. *
  406. * @type {(
  407. * Compiler<
  408. * CompileTree extends undefined ? Node : CompileTree,
  409. * CompileResult extends undefined ? CompileResults : CompileResult
  410. * > |
  411. * undefined
  412. * )}
  413. */
  414. this.compiler = undefined
  415. /**
  416. * Internal state to track where we are while freezing.
  417. *
  418. * @deprecated
  419. * This is a private internal property and should not be used.
  420. * @type {number}
  421. */
  422. this.freezeIndex = -1
  423. /**
  424. * Internal state to track whether we’re frozen.
  425. *
  426. * @deprecated
  427. * This is a private internal property and should not be used.
  428. * @type {boolean | undefined}
  429. */
  430. this.frozen = undefined
  431. /**
  432. * Internal state.
  433. *
  434. * @deprecated
  435. * This is a private internal property and should not be used.
  436. * @type {Data}
  437. */
  438. this.namespace = {}
  439. /**
  440. * Parser to use.
  441. *
  442. * @type {(
  443. * Parser<ParseTree extends undefined ? Node : ParseTree> |
  444. * undefined
  445. * )}
  446. */
  447. this.parser = undefined
  448. /**
  449. * Internal list of configured transformers.
  450. *
  451. * @deprecated
  452. * This is a private internal property and should not be used.
  453. * @type {Pipeline}
  454. */
  455. this.transformers = trough()
  456. }
  457. /**
  458. * Copy a processor.
  459. *
  460. * @deprecated
  461. * This is a private internal method and should not be used.
  462. * @returns {Processor<ParseTree, HeadTree, TailTree, CompileTree, CompileResult>}
  463. * New *unfrozen* processor ({@link Processor `Processor`}) that is
  464. * configured to work the same as its ancestor.
  465. * When the descendant processor is configured in the future it does not
  466. * affect the ancestral processor.
  467. */
  468. copy() {
  469. // Cast as the type parameters will be the same after attaching.
  470. const destination =
  471. /** @type {Processor<ParseTree, HeadTree, TailTree, CompileTree, CompileResult>} */ (
  472. new Processor()
  473. )
  474. let index = -1
  475. while (++index < this.attachers.length) {
  476. const attacher = this.attachers[index]
  477. destination.use(...attacher)
  478. }
  479. destination.data(extend(true, {}, this.namespace))
  480. return destination
  481. }
  482. /**
  483. * Configure the processor with info available to all plugins.
  484. * Information is stored in an object.
  485. *
  486. * Typically, options can be given to a specific plugin, but sometimes it
  487. * makes sense to have information shared with several plugins.
  488. * For example, a list of HTML elements that are self-closing, which is
  489. * needed during all phases.
  490. *
  491. * > 👉 **Note**: setting information cannot occur on *frozen* processors.
  492. * > Call the processor first to create a new unfrozen processor.
  493. *
  494. * > 👉 **Note**: to register custom data in TypeScript, augment the
  495. * > {@link Data `Data`} interface.
  496. *
  497. * @example
  498. * This example show how to get and set info:
  499. *
  500. * ```js
  501. * import {unified} from 'unified'
  502. *
  503. * const processor = unified().data('alpha', 'bravo')
  504. *
  505. * processor.data('alpha') // => 'bravo'
  506. *
  507. * processor.data() // => {alpha: 'bravo'}
  508. *
  509. * processor.data({charlie: 'delta'})
  510. *
  511. * processor.data() // => {charlie: 'delta'}
  512. * ```
  513. *
  514. * @template {keyof Data} Key
  515. *
  516. * @overload
  517. * @returns {Data}
  518. *
  519. * @overload
  520. * @param {Data} dataset
  521. * @returns {Processor<ParseTree, HeadTree, TailTree, CompileTree, CompileResult>}
  522. *
  523. * @overload
  524. * @param {Key} key
  525. * @returns {Data[Key]}
  526. *
  527. * @overload
  528. * @param {Key} key
  529. * @param {Data[Key]} value
  530. * @returns {Processor<ParseTree, HeadTree, TailTree, CompileTree, CompileResult>}
  531. *
  532. * @param {Data | Key} [key]
  533. * Key to get or set, or entire dataset to set, or nothing to get the
  534. * entire dataset (optional).
  535. * @param {Data[Key]} [value]
  536. * Value to set (optional).
  537. * @returns {unknown}
  538. * The current processor when setting, the value at `key` when getting, or
  539. * the entire dataset when getting without key.
  540. */
  541. data(key, value) {
  542. if (typeof key === 'string') {
  543. // Set `key`.
  544. if (arguments.length === 2) {
  545. assertUnfrozen('data', this.frozen)
  546. this.namespace[key] = value
  547. return this
  548. }
  549. // Get `key`.
  550. return (own.call(this.namespace, key) && this.namespace[key]) || undefined
  551. }
  552. // Set space.
  553. if (key) {
  554. assertUnfrozen('data', this.frozen)
  555. this.namespace = key
  556. return this
  557. }
  558. // Get space.
  559. return this.namespace
  560. }
  561. /**
  562. * Freeze a processor.
  563. *
  564. * Frozen processors are meant to be extended and not to be configured
  565. * directly.
  566. *
  567. * When a processor is frozen it cannot be unfrozen.
  568. * New processors working the same way can be created by calling the
  569. * processor.
  570. *
  571. * It’s possible to freeze processors explicitly by calling `.freeze()`.
  572. * Processors freeze automatically when `.parse()`, `.run()`, `.runSync()`,
  573. * `.stringify()`, `.process()`, or `.processSync()` are called.
  574. *
  575. * @returns {Processor<ParseTree, HeadTree, TailTree, CompileTree, CompileResult>}
  576. * The current processor.
  577. */
  578. freeze() {
  579. if (this.frozen) {
  580. return this
  581. }
  582. // Cast so that we can type plugins easier.
  583. // Plugins are supposed to be usable on different processors, not just on
  584. // this exact processor.
  585. const self = /** @type {Processor} */ (/** @type {unknown} */ (this))
  586. while (++this.freezeIndex < this.attachers.length) {
  587. const [attacher, ...options] = this.attachers[this.freezeIndex]
  588. if (options[0] === false) {
  589. continue
  590. }
  591. if (options[0] === true) {
  592. options[0] = undefined
  593. }
  594. const transformer = attacher.call(self, ...options)
  595. if (typeof transformer === 'function') {
  596. this.transformers.use(transformer)
  597. }
  598. }
  599. this.frozen = true
  600. this.freezeIndex = Number.POSITIVE_INFINITY
  601. return this
  602. }
  603. /**
  604. * Parse text to a syntax tree.
  605. *
  606. * > 👉 **Note**: `parse` freezes the processor if not already *frozen*.
  607. *
  608. * > 👉 **Note**: `parse` performs the parse phase, not the run phase or other
  609. * > phases.
  610. *
  611. * @param {Compatible | undefined} [file]
  612. * file to parse (optional); typically `string` or `VFile`; any value
  613. * accepted as `x` in `new VFile(x)`.
  614. * @returns {ParseTree extends undefined ? Node : ParseTree}
  615. * Syntax tree representing `file`.
  616. */
  617. parse(file) {
  618. this.freeze()
  619. const realFile = vfile(file)
  620. const parser = this.parser || this.Parser
  621. assertParser('parse', parser)
  622. return parser(String(realFile), realFile)
  623. }
  624. /**
  625. * Process the given file as configured on the processor.
  626. *
  627. * > 👉 **Note**: `process` freezes the processor if not already *frozen*.
  628. *
  629. * > 👉 **Note**: `process` performs the parse, run, and stringify phases.
  630. *
  631. * @overload
  632. * @param {Compatible | undefined} file
  633. * @param {ProcessCallback<VFileWithOutput<CompileResult>>} done
  634. * @returns {undefined}
  635. *
  636. * @overload
  637. * @param {Compatible | undefined} [file]
  638. * @returns {Promise<VFileWithOutput<CompileResult>>}
  639. *
  640. * @param {Compatible | undefined} [file]
  641. * File (optional); typically `string` or `VFile`]; any value accepted as
  642. * `x` in `new VFile(x)`.
  643. * @param {ProcessCallback<VFileWithOutput<CompileResult>> | undefined} [done]
  644. * Callback (optional).
  645. * @returns {Promise<VFile> | undefined}
  646. * Nothing if `done` is given.
  647. * Otherwise a promise, rejected with a fatal error or resolved with the
  648. * processed file.
  649. *
  650. * The parsed, transformed, and compiled value is available at
  651. * `file.value` (see note).
  652. *
  653. * > 👉 **Note**: unified typically compiles by serializing: most
  654. * > compilers return `string` (or `Uint8Array`).
  655. * > Some compilers, such as the one configured with
  656. * > [`rehype-react`][rehype-react], return other values (in this case, a
  657. * > React tree).
  658. * > If you’re using a compiler that doesn’t serialize, expect different
  659. * > result values.
  660. * >
  661. * > To register custom results in TypeScript, add them to
  662. * > {@link CompileResultMap `CompileResultMap`}.
  663. *
  664. * [rehype-react]: https://github.com/rehypejs/rehype-react
  665. */
  666. process(file, done) {
  667. const self = this
  668. this.freeze()
  669. assertParser('process', this.parser || this.Parser)
  670. assertCompiler('process', this.compiler || this.Compiler)
  671. return done ? executor(undefined, done) : new Promise(executor)
  672. // Note: `void`s needed for TS.
  673. /**
  674. * @param {((file: VFileWithOutput<CompileResult>) => undefined | void) | undefined} resolve
  675. * @param {(error: Error | undefined) => undefined | void} reject
  676. * @returns {undefined}
  677. */
  678. function executor(resolve, reject) {
  679. const realFile = vfile(file)
  680. // Assume `ParseTree` (the result of the parser) matches `HeadTree` (the
  681. // input of the first transform).
  682. const parseTree =
  683. /** @type {HeadTree extends undefined ? Node : HeadTree} */ (
  684. /** @type {unknown} */ (self.parse(realFile))
  685. )
  686. self.run(parseTree, realFile, function (error, tree, file) {
  687. if (error || !tree || !file) {
  688. return realDone(error)
  689. }
  690. // Assume `TailTree` (the output of the last transform) matches
  691. // `CompileTree` (the input of the compiler).
  692. const compileTree =
  693. /** @type {CompileTree extends undefined ? Node : CompileTree} */ (
  694. /** @type {unknown} */ (tree)
  695. )
  696. const compileResult = self.stringify(compileTree, file)
  697. if (looksLikeAValue(compileResult)) {
  698. file.value = compileResult
  699. } else {
  700. file.result = compileResult
  701. }
  702. realDone(error, /** @type {VFileWithOutput<CompileResult>} */ (file))
  703. })
  704. /**
  705. * @param {Error | undefined} error
  706. * @param {VFileWithOutput<CompileResult> | undefined} [file]
  707. * @returns {undefined}
  708. */
  709. function realDone(error, file) {
  710. if (error || !file) {
  711. reject(error)
  712. } else if (resolve) {
  713. resolve(file)
  714. } else {
  715. assert(done, '`done` is defined if `resolve` is not')
  716. done(undefined, file)
  717. }
  718. }
  719. }
  720. }
  721. /**
  722. * Process the given file as configured on the processor.
  723. *
  724. * An error is thrown if asynchronous transforms are configured.
  725. *
  726. * > 👉 **Note**: `processSync` freezes the processor if not already *frozen*.
  727. *
  728. * > 👉 **Note**: `processSync` performs the parse, run, and stringify phases.
  729. *
  730. * @param {Compatible | undefined} [file]
  731. * File (optional); typically `string` or `VFile`; any value accepted as
  732. * `x` in `new VFile(x)`.
  733. * @returns {VFileWithOutput<CompileResult>}
  734. * The processed file.
  735. *
  736. * The parsed, transformed, and compiled value is available at
  737. * `file.value` (see note).
  738. *
  739. * > 👉 **Note**: unified typically compiles by serializing: most
  740. * > compilers return `string` (or `Uint8Array`).
  741. * > Some compilers, such as the one configured with
  742. * > [`rehype-react`][rehype-react], return other values (in this case, a
  743. * > React tree).
  744. * > If you’re using a compiler that doesn’t serialize, expect different
  745. * > result values.
  746. * >
  747. * > To register custom results in TypeScript, add them to
  748. * > {@link CompileResultMap `CompileResultMap`}.
  749. *
  750. * [rehype-react]: https://github.com/rehypejs/rehype-react
  751. */
  752. processSync(file) {
  753. /** @type {boolean} */
  754. let complete = false
  755. /** @type {VFileWithOutput<CompileResult> | undefined} */
  756. let result
  757. this.freeze()
  758. assertParser('processSync', this.parser || this.Parser)
  759. assertCompiler('processSync', this.compiler || this.Compiler)
  760. this.process(file, realDone)
  761. assertDone('processSync', 'process', complete)
  762. assert(result, 'we either bailed on an error or have a tree')
  763. return result
  764. /**
  765. * @type {ProcessCallback<VFileWithOutput<CompileResult>>}
  766. */
  767. function realDone(error, file) {
  768. complete = true
  769. bail(error)
  770. result = file
  771. }
  772. }
  773. /**
  774. * Run *transformers* on a syntax tree.
  775. *
  776. * > 👉 **Note**: `run` freezes the processor if not already *frozen*.
  777. *
  778. * > 👉 **Note**: `run` performs the run phase, not other phases.
  779. *
  780. * @overload
  781. * @param {HeadTree extends undefined ? Node : HeadTree} tree
  782. * @param {RunCallback<TailTree extends undefined ? Node : TailTree>} done
  783. * @returns {undefined}
  784. *
  785. * @overload
  786. * @param {HeadTree extends undefined ? Node : HeadTree} tree
  787. * @param {Compatible | undefined} file
  788. * @param {RunCallback<TailTree extends undefined ? Node : TailTree>} done
  789. * @returns {undefined}
  790. *
  791. * @overload
  792. * @param {HeadTree extends undefined ? Node : HeadTree} tree
  793. * @param {Compatible | undefined} [file]
  794. * @returns {Promise<TailTree extends undefined ? Node : TailTree>}
  795. *
  796. * @param {HeadTree extends undefined ? Node : HeadTree} tree
  797. * Tree to transform and inspect.
  798. * @param {(
  799. * RunCallback<TailTree extends undefined ? Node : TailTree> |
  800. * Compatible
  801. * )} [file]
  802. * File associated with `node` (optional); any value accepted as `x` in
  803. * `new VFile(x)`.
  804. * @param {RunCallback<TailTree extends undefined ? Node : TailTree>} [done]
  805. * Callback (optional).
  806. * @returns {Promise<TailTree extends undefined ? Node : TailTree> | undefined}
  807. * Nothing if `done` is given.
  808. * Otherwise, a promise rejected with a fatal error or resolved with the
  809. * transformed tree.
  810. */
  811. run(tree, file, done) {
  812. assertNode(tree)
  813. this.freeze()
  814. const transformers = this.transformers
  815. if (!done && typeof file === 'function') {
  816. done = file
  817. file = undefined
  818. }
  819. return done ? executor(undefined, done) : new Promise(executor)
  820. // Note: `void`s needed for TS.
  821. /**
  822. * @param {(
  823. * ((tree: TailTree extends undefined ? Node : TailTree) => undefined | void) |
  824. * undefined
  825. * )} resolve
  826. * @param {(error: Error) => undefined | void} reject
  827. * @returns {undefined}
  828. */
  829. function executor(resolve, reject) {
  830. assert(
  831. typeof file !== 'function',
  832. '`file` can’t be a `done` anymore, we checked'
  833. )
  834. const realFile = vfile(file)
  835. transformers.run(tree, realFile, realDone)
  836. /**
  837. * @param {Error | undefined} error
  838. * @param {Node} outputTree
  839. * @param {VFile} file
  840. * @returns {undefined}
  841. */
  842. function realDone(error, outputTree, file) {
  843. const resultingTree =
  844. /** @type {TailTree extends undefined ? Node : TailTree} */ (
  845. outputTree || tree
  846. )
  847. if (error) {
  848. reject(error)
  849. } else if (resolve) {
  850. resolve(resultingTree)
  851. } else {
  852. assert(done, '`done` is defined if `resolve` is not')
  853. done(undefined, resultingTree, file)
  854. }
  855. }
  856. }
  857. }
  858. /**
  859. * Run *transformers* on a syntax tree.
  860. *
  861. * An error is thrown if asynchronous transforms are configured.
  862. *
  863. * > 👉 **Note**: `runSync` freezes the processor if not already *frozen*.
  864. *
  865. * > 👉 **Note**: `runSync` performs the run phase, not other phases.
  866. *
  867. * @param {HeadTree extends undefined ? Node : HeadTree} tree
  868. * Tree to transform and inspect.
  869. * @param {Compatible | undefined} [file]
  870. * File associated with `node` (optional); any value accepted as `x` in
  871. * `new VFile(x)`.
  872. * @returns {TailTree extends undefined ? Node : TailTree}
  873. * Transformed tree.
  874. */
  875. runSync(tree, file) {
  876. /** @type {boolean} */
  877. let complete = false
  878. /** @type {(TailTree extends undefined ? Node : TailTree) | undefined} */
  879. let result
  880. this.run(tree, file, realDone)
  881. assertDone('runSync', 'run', complete)
  882. assert(result, 'we either bailed on an error or have a tree')
  883. return result
  884. /**
  885. * @type {RunCallback<TailTree extends undefined ? Node : TailTree>}
  886. */
  887. function realDone(error, tree) {
  888. bail(error)
  889. result = tree
  890. complete = true
  891. }
  892. }
  893. /**
  894. * Compile a syntax tree.
  895. *
  896. * > 👉 **Note**: `stringify` freezes the processor if not already *frozen*.
  897. *
  898. * > 👉 **Note**: `stringify` performs the stringify phase, not the run phase
  899. * > or other phases.
  900. *
  901. * @param {CompileTree extends undefined ? Node : CompileTree} tree
  902. * Tree to compile.
  903. * @param {Compatible | undefined} [file]
  904. * File associated with `node` (optional); any value accepted as `x` in
  905. * `new VFile(x)`.
  906. * @returns {CompileResult extends undefined ? Value : CompileResult}
  907. * Textual representation of the tree (see note).
  908. *
  909. * > 👉 **Note**: unified typically compiles by serializing: most compilers
  910. * > return `string` (or `Uint8Array`).
  911. * > Some compilers, such as the one configured with
  912. * > [`rehype-react`][rehype-react], return other values (in this case, a
  913. * > React tree).
  914. * > If you’re using a compiler that doesn’t serialize, expect different
  915. * > result values.
  916. * >
  917. * > To register custom results in TypeScript, add them to
  918. * > {@link CompileResultMap `CompileResultMap`}.
  919. *
  920. * [rehype-react]: https://github.com/rehypejs/rehype-react
  921. */
  922. stringify(tree, file) {
  923. this.freeze()
  924. const realFile = vfile(file)
  925. const compiler = this.compiler || this.Compiler
  926. assertCompiler('stringify', compiler)
  927. assertNode(tree)
  928. return compiler(tree, realFile)
  929. }
  930. /**
  931. * Configure the processor to use a plugin, a list of usable values, or a
  932. * preset.
  933. *
  934. * If the processor is already using a plugin, the previous plugin
  935. * configuration is changed based on the options that are passed in.
  936. * In other words, the plugin is not added a second time.
  937. *
  938. * > 👉 **Note**: `use` cannot be called on *frozen* processors.
  939. * > Call the processor first to create a new unfrozen processor.
  940. *
  941. * @example
  942. * There are many ways to pass plugins to `.use()`.
  943. * This example gives an overview:
  944. *
  945. * ```js
  946. * import {unified} from 'unified'
  947. *
  948. * unified()
  949. * // Plugin with options:
  950. * .use(pluginA, {x: true, y: true})
  951. * // Passing the same plugin again merges configuration (to `{x: true, y: false, z: true}`):
  952. * .use(pluginA, {y: false, z: true})
  953. * // Plugins:
  954. * .use([pluginB, pluginC])
  955. * // Two plugins, the second with options:
  956. * .use([pluginD, [pluginE, {}]])
  957. * // Preset with plugins and settings:
  958. * .use({plugins: [pluginF, [pluginG, {}]], settings: {position: false}})
  959. * // Settings only:
  960. * .use({settings: {position: false}})
  961. * ```
  962. *
  963. * @template {Array<unknown>} [Parameters=[]]
  964. * @template {Node | string | undefined} [Input=undefined]
  965. * @template [Output=Input]
  966. *
  967. * @overload
  968. * @param {Preset | null | undefined} [preset]
  969. * @returns {Processor<ParseTree, HeadTree, TailTree, CompileTree, CompileResult>}
  970. *
  971. * @overload
  972. * @param {PluggableList} list
  973. * @returns {Processor<ParseTree, HeadTree, TailTree, CompileTree, CompileResult>}
  974. *
  975. * @overload
  976. * @param {Plugin<Parameters, Input, Output>} plugin
  977. * @param {...(Parameters | [boolean])} parameters
  978. * @returns {UsePlugin<ParseTree, HeadTree, TailTree, CompileTree, CompileResult, Input, Output>}
  979. *
  980. * @param {PluggableList | Plugin | Preset | null | undefined} value
  981. * Usable value.
  982. * @param {...unknown} parameters
  983. * Parameters, when a plugin is given as a usable value.
  984. * @returns {Processor<ParseTree, HeadTree, TailTree, CompileTree, CompileResult>}
  985. * Current processor.
  986. */
  987. use(value, ...parameters) {
  988. const attachers = this.attachers
  989. const namespace = this.namespace
  990. assertUnfrozen('use', this.frozen)
  991. if (value === null || value === undefined) {
  992. // Empty.
  993. } else if (typeof value === 'function') {
  994. addPlugin(value, parameters)
  995. } else if (typeof value === 'object') {
  996. if (Array.isArray(value)) {
  997. addList(value)
  998. } else {
  999. addPreset(value)
  1000. }
  1001. } else {
  1002. throw new TypeError('Expected usable value, not `' + value + '`')
  1003. }
  1004. return this
  1005. /**
  1006. * @param {Pluggable} value
  1007. * @returns {undefined}
  1008. */
  1009. function add(value) {
  1010. if (typeof value === 'function') {
  1011. addPlugin(value, [])
  1012. } else if (typeof value === 'object') {
  1013. if (Array.isArray(value)) {
  1014. const [plugin, ...parameters] =
  1015. /** @type {PluginTuple<Array<unknown>>} */ (value)
  1016. addPlugin(plugin, parameters)
  1017. } else {
  1018. addPreset(value)
  1019. }
  1020. } else {
  1021. throw new TypeError('Expected usable value, not `' + value + '`')
  1022. }
  1023. }
  1024. /**
  1025. * @param {Preset} result
  1026. * @returns {undefined}
  1027. */
  1028. function addPreset(result) {
  1029. if (!('plugins' in result) && !('settings' in result)) {
  1030. throw new Error(
  1031. 'Expected usable value but received an empty preset, which is probably a mistake: presets typically come with `plugins` and sometimes with `settings`, but this has neither'
  1032. )
  1033. }
  1034. addList(result.plugins)
  1035. if (result.settings) {
  1036. namespace.settings = extend(true, namespace.settings, result.settings)
  1037. }
  1038. }
  1039. /**
  1040. * @param {PluggableList | null | undefined} plugins
  1041. * @returns {undefined}
  1042. */
  1043. function addList(plugins) {
  1044. let index = -1
  1045. if (plugins === null || plugins === undefined) {
  1046. // Empty.
  1047. } else if (Array.isArray(plugins)) {
  1048. while (++index < plugins.length) {
  1049. const thing = plugins[index]
  1050. add(thing)
  1051. }
  1052. } else {
  1053. throw new TypeError('Expected a list of plugins, not `' + plugins + '`')
  1054. }
  1055. }
  1056. /**
  1057. * @param {Plugin} plugin
  1058. * @param {Array<unknown>} parameters
  1059. * @returns {undefined}
  1060. */
  1061. function addPlugin(plugin, parameters) {
  1062. let index = -1
  1063. let entryIndex = -1
  1064. while (++index < attachers.length) {
  1065. if (attachers[index][0] === plugin) {
  1066. entryIndex = index
  1067. break
  1068. }
  1069. }
  1070. if (entryIndex === -1) {
  1071. attachers.push([plugin, ...parameters])
  1072. }
  1073. // Only set if there was at least a `primary` value, otherwise we’d change
  1074. // `arguments.length`.
  1075. else if (parameters.length > 0) {
  1076. let [primary, ...rest] = parameters
  1077. const currentPrimary = attachers[entryIndex][1]
  1078. if (isPlainObj(currentPrimary) && isPlainObj(primary)) {
  1079. primary = extend(true, currentPrimary, primary)
  1080. }
  1081. attachers[entryIndex] = [plugin, primary, ...rest]
  1082. }
  1083. }
  1084. }
  1085. }
  1086. // Note: this returns a *callable* instance.
  1087. // That’s why it’s documented as a function.
  1088. /**
  1089. * Create a new processor.
  1090. *
  1091. * @example
  1092. * This example shows how a new processor can be created (from `remark`) and linked
  1093. * to **stdin**(4) and **stdout**(4).
  1094. *
  1095. * ```js
  1096. * import process from 'node:process'
  1097. * import concatStream from 'concat-stream'
  1098. * import {remark} from 'remark'
  1099. *
  1100. * process.stdin.pipe(
  1101. * concatStream(function (buf) {
  1102. * process.stdout.write(String(remark().processSync(buf)))
  1103. * })
  1104. * )
  1105. * ```
  1106. *
  1107. * @returns
  1108. * New *unfrozen* processor (`processor`).
  1109. *
  1110. * This processor is configured to work the same as its ancestor.
  1111. * When the descendant processor is configured in the future it does not
  1112. * affect the ancestral processor.
  1113. */
  1114. export const unified = new Processor().freeze()
  1115. /**
  1116. * Assert a parser is available.
  1117. *
  1118. * @param {string} name
  1119. * @param {unknown} value
  1120. * @returns {asserts value is Parser}
  1121. */
  1122. function assertParser(name, value) {
  1123. if (typeof value !== 'function') {
  1124. throw new TypeError('Cannot `' + name + '` without `parser`')
  1125. }
  1126. }
  1127. /**
  1128. * Assert a compiler is available.
  1129. *
  1130. * @param {string} name
  1131. * @param {unknown} value
  1132. * @returns {asserts value is Compiler}
  1133. */
  1134. function assertCompiler(name, value) {
  1135. if (typeof value !== 'function') {
  1136. throw new TypeError('Cannot `' + name + '` without `compiler`')
  1137. }
  1138. }
  1139. /**
  1140. * Assert the processor is not frozen.
  1141. *
  1142. * @param {string} name
  1143. * @param {unknown} frozen
  1144. * @returns {asserts frozen is false}
  1145. */
  1146. function assertUnfrozen(name, frozen) {
  1147. if (frozen) {
  1148. throw new Error(
  1149. 'Cannot call `' +
  1150. name +
  1151. '` on a frozen processor.\nCreate a new processor first, by calling it: use `processor()` instead of `processor`.'
  1152. )
  1153. }
  1154. }
  1155. /**
  1156. * Assert `node` is a unist node.
  1157. *
  1158. * @param {unknown} node
  1159. * @returns {asserts node is Node}
  1160. */
  1161. function assertNode(node) {
  1162. // `isPlainObj` unfortunately uses `any` instead of `unknown`.
  1163. // type-coverage:ignore-next-line
  1164. if (!isPlainObj(node) || typeof node.type !== 'string') {
  1165. throw new TypeError('Expected node, got `' + node + '`')
  1166. // Fine.
  1167. }
  1168. }
  1169. /**
  1170. * Assert that `complete` is `true`.
  1171. *
  1172. * @param {string} name
  1173. * @param {string} asyncName
  1174. * @param {unknown} complete
  1175. * @returns {asserts complete is true}
  1176. */
  1177. function assertDone(name, asyncName, complete) {
  1178. if (!complete) {
  1179. throw new Error(
  1180. '`' + name + '` finished async. Use `' + asyncName + '` instead'
  1181. )
  1182. }
  1183. }
  1184. /**
  1185. * @param {Compatible | undefined} [value]
  1186. * @returns {VFile}
  1187. */
  1188. function vfile(value) {
  1189. return looksLikeAVFile(value) ? value : new VFile(value)
  1190. }
  1191. /**
  1192. * @param {Compatible | undefined} [value]
  1193. * @returns {value is VFile}
  1194. */
  1195. function looksLikeAVFile(value) {
  1196. return Boolean(
  1197. value &&
  1198. typeof value === 'object' &&
  1199. 'message' in value &&
  1200. 'messages' in value
  1201. )
  1202. }
  1203. /**
  1204. * @param {unknown} [value]
  1205. * @returns {value is Value}
  1206. */
  1207. function looksLikeAValue(value) {
  1208. return typeof value === 'string' || isUint8Array(value)
  1209. }
  1210. /**
  1211. * Assert `value` is an `Uint8Array`.
  1212. *
  1213. * @param {unknown} value
  1214. * thing.
  1215. * @returns {value is Uint8Array}
  1216. * Whether `value` is an `Uint8Array`.
  1217. */
  1218. function isUint8Array(value) {
  1219. return Boolean(
  1220. value &&
  1221. typeof value === 'object' &&
  1222. 'byteLength' in value &&
  1223. 'byteOffset' in value
  1224. )
  1225. }