You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

478 lines
12 KiB

2 years ago
  1. import Declaration, { DeclarationProps } from './declaration.js'
  2. import Comment, { CommentProps } from './comment.js'
  3. import { Stringifier, Syntax } from './postcss.js'
  4. import AtRule, { AtRuleProps } from './at-rule.js'
  5. import Rule, { RuleProps } from './rule.js'
  6. import Warning, { WarningOptions } from './warning.js'
  7. import CssSyntaxError from './css-syntax-error.js'
  8. import Result from './result.js'
  9. import Input from './input.js'
  10. import Root from './root.js'
  11. import Document from './document.js'
  12. import Container from './container.js'
  13. export type ChildNode = AtRule | Rule | Declaration | Comment
  14. export type AnyNode = AtRule | Rule | Declaration | Comment | Root | Document
  15. export type ChildProps =
  16. | AtRuleProps
  17. | RuleProps
  18. | DeclarationProps
  19. | CommentProps
  20. export interface Position {
  21. /**
  22. * Source offset in file. It starts from 0.
  23. */
  24. offset: number
  25. /**
  26. * Source line in file. In contrast to `offset` it starts from 1.
  27. */
  28. column: number
  29. /**
  30. * Source column in file.
  31. */
  32. line: number
  33. }
  34. export interface Range {
  35. /**
  36. * Start position, inclusive.
  37. */
  38. start: Position
  39. /**
  40. * End position, exclusive.
  41. */
  42. end: Position
  43. }
  44. export interface Source {
  45. /**
  46. * The file source of the node.
  47. */
  48. input: Input
  49. /**
  50. * The inclusive starting position of the nodes source.
  51. */
  52. start?: Position
  53. /**
  54. * The inclusive ending position of the node's source.
  55. */
  56. end?: Position
  57. }
  58. export interface NodeProps {
  59. source?: Source
  60. }
  61. interface NodeErrorOptions {
  62. /**
  63. * Plugin name that created this error. PostCSS will set it automatically.
  64. */
  65. plugin?: string
  66. /**
  67. * A word inside a node's string, that should be highlighted as source
  68. * of error.
  69. */
  70. word?: string
  71. /**
  72. * An index inside a node's string that should be highlighted as source
  73. * of error.
  74. */
  75. index?: number
  76. /**
  77. * An ending index inside a node's string that should be highlighted as
  78. * source of error.
  79. */
  80. endIndex?: number
  81. }
  82. /**
  83. * All node classes inherit the following common methods.
  84. *
  85. * You should not extend this classes to create AST for selector or value
  86. * parser.
  87. */
  88. export default abstract class Node {
  89. /**
  90. * tring representing the nodes type. Possible values are `root`, `atrule`,
  91. * `rule`, `decl`, or `comment`.
  92. *
  93. * ```js
  94. * new Declaration({ prop: 'color', value: 'black' }).type //=> 'decl'
  95. * ```
  96. */
  97. type: string
  98. /**
  99. * The nodes parent node.
  100. *
  101. * ```js
  102. * root.nodes[0].parent === root
  103. * ```
  104. */
  105. parent: Document | Container | undefined
  106. /**
  107. * The input source of the node.
  108. *
  109. * The property is used in source map generation.
  110. *
  111. * If you create a node manually (e.g., with `postcss.decl()`),
  112. * that node will not have a `source` property and will be absent
  113. * from the source map. For this reason, the plugin developer should
  114. * consider cloning nodes to create new ones (in which case the new nodes
  115. * source will reference the original, cloned node) or setting
  116. * the `source` property manually.
  117. *
  118. * ```js
  119. * decl.source.input.from //=> '/home/ai/a.sass'
  120. * decl.source.start //=> { line: 10, column: 2 }
  121. * decl.source.end //=> { line: 10, column: 12 }
  122. * ```
  123. *
  124. * ```js
  125. * // Bad
  126. * const prefixed = postcss.decl({
  127. * prop: '-moz-' + decl.prop,
  128. * value: decl.value
  129. * })
  130. *
  131. * // Good
  132. * const prefixed = decl.clone({ prop: '-moz-' + decl.prop })
  133. * ```
  134. *
  135. * ```js
  136. * if (atrule.name === 'add-link') {
  137. * const rule = postcss.rule({ selector: 'a', source: atrule.source })
  138. * atrule.parent.insertBefore(atrule, rule)
  139. * }
  140. * ```
  141. */
  142. source?: Source
  143. /**
  144. * Information to generate byte-to-byte equal node string as it was
  145. * in the origin input.
  146. *
  147. * Every parser saves its own properties,
  148. * but the default CSS parser uses:
  149. *
  150. * * `before`: the space symbols before the node. It also stores `*`
  151. * and `_` symbols before the declaration (IE hack).
  152. * * `after`: the space symbols after the last child of the node
  153. * to the end of the node.
  154. * * `between`: the symbols between the property and value
  155. * for declarations, selector and `{` for rules, or last parameter
  156. * and `{` for at-rules.
  157. * * `semicolon`: contains true if the last child has
  158. * an (optional) semicolon.
  159. * * `afterName`: the space between the at-rule name and its parameters.
  160. * * `left`: the space symbols between `/*` and the comments text.
  161. * * `right`: the space symbols between the comments text
  162. * and <code>*&#47;</code>.
  163. * * `important`: the content of the important statement,
  164. * if it is not just `!important`.
  165. *
  166. * PostCSS cleans selectors, declaration values and at-rule parameters
  167. * from comments and extra spaces, but it stores origin content in raws
  168. * properties. As such, if you dont change a declarations value,
  169. * PostCSS will use the raw value with comments.
  170. *
  171. * ```js
  172. * const root = postcss.parse('a {\n color:black\n}')
  173. * root.first.first.raws //=> { before: '\n ', between: ':' }
  174. * ```
  175. */
  176. raws: any
  177. /**
  178. * @param defaults Value for node properties.
  179. */
  180. constructor(defaults?: object)
  181. /**
  182. * Returns a `CssSyntaxError` instance containing the original position
  183. * of the node in the source, showing line and column numbers and also
  184. * a small excerpt to facilitate debugging.
  185. *
  186. * If present, an input source map will be used to get the original position
  187. * of the source, even from a previous compilation step
  188. * (e.g., from Sass compilation).
  189. *
  190. * This method produces very useful error messages.
  191. *
  192. * ```js
  193. * if (!variables[name]) {
  194. * throw decl.error(`Unknown variable ${name}`, { word: name })
  195. * // CssSyntaxError: postcss-vars:a.sass:4:3: Unknown variable $black
  196. * // color: $black
  197. * // a
  198. * // ^
  199. * // background: white
  200. * }
  201. * ```
  202. *
  203. * @param message Error description.
  204. * @param opts Options.
  205. *
  206. * @return Error object to throw it.
  207. */
  208. error(message: string, options?: NodeErrorOptions): CssSyntaxError
  209. /**
  210. * This method is provided as a convenience wrapper for `Result#warn`.
  211. *
  212. * ```js
  213. * Declaration: {
  214. * bad: (decl, { result }) => {
  215. * decl.warn(result, 'Deprecated property bad')
  216. * }
  217. * }
  218. * ```
  219. *
  220. * @param result The `Result` instance that will receive the warning.
  221. * @param text Warning message.
  222. * @param opts Warning Options.
  223. *
  224. * @return Created warning object.
  225. */
  226. warn(result: Result, text: string, opts?: WarningOptions): Warning
  227. /**
  228. * Removes the node from its parent and cleans the parent properties
  229. * from the node and its children.
  230. *
  231. * ```js
  232. * if (decl.prop.match(/^-webkit-/)) {
  233. * decl.remove()
  234. * }
  235. * ```
  236. *
  237. * @return Node to make calls chain.
  238. */
  239. remove(): this
  240. /**
  241. * Returns a CSS string representing the node.
  242. *
  243. * ```js
  244. * new Rule({ selector: 'a' }).toString() //=> "a {}"
  245. * ```
  246. *
  247. * @param stringifier A syntax to use in string generation.
  248. * @return CSS string of this node.
  249. */
  250. toString(stringifier?: Stringifier | Syntax): string
  251. /**
  252. * Assigns properties to the current node.
  253. *
  254. * ```js
  255. * decl.assign({ prop: 'word-wrap', value: 'break-word' })
  256. * ```
  257. *
  258. * @param overrides New properties to override the node.
  259. * @return Current node to methods chain.
  260. */
  261. assign(overrides: object): this
  262. /**
  263. * Returns an exact clone of the node.
  264. *
  265. * The resulting cloned node and its (cloned) children will retain
  266. * code style properties.
  267. *
  268. * ```js
  269. * decl.raws.before //=> "\n "
  270. * const cloned = decl.clone({ prop: '-moz-' + decl.prop })
  271. * cloned.raws.before //=> "\n "
  272. * cloned.toString() //=> -moz-transform: scale(0)
  273. * ```
  274. *
  275. * @param overrides New properties to override in the clone.
  276. * @return Clone of the node.
  277. */
  278. clone(overrides?: object): this
  279. /**
  280. * Shortcut to clone the node and insert the resulting cloned node
  281. * before the current node.
  282. *
  283. * ```js
  284. * decl.cloneBefore({ prop: '-moz-' + decl.prop })
  285. * ```
  286. *
  287. * @param overrides Mew properties to override in the clone.
  288. *
  289. * @return New node
  290. */
  291. cloneBefore(overrides?: object): this
  292. /**
  293. * Shortcut to clone the node and insert the resulting cloned node
  294. * after the current node.
  295. *
  296. * @param overrides New properties to override in the clone.
  297. * @return New node.
  298. */
  299. cloneAfter(overrides?: object): this
  300. /**
  301. * Inserts node(s) before the current node and removes the current node.
  302. *
  303. * ```js
  304. * AtRule: {
  305. * mixin: atrule => {
  306. * atrule.replaceWith(mixinRules[atrule.params])
  307. * }
  308. * }
  309. * ```
  310. *
  311. * @param nodes Mode(s) to replace current one.
  312. * @return Current node to methods chain.
  313. */
  314. replaceWith(
  315. ...nodes: (ChildNode | ChildProps | ChildNode[] | ChildProps[])[]
  316. ): this
  317. /**
  318. * Returns the next child of the nodes parent.
  319. * Returns `undefined` if the current node is the last child.
  320. *
  321. * ```js
  322. * if (comment.text === 'delete next') {
  323. * const next = comment.next()
  324. * if (next) {
  325. * next.remove()
  326. * }
  327. * }
  328. * ```
  329. *
  330. * @return Next node.
  331. */
  332. next(): ChildNode | undefined
  333. /**
  334. * Returns the previous child of the nodes parent.
  335. * Returns `undefined` if the current node is the first child.
  336. *
  337. * ```js
  338. * const annotation = decl.prev()
  339. * if (annotation.type === 'comment') {
  340. * readAnnotation(annotation.text)
  341. * }
  342. * ```
  343. *
  344. * @return Previous node.
  345. */
  346. prev(): ChildNode | undefined
  347. /**
  348. * Insert new node before current node to current nodes parent.
  349. *
  350. * Just alias for `node.parent.insertBefore(node, add)`.
  351. *
  352. * ```js
  353. * decl.before('content: ""')
  354. * ```
  355. *
  356. * @param newNode New node.
  357. * @return This node for methods chain.
  358. */
  359. before(newNode: Node | ChildProps | string | Node[]): this
  360. /**
  361. * Insert new node after current node to current nodes parent.
  362. *
  363. * Just alias for `node.parent.insertAfter(node, add)`.
  364. *
  365. * ```js
  366. * decl.after('color: black')
  367. * ```
  368. *
  369. * @param newNode New node.
  370. * @return This node for methods chain.
  371. */
  372. after(newNode: Node | ChildProps | string | Node[]): this
  373. /**
  374. * Finds the Root instance of the nodes tree.
  375. *
  376. * ```js
  377. * root.nodes[0].nodes[0].root() === root
  378. * ```
  379. *
  380. * @return Root parent.
  381. */
  382. root(): Root
  383. /**
  384. * Returns a `Node#raws` value. If the node is missing
  385. * the code style property (because the node was manually built or cloned),
  386. * PostCSS will try to autodetect the code style property by looking
  387. * at other nodes in the tree.
  388. *
  389. * ```js
  390. * const root = postcss.parse('a { background: white }')
  391. * root.nodes[0].append({ prop: 'color', value: 'black' })
  392. * root.nodes[0].nodes[1].raws.before //=> undefined
  393. * root.nodes[0].nodes[1].raw('before') //=> ' '
  394. * ```
  395. *
  396. * @param prop Name of code style property.
  397. * @param defaultType Name of default value, it can be missed
  398. * if the value is the same as prop.
  399. * @return {string} Code style value.
  400. */
  401. raw(prop: string, defaultType?: string): string
  402. /**
  403. * Clear the code style properties for the node and its children.
  404. *
  405. * ```js
  406. * node.raws.before //=> ' '
  407. * node.cleanRaws()
  408. * node.raws.before //=> undefined
  409. * ```
  410. *
  411. * @param keepBetween Keep the `raws.between` symbols.
  412. */
  413. cleanRaws(keepBetween?: boolean): void
  414. /**
  415. * Fix circular links on `JSON.stringify()`.
  416. *
  417. * @return Cleaned object.
  418. */
  419. toJSON(): object
  420. /**
  421. * Convert string index to line/column.
  422. *
  423. * @param index The symbol number in the nodes string.
  424. * @return Symbol position in file.
  425. */
  426. positionInside(index: number): Position
  427. /**
  428. * Get the position for a word or an index inside the node.
  429. *
  430. * @param opts Options.
  431. * @return Position.
  432. */
  433. positionBy(opts?: Pick<WarningOptions, 'word' | 'index'>): Position
  434. /**
  435. * Get the range for a word or start and end index inside the node.
  436. * The start index is inclusive; the end index is exclusive.
  437. *
  438. * @param opts Options.
  439. * @return Range.
  440. */
  441. rangeBy(opts?: Pick<WarningOptions, 'word' | 'index' | 'endIndex'>): Range
  442. }