postcss-discard-comments 7.0.4 → 7.0.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "postcss-discard-comments",
3
- "version": "7.0.4",
3
+ "version": "7.0.6",
4
4
  "description": "Discard comments in your CSS files with PostCSS.",
5
5
  "main": "src/index.js",
6
6
  "types": "types/index.d.ts",
@@ -24,7 +24,7 @@
24
24
  },
25
25
  "repository": "cssnano/cssnano",
26
26
  "dependencies": {
27
- "postcss-selector-parser": "^7.1.0"
27
+ "postcss-selector-parser": "^7.1.1"
28
28
  },
29
29
  "bugs": {
30
30
  "url": "https://github.com/cssnano/cssnano/issues"
@@ -33,7 +33,7 @@
33
33
  "node": "^18.12.0 || ^20.9.0 || >=22.0"
34
34
  },
35
35
  "devDependencies": {
36
- "postcss": "^8.5.3",
36
+ "postcss": "^8.5.6",
37
37
  "postcss-scss": "^4.0.9",
38
38
  "postcss-simple-vars": "^7.0.1"
39
39
  },
package/src/index.js CHANGED
@@ -16,8 +16,25 @@ const selectorParser = require('postcss-selector-parser');
16
16
  function pluginCreator(opts = {}) {
17
17
  const remover = new CommentRemover(opts);
18
18
  const matcherCache = new Map();
19
+ const parserCache = new Map();
19
20
  const replacerCache = new Map();
20
21
 
22
+ /**
23
+ * @param {string} source
24
+ * @return {[number, number, number][]}
25
+ */
26
+ function getTokens(source) {
27
+ if (parserCache.has(source)) {
28
+ return parserCache.get(source);
29
+ }
30
+
31
+ const tokens = commentParser(source);
32
+
33
+ parserCache.set(source, tokens);
34
+
35
+ return tokens;
36
+ }
37
+
21
38
  /**
22
39
  * @param {string} source
23
40
  * @return {[number, number, number][]}
@@ -27,7 +44,7 @@ function pluginCreator(opts = {}) {
27
44
  return matcherCache.get(source);
28
45
  }
29
46
 
30
- const result = commentParser(source).filter(([type]) => type);
47
+ const result = getTokens(source).filter(([type]) => type);
31
48
 
32
49
  matcherCache.set(source, result);
33
50
 
@@ -35,29 +52,46 @@ function pluginCreator(opts = {}) {
35
52
  }
36
53
 
37
54
  /**
38
- * @param {string} source
55
+ * @param {string | undefined} rawSource
39
56
  * @param {(s: string) => string[]} space
57
+ * @param {string=} separator
40
58
  * @return {string}
41
59
  */
42
- function replaceComments(source, space, separator = ' ') {
60
+ function replaceComments(rawSource, space, separator = ' ') {
61
+ const source = rawSource || '';
43
62
  const key = source + '@|@' + separator;
44
63
 
45
64
  if (replacerCache.has(key)) {
46
65
  return replacerCache.get(key);
47
66
  }
48
- const parsed = commentParser(source).reduce((value, [type, start, end]) => {
49
- const contents = source.slice(start, end);
50
67
 
68
+ if (source.indexOf('/*') === -1) {
69
+ const normalized = space(source).join(' ');
70
+
71
+ replacerCache.set(key, normalized);
72
+
73
+ return normalized;
74
+ }
75
+
76
+ const parts = [];
77
+
78
+ for (const [type, start, end] of getTokens(source)) {
51
79
  if (!type) {
52
- return value + contents;
80
+ parts.push(source.slice(start, end));
81
+ continue;
53
82
  }
54
83
 
84
+ const contents = source.slice(start, end);
85
+
55
86
  if (remover.canRemove(contents)) {
56
- return value + separator;
87
+ parts.push(separator);
88
+ continue;
57
89
  }
58
90
 
59
- return `${value}/*${contents}*/`;
60
- }, '');
91
+ parts.push('/*' + contents + '*/');
92
+ }
93
+
94
+ const parsed = parts.join('');
61
95
 
62
96
  const result = space(parsed).join(' ');
63
97
 
@@ -67,16 +101,24 @@ function pluginCreator(opts = {}) {
67
101
  }
68
102
 
69
103
  /**
70
- * @param {string} source
104
+ * @param {string | undefined} rawSource
71
105
  * @param {(s: string) => string[]} space
72
106
  * @return {string}
73
107
  */
74
- function replaceCommentsInSelector(source, space) {
108
+ function replaceCommentsInSelector(rawSource, space) {
109
+ const source = rawSource || '';
75
110
  const key = source + '@|@';
76
111
 
77
112
  if (replacerCache.has(key)) {
78
113
  return replacerCache.get(key);
79
114
  }
115
+ if (source.indexOf('/*') === -1) {
116
+ const normalized = space(source).join(' ');
117
+
118
+ replacerCache.set(key, normalized);
119
+
120
+ return normalized;
121
+ }
80
122
  const processed = selectorParser((ast) => {
81
123
  ast.walk((node) => {
82
124
  if (node.type === 'comment') {
@@ -1,30 +1,94 @@
1
1
  'use strict';
2
2
 
3
+ // State machine states reused between parses for better perf
4
+ const STATES = {
5
+ NORMAL: 0,
6
+ IN_SINGLE_QUOTE: 1,
7
+ IN_DOUBLE_QUOTE: 2,
8
+ IN_COMMENT: 3,
9
+ };
10
+
3
11
  /**
12
+ * CSS Comment Parser with context awareness
13
+ * Properly handles comments inside strings, URLs, and escaped characters
14
+ *
4
15
  * @param {string} input
5
16
  * @return {[number, number, number][]}
6
17
  */
7
18
  module.exports = function commentParser(input) {
8
- /** @type [number, number, number][] */
19
+ /** @type {[number, number, number][]} */
9
20
  const tokens = [];
10
21
  const length = input.length;
11
22
  let pos = 0;
12
- let next;
23
+
24
+ let state = STATES.NORMAL;
25
+ let tokenStart = 0;
26
+ let commentStart = 0;
13
27
 
14
28
  while (pos < length) {
15
- next = input.indexOf('/*', pos);
16
-
17
- if (~next) {
18
- tokens.push([0, pos, next]);
19
- pos = next;
20
-
21
- next = input.indexOf('*/', pos + 2);
22
- tokens.push([1, pos + 2, next]);
23
- pos = next + 2;
24
- } else {
25
- tokens.push([0, pos, length]);
26
- pos = length;
29
+ const char = input[pos];
30
+ const nextChar = pos + 1 < length ? input[pos + 1] : '';
31
+
32
+ switch (state) {
33
+ case STATES.NORMAL:
34
+ if (char === '/' && nextChar === '*') {
35
+ // Found comment start - add non-comment token if needed
36
+ if (pos > tokenStart) {
37
+ tokens.push([0, tokenStart, pos]);
38
+ }
39
+ commentStart = pos;
40
+ state = STATES.IN_COMMENT;
41
+ pos += 2; // Skip /*
42
+ continue;
43
+ } else if (char === '"') {
44
+ state = STATES.IN_DOUBLE_QUOTE;
45
+ } else if (char === "'") {
46
+ state = STATES.IN_SINGLE_QUOTE;
47
+ }
48
+ break;
49
+
50
+ case STATES.IN_SINGLE_QUOTE:
51
+ if (char === '\\' && nextChar) {
52
+ // Skip escaped character
53
+ pos += 2;
54
+ continue;
55
+ } else if (char === "'") {
56
+ state = STATES.NORMAL;
57
+ }
58
+ break;
59
+
60
+ case STATES.IN_DOUBLE_QUOTE:
61
+ if (char === '\\' && nextChar) {
62
+ // Skip escaped character
63
+ pos += 2;
64
+ continue;
65
+ } else if (char === '"') {
66
+ state = STATES.NORMAL;
67
+ }
68
+ break;
69
+
70
+ case STATES.IN_COMMENT:
71
+ if (char === '*' && nextChar === '/') {
72
+ // Found comment end
73
+ tokens.push([1, commentStart + 2, pos]);
74
+ tokenStart = pos + 2;
75
+ state = STATES.NORMAL;
76
+ pos += 2; // Skip */
77
+ continue;
78
+ }
79
+ break;
27
80
  }
81
+
82
+ pos++;
83
+ }
84
+
85
+ // Handle remaining content
86
+ if (state === STATES.IN_COMMENT) {
87
+ // Unclosed comment - treat as comment to end
88
+ tokens.push([1, commentStart + 2, length]);
89
+ } else if (tokenStart < length) {
90
+ // Add final non-comment token
91
+ tokens.push([0, tokenStart, length]);
28
92
  }
29
93
 
30
94
  return tokens;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.js"],"names":[],"mappings":";AAKA;;;;GAIG;AACH;;;;GAIG;AACH,sCAHW,OAAO,GACN,OAAO,SAAS,EAAE,MAAM,CAoLnC;;;;;;gBA3Lc,OAAO,YAAC;wBACR,OAAO,YAAC;aACR,CAAA,CAAC,CAAC,EAAE,MAAM,KAAK,OAAO,aAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.js"],"names":[],"mappings":";AAKA;;;;GAIG;AACH;;;;GAIG;AACH,sCAHW,OAAO,GACN,OAAO,SAAS,EAAE,MAAM,CA8NnC;;;;;;gBArOc,OAAO,YAAC;wBACR,OAAO,YAAC;aACR,CAAA,CAAC,CAAC,EAAE,MAAM,KAAK,OAAO,aAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"commentParser.d.ts","sourceRoot":"","sources":["../../src/lib/commentParser.js"],"names":[],"mappings":"AAMiB,iCAHN,MAAM,GACL,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,CA0BrC"}
1
+ {"version":3,"file":"commentParser.d.ts","sourceRoot":"","sources":["../../src/lib/commentParser.js"],"names":[],"mappings":"AAiBiB,iCAHN,MAAM,GACL,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,CA+ErC"}