@zaininnari/postcss-cachebuster 0.4.0 → 0.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
@@ -1,10 +1,22 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.5.0 (2026-01-04)
4
+
5
+ - Add Prettier
6
+
7
+ ## 0.4.1 (2026-01-02)
8
+
9
+ - Add ESLint
10
+ - @eslint/js recommended
11
+ - eslint-plugin-n recommended
12
+
3
13
  ## 0.4.0 (2025-12-30)
14
+
4
15
  - update dependencies
5
16
  - update support node version 20+
6
17
 
7
18
  ## 0.3.0 (2025-12-29)
19
+
8
20
  - update npm package
9
21
  - update support node version 18+
10
22
 
@@ -15,12 +27,13 @@
15
27
  ## 0.1.5 (2018-12-07)
16
28
 
17
29
  ## 0.1.4 (2017-01-07)
30
+
18
31
  Fix multiple css imports in single css file.
19
32
 
20
33
  ## 0.1.3 (2016-05-27)
21
34
 
22
- - `type` can be defined as function.
23
- Thanks for Jackson Ray Hamilton [@jacksonrayhamilton](http://github.com/jacksonrayhamilton)
35
+ - `type` can be defined as function.
36
+ Thanks for Jackson Ray Hamilton [@jacksonrayhamilton](http://github.com/jacksonrayhamilton)
24
37
 
25
38
  ## 0.1.2 (2015-11-19)
26
39
 
@@ -29,5 +42,3 @@ Thanks for Jackson Ray Hamilton [@jacksonrayhamilton](http://github.com/jacksonr
29
42
  ## 0.1.1 (2015-11-18)
30
43
 
31
44
  - Added .htc support
32
-
33
-
package/LICENSE CHANGED
@@ -1,6 +1,7 @@
1
1
  The MIT License (MIT)
2
2
 
3
3
  Copyright 2015 Gleb Mikheev <glebmachine@gmail.com>
4
+ Copyright 2026 zaininnari
4
5
 
5
6
  Permission is hereby granted, free of charge, to any person obtaining a copy of
6
7
  this software and associated documentation files (the "Software"), to deal in
package/README.md CHANGED
@@ -8,29 +8,34 @@ This project is a fork of https://github.com/glebmachine/postcss-cachebuster.
8
8
  [PostCSS] plugin added cachebuster to local files based on their date changed.
9
9
 
10
10
  ## Install
11
+
11
12
  ```bash
12
13
  npm i -D @zaininnari/postcss-cachebuster
13
14
  ```
14
15
 
15
16
  ## Usage
17
+
16
18
  ```js
17
19
  import postcss from 'postcss';
18
20
  import cachebuster from '@zaininnari/postcss-cachebuster';
19
21
 
20
- await postcss([
22
+ const result = await postcss([
21
23
  cachebuster({
22
24
  imagesPath: '/images',
23
- cssPath: '/stylesheets'
24
- })
25
+ cssPath: '/stylesheets',
26
+ }),
25
27
  ]).process(css, { from: undefined });
28
+
29
+ result.css;
26
30
  ```
27
31
 
28
32
  ## Input css example
33
+
29
34
  ```css
30
- @import url("/css/styles.css");
35
+ @import url('/css/styles.css');
31
36
  .foo {
32
- background-image : url('../images/index/logo.png');
33
- behavior : url('../behaviors/backgroundsize.min.htc');
37
+ background-image: url('../images/index/logo.png');
38
+ behavior: url('../behaviors/backgroundsize.min.htc');
34
39
  }
35
40
  @font-face {
36
41
  font-family: 'My font';
@@ -39,11 +44,12 @@ await postcss([
39
44
  ```
40
45
 
41
46
  ## Output css example
47
+
42
48
  ```css
43
- @import url("/css/styles.css?v66f22a33fff");
49
+ @import url('/css/styles.css?v66f22a33fff');
44
50
  .foo {
45
- background-image : url('../images/index/logo.png?v14f32a475b8');
46
- behavior : url('../behaviors/backgroundsize.min.htc?v15f55a666c2');
51
+ background-image: url('../images/index/logo.png?v14f32a475b8');
52
+ behavior: url('../behaviors/backgroundsize.min.htc?v15f55a666c2');
47
53
  }
48
54
  @font-face {
49
55
  font-family: 'My font';
@@ -52,14 +58,19 @@ await postcss([
52
58
  ```
53
59
 
54
60
  ## Configure
61
+
55
62
  ```js
56
- postcss([
57
- require('postcss-cachebuster')({
58
- imagesPath : '/images',
59
- cssPath : '/stylesheets'
60
- })
61
- ])
63
+ import postcss from 'postcss';
64
+ import cachebuster from '@zaininnari/postcss-cachebuster';
65
+
66
+ postcss([
67
+ cachebuster({
68
+ imagesPath: '/images',
69
+ cssPath: '/stylesheets',
70
+ }),
71
+ ]);
62
72
  ```
73
+
63
74
  See [PostCSS] docs for examples for your environment.
64
75
 
65
76
  [PostCSS]: https://postcss.org/
@@ -95,31 +106,32 @@ Add to this list by setting the `additionalProps` configuration option.
95
106
  To add support for `mask-image` properties, for example:
96
107
 
97
108
  ```js
98
- postcss([
99
- require('postcss-cachebuster')({
100
- additionalProps : [
101
- 'mask-image',
102
- '-webkit-mask-image'
103
- ]
104
- })
105
- ])
109
+ import postcss from 'postcss';
110
+ import cachebuster from '@zaininnari/postcss-cachebuster';
111
+
112
+ postcss([
113
+ cachebuster({
114
+ additionalProps: ['mask-image', '-webkit-mask-image'],
115
+ }),
116
+ ]);
106
117
  ```
107
118
 
108
119
  Replace the default list by setting the `supportedProps` configuration option.
109
120
  To limit the cachbusting to background images only, for example:
110
121
 
111
122
  ```js
112
- postcss([
113
- require('postcss-cachebuster')({
114
- supportedProps : [
115
- 'background',
116
- 'background-image'
117
- ]
118
- })
119
- ])
123
+ import postcss from 'postcss';
124
+ import cachebuster from '@zaininnari/postcss-cachebuster';
125
+
126
+ postcss([
127
+ cachebuster({
128
+ supportedProps: ['background', 'background-image'],
129
+ }),
130
+ ]);
120
131
  ```
121
132
 
122
133
  ## Contributors
134
+
123
135
  - Gleb Mikheev (https://github.com/glebmachine)
124
136
  - Graham Bates (https://github.com/grahambates)
125
137
  - Yusuke Yagyu (https://github.com/gyugyu)
@@ -0,0 +1,26 @@
1
+ import js from '@eslint/js';
2
+ import globals from 'globals';
3
+ import n from 'eslint-plugin-n';
4
+ import eslintConfigPrettier from 'eslint-config-prettier';
5
+ import { defineConfig } from 'eslint/config';
6
+
7
+ export default defineConfig([
8
+ {
9
+ files: ['**/*.{js,mjs,cjs}'],
10
+ languageOptions: {
11
+ ecmaVersion: 'latest',
12
+ sourceType: 'module',
13
+ globals: {
14
+ ...globals.node,
15
+ ...globals.mocha,
16
+ myCustomGlobal: 'readonly',
17
+ },
18
+ },
19
+ plugins: {
20
+ js,
21
+ n,
22
+ },
23
+ extends: ['js/recommended', 'n/recommended', eslintConfigPrettier],
24
+ ignores: ['eslint.config.js'],
25
+ },
26
+ ]);
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,8 +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;
20
+ // prettier-ignore
12
21
  const supportedPropsDefault = [
13
22
  'background',
14
23
  'background-image',
@@ -42,9 +51,7 @@ const plugin = (opts = {}) => {
42
51
  cachebuster = checksums[checksumKey];
43
52
  } else {
44
53
  const data = fs.readFileSync(assetPath);
45
- cachebuster = crypto.createHash(opts.hashAlgorithm)
46
- .update(data)
47
- .digest('hex');
54
+ cachebuster = crypto.createHash(opts.hashAlgorithm).update(data).digest('hex');
48
55
 
49
56
  checksums[checksumKey] = cachebuster;
50
57
  }
@@ -56,10 +63,17 @@ const plugin = (opts = {}) => {
56
63
  return cachebuster;
57
64
  }
58
65
 
59
- function resolveUrl(assetUrl, file, imagesPath) {
66
+ /**
67
+ * @param assetUrl {URL}
68
+ * @param file {string}
69
+ * @param imagesPath {string}
70
+ * @param isRootRelativeButNotProtocolRelative {boolean}
71
+ * @returns {string}
72
+ */
73
+ function resolveUrl(assetUrl, file, imagesPath, isRootRelativeButNotProtocolRelative) {
60
74
  let assetPath = decodeURI(assetUrl.pathname);
61
75
 
62
- if (/^\//.test(assetUrl.pathname)) {
76
+ if (isRootRelativeButNotProtocolRelative) {
63
77
  assetPath = path.join(imagesPath, assetPath);
64
78
  } else {
65
79
  assetPath = path.join(opts.cssPath || path.dirname(file), assetPath);
@@ -67,14 +81,22 @@ const plugin = (opts = {}) => {
67
81
  return assetPath;
68
82
  }
69
83
 
70
- function updateAssetUrl(assetUrl, inputFile) {
71
- const assetPath = resolveUrl(assetUrl, inputFile, opts.imagesPath);
84
+ /**
85
+ * @param assetUrl {URL}
86
+ * @param inputFile {string}
87
+ * @param isRootRelativeButNotProtocolRelative {boolean}
88
+ */
89
+ function updateAssetUrl(assetUrl, inputFile, isRootRelativeButNotProtocolRelative) {
90
+ const assetPath = resolveUrl(assetUrl, inputFile, opts.imagesPath, isRootRelativeButNotProtocolRelative);
72
91
 
73
92
  // complete url with cachebuster
74
- const cachebuster = createCachebuster(assetPath, assetUrl.pathname, opts.type);
93
+ const originPath = isRootRelativeButNotProtocolRelative ? assetUrl.pathname : assetUrl.pathname.substring(1);
94
+ const cachebuster = createCachebuster(assetPath, originPath, opts.type);
75
95
  if (!cachebuster) {
76
96
  return;
77
- } else if (typeof opts.type === 'function') {
97
+ }
98
+
99
+ if (typeof opts.type === 'function') {
78
100
  assetUrl.pathname = cachebuster;
79
101
  } else if (assetUrl.search && assetUrl.search.length > 1) {
80
102
  assetUrl.search = assetUrl.search + '&' + opts.paramName + cachebuster;
@@ -83,6 +105,39 @@ const plugin = (opts = {}) => {
83
105
  }
84
106
  }
85
107
 
108
+ const DUMMY_BASE = 'http://localhost';
109
+
110
+ /**
111
+ * @param input {string}
112
+ * @returns {parsed}
113
+ */
114
+ function parseUrlPreserveRelative(input) {
115
+ const isAbsolute = /^[a-zA-Z][a-zA-Z\d+\-.]*:/.test(input);
116
+ const isRootRelativeButNotProtocolRelative = /^\/(?!\/)/.test(input);
117
+ const u = isAbsolute ? new URL(input) : new URL(input, DUMMY_BASE);
118
+ return { u, isAbsolute, isRootRelativeButNotProtocolRelative, originalUrl: input };
119
+ }
120
+
121
+ /**
122
+ * @param parsed {parsed}
123
+ * @returns {string|string}
124
+ */
125
+ function formatUrlPreserveRelative(parsed) {
126
+ const u = parsed.u;
127
+ const isAbsolute = parsed.isAbsolute;
128
+ const isRootRelativeButNotProtocolRelative = parsed.isRootRelativeButNotProtocolRelative;
129
+
130
+ if (isAbsolute) {
131
+ return u.toString();
132
+ }
133
+ const url = `${u.pathname}${u.search}${u.hash}`;
134
+ if (!isRootRelativeButNotProtocolRelative) {
135
+ // remove start slash
136
+ return url.substring(1);
137
+ }
138
+ return url;
139
+ }
140
+
86
141
  return {
87
142
  postcssPlugin: 'postcss-cachebuster',
88
143
  Once(root) {
@@ -96,10 +151,10 @@ const plugin = (opts = {}) => {
96
151
  const quote = results[1] || '"';
97
152
  const originalUrl = results[2];
98
153
 
99
- const assetUrl = url.parse(originalUrl);
100
- updateAssetUrl(assetUrl, inputFile);
154
+ const parsed = parseUrlPreserveRelative(originalUrl);
155
+ updateAssetUrl(parsed.u, inputFile, parsed.isRootRelativeButNotProtocolRelative, parsed.originalUrl);
101
156
 
102
- atrule.params = 'url(' + quote + url.format(assetUrl) + quote + ')';
157
+ atrule.params = 'url(' + quote + formatUrlPreserveRelative(parsed) + quote + ')';
103
158
  });
104
159
 
105
160
  root.walkDecls(function walkThroughtDeclarations(declaration) {
@@ -111,20 +166,22 @@ const plugin = (opts = {}) => {
111
166
  declaration.value = declaration.value.replace(pattern, function (match, quote, originalUrl) {
112
167
  quote = quote || '"';
113
168
 
114
- const assetUrl = url.parse(originalUrl);
169
+ const parsed = parseUrlPreserveRelative(originalUrl);
170
+ const assetUrl = parsed.u;
115
171
 
116
172
  // only locals
117
173
  if (
118
- assetUrl.host ||
174
+ assetUrl.toString().indexOf(DUMMY_BASE) !== 0 ||
175
+ parsed.isAbsolute ||
119
176
  assetUrl.pathname.indexOf('//') === 0 ||
120
177
  assetUrl.pathname.indexOf(';base64') !== -1
121
178
  ) {
122
179
  return match;
123
180
  }
124
181
 
125
- updateAssetUrl(assetUrl, inputFile);
182
+ updateAssetUrl(assetUrl, inputFile, parsed.isRootRelativeButNotProtocolRelative);
126
183
 
127
- return 'url(' + quote + url.format(assetUrl) + quote + ')';
184
+ return 'url(' + quote + formatUrlPreserveRelative(parsed) + quote + ')';
128
185
  });
129
186
  });
130
187
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zaininnari/postcss-cachebuster",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Fork of postcss-cachebuster. Cachebusting all local files in css",
5
5
  "keywords": [
6
6
  "postcss",
@@ -38,11 +38,17 @@
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-config-prettier": "^10.1.8",
45
+ "eslint-plugin-n": "^17.23.1",
46
+ "globals": "^16.5.0",
42
47
  "gulp": "^5.0.1",
43
48
  "gulp-mocha": "^10.0.1",
44
49
  "gulp-postcss": "^10.0.0",
45
50
  "postcss": "^8.5.6",
51
+ "prettier": "^3.7.4",
46
52
  "sinon": "^21.0.1",
47
53
  "sinon-chai": "^4.0.1"
48
54
  },
@@ -50,6 +56,9 @@
50
56
  "glob": "9.3.5"
51
57
  },
52
58
  "scripts": {
53
- "test": "gulp --gulpfile gulpfile.mjs"
59
+ "lint": "eslint .",
60
+ "test": "gulp --gulpfile gulpfile.mjs",
61
+ "format:check": "prettier . --check",
62
+ "format:write": "prettier . --write"
54
63
  }
55
64
  }
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
-
@@ -1,31 +0,0 @@
1
- name: Test (latest deps)
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
-
23
- - name: Remove package-lock.json
24
- run: rm -f package-lock.json
25
-
26
- - name: Install dependencies (latest allowed by package.json)
27
- run: npm install
28
-
29
- - name: Run tests
30
- run: npm test
31
-
@@ -1,28 +0,0 @@
1
- name: Test
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 test
@@ -1,6 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <project version="4">
3
- <component name="Ask2AgentMigrationStateService">
4
- <option name="migrationStatus" value="COMPLETED" />
5
- </component>
6
- </project>
@@ -1,6 +0,0 @@
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/.idea/misc.xml DELETED
@@ -1,6 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <project version="4">
3
- <component name="ProjectRootManager" version="2">
4
- <output url="file://$PROJECT_DIR$/out" />
5
- </component>
6
- </project>
package/.idea/modules.xml DELETED
@@ -1,8 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <project version="4">
3
- <component name="ProjectModuleManager">
4
- <modules>
5
- <module fileurl="file://$PROJECT_DIR$/postcss-cachebuster.iml" filepath="$PROJECT_DIR$/postcss-cachebuster.iml" />
6
- </modules>
7
- </component>
8
- </project>
package/.idea/vcs.xml DELETED
@@ -1,7 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <project version="4">
3
- <component name="VcsDirectoryMappings">
4
- <mapping directory="" vcs="Git" />
5
- <mapping directory="$PROJECT_DIR$" vcs="Git" />
6
- </component>
7
- </project>