@zaininnari/postcss-cachebuster 0.3.0 → 0.4.1

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.
@@ -0,0 +1,28 @@
1
+ name: Lint
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+
7
+ jobs:
8
+ test:
9
+ runs-on: ubuntu-latest
10
+
11
+ strategy:
12
+ matrix:
13
+ node-version: [20, 22, 24]
14
+
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+
18
+ - name: Use Node.js
19
+ uses: actions/setup-node@v4
20
+ with:
21
+ node-version: ${{ matrix.node-version }}
22
+ cache: 'npm'
23
+
24
+ - name: Install dependencies
25
+ run: npm ci
26
+
27
+ - name: Run tests
28
+ run: npm run lint
@@ -10,7 +10,7 @@ jobs:
10
10
 
11
11
  strategy:
12
12
  matrix:
13
- node-version: [18, 20, 22, 24]
13
+ node-version: [20, 22, 24]
14
14
 
15
15
  steps:
16
16
  - uses: actions/checkout@v4
@@ -10,7 +10,7 @@ jobs:
10
10
 
11
11
  strategy:
12
12
  matrix:
13
- node-version: [18, 20, 22, 24]
13
+ node-version: [20, 22, 24]
14
14
 
15
15
  steps:
16
16
  - uses: actions/checkout@v4
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="JavaScriptLibraryMappings">
4
+ <includedPredefinedLibrary name="Node.js Core" />
5
+ </component>
6
+ </project>
package/CHANGELOG.md CHANGED
@@ -1,3 +1,27 @@
1
+ # Changelog
2
+
3
+ ## 0.4.1 (2026-01-02)
4
+ - Add ESLint
5
+ - @eslint/js recommended
6
+ - eslint-plugin-n recommended
7
+
8
+ ## 0.4.0 (2025-12-30)
9
+ - update dependencies
10
+ - update support node version 20+
11
+
12
+ ## 0.3.0 (2025-12-29)
13
+ - update npm package
14
+ - update support node version 18+
15
+
16
+ ## Before fork
17
+
18
+ ## 0.1.6 (2018-12-07)
19
+
20
+ ## 0.1.5 (2018-12-07)
21
+
22
+ ## 0.1.4 (2017-01-07)
23
+ Fix multiple css imports in single css file.
24
+
1
25
  ## 0.1.3 (2016-05-27)
2
26
 
3
27
  - `type` can be defined as function.
@@ -0,0 +1,22 @@
1
+ import js from "@eslint/js";
2
+ import globals from "globals";
3
+ import n from "eslint-plugin-n";
4
+ import {defineConfig} from "eslint/config";
5
+
6
+ export default defineConfig([
7
+ {
8
+ files: ["**/*.{js,mjs,cjs}"],
9
+ languageOptions: {
10
+ ecmaVersion: "latest",
11
+ sourceType: "module",
12
+ globals: {
13
+ ...globals.node,
14
+ ...globals.mocha,
15
+ myCustomGlobal: "readonly",
16
+ },
17
+ },
18
+ plugins: {js, n},
19
+ extends: ["js/recommended", "n/recommended"],
20
+ ignores: ['eslint.config.js'],
21
+ },
22
+ ]);
package/index.mjs CHANGED
@@ -1,4 +1,3 @@
1
- import url from 'url';
2
1
  import fs from 'fs';
3
2
  import crypto from 'crypto';
4
3
  import chalk from 'chalk';
@@ -7,9 +6,18 @@ import path from 'canonical-path';
7
6
 
8
7
  const checksums = {};
9
8
 
