@markuplint/pug-parser 5.4.0 → 5.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,17 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [5.5.0](https://github.com/markuplint/markuplint/compare/v5.4.0...v5.5.0) (2026-11-10)
7
+
8
+ ### Bug Fixes
9
+
10
+ * **pug-parser:** count a leading BOM in the positions ([6d47ba9](https://github.com/markuplint/markuplint/commit/6d47ba911af99c3c4c7cc5848ef1b168f0719e33)), closes [#4488](https://github.com/markuplint/markuplint/issues/4488)
11
+
12
+ ### Features
13
+
14
+ * **pug-parser:** record every value after `=` as the code that it is ([58ae2d8](https://github.com/markuplint/markuplint/commit/58ae2d8a7866aee37a1932d51a05e4feead02435))
15
+
16
+
6
17
  # [5.4.0](https://github.com/markuplint/markuplint/compare/v5.3.0...v5.4.0) (2026-10-29)
7
18
 
8
19
  **Note:** Version bump only for package @markuplint/pug-parser
@@ -0,0 +1,28 @@
1
+ import type { LiteralValueDeclaration } from '@markuplint/ml-ast';
2
+ /**
3
+ * How Pug renders a literal in an attribute value (`pug_attr` of `pug-runtime`, which `pug-attrs` uses to
4
+ * fold a constant too), where it differs from the default of `@markuplint/ml-core`. Pug has no spec
5
+ * package, so the parser says it. Names are in lower case.
6
+ *
7
+ * Pug has no list of boolean attributes and decides by the value alone:
8
+ *
9
+ * - `true` is the attribute with its name as the value (`checked="checked"`, or `checked` under
10
+ * `doctype html`), whichever attribute it is, `data-x=true` included: it is `""` here, as the attribute
11
+ * is present
12
+ * - `false`, `null` and `undefined` remove the attribute
13
+ * - `class` and `style` are also removed for a falsy value, `""` and `0`
14
+ * - Any other value is written with `JSON.stringify`, so `0` is `"0"` on any attribute, a boolean attribute
15
+ * included (`disabled=0` is `disabled="0"`, which is present)
16
+ *
17
+ * What it does not tell, which does not affect a rule:
18
+ *
19
+ * - A boolean attribute has its name as the value (`disabled="disabled"`) unless `doctype html`
20
+ * - A string on a boolean attribute is `""` in the default, and Pug writes the string
21
+ * - A quoted attribute name (`'data-x'="y"`) and an unescaped one (`data-x!="y"`) are read as code
22
+ * whose value is not known
23
+ *
24
+ * @see https://pugjs.org/language/attributes.html#boolean-attributes
25
+ * @see https://pugjs.org/language/doctype.html#doctype-shortcuts
26
+ * @see https://github.com/pugjs/pug/blob/master/packages/pug-runtime/index.js#pug_attr
27
+ */
28
+ export declare const PUG_LITERAL_VALUES: LiteralValueDeclaration;
@@ -0,0 +1,37 @@
1
+ /**
2
+ * How Pug renders a literal in an attribute value (`pug_attr` of `pug-runtime`, which `pug-attrs` uses to
3
+ * fold a constant too), where it differs from the default of `@markuplint/ml-core`. Pug has no spec
4
+ * package, so the parser says it. Names are in lower case.
5
+ *
6
+ * Pug has no list of boolean attributes and decides by the value alone:
7
+ *
8
+ * - `true` is the attribute with its name as the value (`checked="checked"`, or `checked` under
9
+ * `doctype html`), whichever attribute it is, `data-x=true` included: it is `""` here, as the attribute
10
+ * is present
11
+ * - `false`, `null` and `undefined` remove the attribute
12
+ * - `class` and `style` are also removed for a falsy value, `""` and `0`
13
+ * - Any other value is written with `JSON.stringify`, so `0` is `"0"` on any attribute, a boolean attribute
14
+ * included (`disabled=0` is `disabled="0"`, which is present)
15
+ *
16
+ * What it does not tell, which does not affect a rule:
17
+ *
18
+ * - A boolean attribute has its name as the value (`disabled="disabled"`) unless `doctype html`
19
+ * - A string on a boolean attribute is `""` in the default, and Pug writes the string
20
+ * - A quoted attribute name (`'data-x'="y"`) and an unescaped one (`data-x!="y"`) are read as code
21
+ * whose value is not known
22
+ *
23
+ * @see https://pugjs.org/language/attributes.html#boolean-attributes
24
+ * @see https://pugjs.org/language/doctype.html#doctype-shortcuts
25
+ * @see https://github.com/pugjs/pug/blob/master/packages/pug-runtime/index.js#pug_attr
26
+ */
27
+ export const PUG_LITERAL_VALUES = {
28
+ booleanAttrs: { '""': '', '0': '0' },
29
+ ariaAttrs: { true: '', false: null },
30
+ dataAttrs: { true: '', false: null },
31
+ otherAttrs: { true: '', false: null },
32
+ attrs: {
33
+ class: { '""': null, '0': null },
34
+ style: { '""': null, '0': null },
35
+ hidden: { '0': '0' },
36
+ },
37
+ };
package/lib/parser.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { HtmlParser } from '@markuplint/html-parser';
2
- import { ParserError, Parser, AttrState, scriptParser, getNamespace } from '@markuplint/parser-utils';
2
+ import { ParserError, Parser, AttrState, getNamespace } from '@markuplint/parser-utils';
3
+ import { PUG_LITERAL_VALUES } from './literal-values.js';
3
4
  import { pugParse } from './pug-parser/index.js';
4
5
  class HtmlInPugParser extends HtmlParser {
5
6
  constructor() {
@@ -32,6 +33,11 @@ class PugParser extends Parser {
32
33
  super({
33
34
  // Pug expresses nesting through indentation and never emits explicit closing tags.
34
35
  endTagType: 'never',
36
+ // A value of `=` is JavaScript ("their values are just regular JavaScript")
37
+ attrExpressionLanguage: 'javascript',
38
+ literalValues: PUG_LITERAL_VALUES,
39
+ // pug-lexer strips a leading BOM itself, and a space in its place would be indentation
40
+ leadingBom: 'strip',
35
41
  });
36
42
  }
37
43
  tokenize(options) {
@@ -301,41 +307,16 @@ class PugParser extends Parser {
301
307
  potentialName: attr.name.raw.slice(0, -1),
302
308
  });
303
309
  }
304
- const valueCodeTokens = scriptParser(attr.value.raw.trim());
305
- if (valueCodeTokens.length === 1) {
306
- const token = valueCodeTokens[0];
307
- switch (token.type) {
308
- case 'Numeric': {
309
- return {
310
- ...attr,
311
- valueType: 'number',
312
- };
313
- }
314
- case 'Boolean': {
315
- return {
316
- ...attr,
317
- valueType: 'boolean',
318
- };
319
- }
320
- case 'String':
321
- case 'Template': {
322
- const value = super.visitAttr(attr.value, {
323
- startState: AttrState.BeforeValue,
324
- noQuoteValueType: 'script',
325
- });
326
- if (value.type === 'spread') {
327
- throw new ParserError('Unexpected attribute value', value);
328
- }
329
- return {
330
- ...attr,
331
- startQuote: value.startQuote,
332
- value: value.value,
333
- endQuote: value.endQuote,
334
- ...(attr.name.raw.endsWith('!') ? { valueType: 'code' } : {}),
335
- };
336
- }
337
- }
310
+ // An attribute without a value (`disabled`) is `true` and is not written as code, while any value after
311
+ // `=` is JavaScript (the introduction of the page below, which has no heading to cite, says that the
312
+ // values of the attributes are regular JavaScript): whether it is a literal, and what Pug renders it
313
+ // as, is up to `@markuplint/ml-core`, which reads it as it reads the code of any language.
314
+ // @see https://pugjs.org/language/attributes.html
315
+ if (attr.equal.raw === '') {
316
+ return attr;
338
317
  }
318
+ // The value is code. A literal that `@markuplint/ml-core` reads is a string there, and any other code
319
+ // (and an unescaped one, `href!="x"`) stays code.
339
320
  return {
340
321
  ...attr,
341
322
  isDynamicValue: true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@markuplint/pug-parser",
3
- "version": "5.4.0",
3
+ "version": "5.5.0",
4
4
  "description": "Pug parser for markuplint",
5
5
  "repository": {
6
6
  "type": "git",
@@ -28,11 +28,11 @@
28
28
  "clean": "tsc --build --clean tsconfig.build.json"
29
29
  },
30
30
  "dependencies": {
31
- "@markuplint/html-parser": "5.4.0",
32
- "@markuplint/ml-ast": "5.3.0",
33
- "@markuplint/parser-utils": "5.4.0",
31
+ "@markuplint/html-parser": "5.5.0",
32
+ "@markuplint/ml-ast": "5.5.0",
33
+ "@markuplint/parser-utils": "5.5.0",
34
34
  "pug-lexer": "^5.0.1",
35
35
  "pug-parser": "^6.0.0"
36
36
  },
37
- "gitHead": "647f6fb5d5743f060678599c97e9c84b58970747"
37
+ "gitHead": "fedebcfd56eaec4642e68643b2f9e10750d810e1"
38
38
  }