9
+ /**
10
+ * parsed
11
+ * @typedef {Object} parsed
12
+ * @property {URL} u
13
+ * @property {boolean} isAbsolute
14
+ * @property {boolean} isRootRelativeButNotProtocolRelative
15
+ * @property {string} originalUrl
16
+ */
17
+
10
18
  const plugin = (opts = {}) => {
11
19
  const pattern = /url\((['"])?([^'")]+)(['"])?\)/g;
12
- let supportedProps = [
20
+ const supportedPropsDefault = [
13
21
  'background',
14
22
  'background-image',
15
23
  'border-image',
@@ -22,8 +30,9 @@ const plugin = (opts = {}) => {
22
30
  opts.type = opts.type || 'mtime';
23
31
  opts.paramName = opts.paramName || 'v';
24
32
  opts.hashAlgorithm = opts.hashAlgorithm || 'md5';
25
- supportedProps = opts.supportedProps || supportedProps;
26
- supportedProps = supportedProps.concat(opts.additionalProps || []);
33
+ opts.supportedProps = opts.supportedProps || supportedPropsDefault;
34
+ opts.additionalProps = opts.additionalProps || [];
35
+ const supportedProps = opts.supportedProps.concat(opts.additionalProps);
27
36
 
28
37
  function createCachebuster(assetPath, origPath, type) {
29
38
  let cachebuster;
@@ -55,10 +64,17 @@ const plugin = (opts = {}) => {
55
64
  return cachebuster;
56
65
  }
57
66
 
58
- function resolveUrl(assetUrl, file, imagesPath) {
67
+ /**
68
+ * @param assetUrl {URL}
69
+ * @param file {string}
70
+ * @param imagesPath {string}
71
+ * @param isRootRelativeButNotProtocolRelative {boolean}
72
+ * @returns {string}
73
+ */
74
+ function resolveUrl(assetUrl, file, imagesPath, isRootRelativeButNotProtocolRelative) {
59
75
  let assetPath = decodeURI(assetUrl.pathname);
60
76
 
61
- if (/^\//.test(assetUrl.pathname)) {
77
+ if (isRootRelativeButNotProtocolRelative) {
62
78
  assetPath = path.join(imagesPath, assetPath);
63
79
  } else {
64
80
  assetPath = path.join(opts.cssPath || path.dirname(file), assetPath);
@@ -66,14 +82,22 @@ const plugin = (opts = {}) => {
66
82
  return assetPath;
67
83
  }
68
84
 
69
- function updateAssetUrl(assetUrl, inputFile) {
70
- const assetPath = resolveUrl(assetUrl, inputFile, opts.imagesPath);
85
+ /**
86
+ * @param assetUrl {URL}
87
+ * @param inputFile {string}
88
+ * @param isRootRelativeButNotProtocolRelative {boolean}
89
+ */
90
+ function updateAssetUrl(assetUrl, inputFile, isRootRelativeButNotProtocolRelative) {
91
+ const assetPath = resolveUrl(assetUrl, inputFile, opts.imagesPath, isRootRelativeButNotProtocolRelative);
71
92
 
72
93
  // complete url with cachebuster
73
- const cachebuster = createCachebuster(assetPath, assetUrl.pathname, opts.type);
94
+ const originPath = isRootRelativeButNotProtocolRelative ? assetUrl.pathname : assetUrl.pathname.substring(1);
95
+ const cachebuster = createCachebuster(assetPath, originPath, opts.type);
74
96
  if (!cachebuster) {
75
97
  return;
76
- } else if (typeof opts.type === 'function') {
98
+ }
99
+
100
+ if (typeof opts.type === 'function') {
77
101
  assetUrl.pathname = cachebuster;
78
102
  } else if (assetUrl.search && assetUrl.search.length > 1) {
79
103
  assetUrl.search = assetUrl.search + '&' + opts.paramName + cachebuster;
@@ -82,6 +106,39 @@ const plugin = (opts = {}) => {
82
106
  }
83
107
  }
84
108
 
109
+ const DUMMY_BASE = 'http://localhost';
110
+
111
+ /**
112
+ * @param input {string}
113
+ * @returns {parsed}
114
+ */
115
+ function parseUrlPreserveRelative(input) {
116
+ const isAbsolute = /^[a-zA-Z][a-zA-Z\d+\-.]*:/.test(input);
117
+ const isRootRelativeButNotProtocolRelative = /^\/(?!\/)/.test(input);
118
+ const u = isAbsolute ? new URL(input) : new URL(input, DUMMY_BASE);
119
+ return {u, isAbsolute, isRootRelativeButNotProtocolRelative, originalUrl: input};
120
+ }
121
+
122
+ /**
123
+ * @param parsed {parsed}
124
+ * @returns {string|string}
125
+ */
126
+ function formatUrlPreserveRelative(parsed) {
127
+ const u = parsed.u;
128
+ const isAbsolute = parsed.isAbsolute;
129
+ const isRootRelativeButNotProtocolRelative = parsed.isRootRelativeButNotProtocolRelative;
130
+
131
+ if (isAbsolute) {
132
+ return u.toString();
133
+ }
134
+ const url = `${u.pathname}${u.search}${u.hash}`;
135
+ if (!isRootRelativeButNotProtocolRelative) {
136
+ // remove start slash
137
+ return url.substring(1);
138
+ }
139
+ return url;
140
+ }
141
+
85
142
  return {
86
143
  postcssPlugin: 'postcss-cachebuster',
87
144
  Once(root) {
@@ -95,10 +152,10 @@ const plugin = (opts = {}) => {
95
152
  const quote = results[1] || '"';
96
153
  const originalUrl = results[2];
97
154
 
98
- const assetUrl = url.parse(originalUrl);
99
- updateAssetUrl(assetUrl, inputFile);
155
+ const parsed = parseUrlPreserveRelative(originalUrl);
156
+ updateAssetUrl(parsed.u, inputFile, parsed.isRootRelativeButNotProtocolRelative, parsed.originalUrl);
100
157
 
101
- atrule.params = 'url(' + quote + url.format(assetUrl) + quote + ')';
158
+ atrule.params = 'url(' + quote + formatUrlPreserveRelative(parsed) + quote + ')';
102
159
  });
103
160
 
104
161
  root.walkDecls(function walkThroughtDeclarations(declaration) {
@@ -110,20 +167,22 @@ const plugin = (opts = {}) => {
110
167
  declaration.value = declaration.value.replace(pattern, function (match, quote, originalUrl) {
111
168
  quote = quote || '"';
112
169
 
113
- const assetUrl = url.parse(originalUrl);
170
+ const parsed = parseUrlPreserveRelative(originalUrl);
171
+ const assetUrl = parsed.u;
114
172
 
115
173
  // only locals
116
174
  if (
117
- assetUrl.host ||
175
+ assetUrl.toString().indexOf(DUMMY_BASE) !== 0 ||
176
+ parsed.isAbsolute ||
118
177
  assetUrl.pathname.indexOf('//') === 0 ||
119
178
  assetUrl.pathname.indexOf(';base64') !== -1
120
179
  ) {
121
180
  return match;
122
181
  }
123
182
 
124
- updateAssetUrl(assetUrl, inputFile);
183
+ updateAssetUrl(assetUrl, inputFile, parsed.isRootRelativeButNotProtocolRelative);
125
184
 
126
- return 'url(' + quote + url.format(assetUrl) + quote + ')';
185
+ return 'url(' + quote + formatUrlPreserveRelative(parsed) + quote + ')';
127
186
  });
128
187
  });
129
188
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zaininnari/postcss-cachebuster",
3
- "version": "0.3.0",
3
+ "version": "0.4.1",
4
4
  "description": "Fork of postcss-cachebuster. Cachebusting all local files in css",
5
5
  "keywords": [
6
6
  "postcss",
@@ -38,7 +38,11 @@
38
38
  "path": "^0.12.7"
39
39
  },
40
40
  "devDependencies": {
41
+ "@eslint/js": "^9.39.2",
41
42
  "chai": "^6.2.2",
43
+ "eslint": "^9.39.2",
44
+ "eslint-plugin-n": "^17.23.1",
45
+ "globals": "^16.5.0",
42
46
  "gulp": "^5.0.1",
43
47
  "gulp-mocha": "^10.0.1",
44
48
  "gulp-postcss": "^10.0.0",
@@ -46,8 +50,11 @@
46
50
  "sinon": "^21.0.1",
47
51
  "sinon-chai": "^4.0.1"
48
52
  },
53
+ "overrides": {
54
+ "glob": "9.3.5"
55
+ },
49
56
  "scripts": {
50
- "test": "gulp --gulpfile gulpfile.mjs",
51
- "lint:watch": "esw --fix --watch index.mjs"
57
+ "lint": "eslint .",
58
+ "test": "gulp --gulpfile gulpfile.mjs"
52
59
  }
53
60
  }
package/.eslintrc.json DELETED
@@ -1,13 +0,0 @@
1
-
2
- {
3
- "rules": {
4
- "max-len": 0,
5
- "indent": ["error", 2, { "VariableDeclarator": 1 }],
6
- "global-require": 0,
7
- "func-names": 0,
8
- "prefer-rest-params": 1,
9
- "strict": 1,
10
- "no-param-reassign": 0
11
- }
12
- }
13
-