Skip to content

Commit 8348409

Browse files
authored
postcss-rebase-url: do not rebase initial values for at-property (#1106)
1 parent fddfbe1 commit 8348409

File tree

6 files changed

+37
-7
lines changed

6 files changed

+37
-7
lines changed

plugins/postcss-rebase-url/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changes to PostCSS Rebase URL
22

3+
### Unreleased (patch)
4+
5+
- Do not rebase `url()` values in `initial-value` declarations for `@property`.
6+
37
### 1.0.1
48

59
_September 2, 2023_
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
"use strict";var e=require("@csstools/css-tokenizer"),r=require("@csstools/css-parser-algorithms"),t=require("path");const s=/^([-_a-z0-9]+:)?\/\//i;function rebase(e,r,i){if(e.startsWith("data:"))return!1;if(s.test(e))return!1;if(e.startsWith("/"))return e;if(e.startsWith("#"))return e;try{const r=new URL(e);if(r.port||r.protocol)return!1}catch{}const o=t.posix.resolve(t.posix.join(r,e));return t.posix.relative(i,o)}function serializeString(e){let r="";for(const t of e){const e=t.codePointAt(0);if(void 0!==e)switch(e){case 0:r+=String.fromCodePoint(65533);break;case 127:r+=`\\${e.toString(16)}`;break;case 34:case 39:case 92:r+=`\\${t}`;break;default:if(1<=e&&e<=31){r+=`\\${e.toString(16)} `;break}r+=t}else r+=String.fromCodePoint(65533)}return r}function normalizedDir(e){return t.parse(t.resolve(e.trim())).dir.split(t.sep).join(t.posix.sep)}const i=/url\(/i,o=/url/i,creator=()=>({postcssPlugin:"postcss-rebase-url",prepare(){const t=new WeakSet,s=new Set;return{Once(e){e.walkAtRules(/property/i,(e=>{if(!e.nodes)return;const r=e.nodes.find((e=>{if("decl"===e.type&&/syntax/i.test(e.prop))return!0}));r&&/<url>/i.test(r.value)&&s.add(e.params.trim())}))},Declaration(n,{result:a}){var u;if(t.has(n))return;if(n.variable&&!s.has(n.prop))return;const{from:l}=a.opts;if(!l)return;if(null==(u=n.source)||!u.input.from)return;if(!i.test(n.value))return;const c=normalizedDir(l),p=n.source.input.from.trim();if(!p)return;const f=normalizedDir(p),d=r.parseCommaSeparatedListOfComponentValues(e.tokenize({css:n.value})),v=r.replaceComponentValues(d,(t=>{if(r.isTokenNode(t)&&t.value[0]===e.TokenType.URL){const e=rebase(t.value[4].value.trim(),f,c);if(e)return t.value[4].value=e,t.value[1]=`url(${serializeString(e)})`,t}if(r.isFunctionNode(t)&&o.test(t.getName()))for(const s of t.value)if(!r.isWhitespaceNode(s)&&!r.isCommentNode(s)&&r.isTokenNode(s)&&s.value[0]===e.TokenType.String){const e=rebase(s.value[4].value.trim(),f,c);if(e)return s.value[4].value=e,s.value[1]=`"${serializeString(e)}"`,t;break}})),m=r.stringify(v);m!==n.value&&(n.value=m,t.add(n))}}}});creator.postcss=!0,module.exports=creator;
1+
"use strict";var e=require("@csstools/css-tokenizer"),r=require("@csstools/css-parser-algorithms"),t=require("path");const i=/^([-_a-z0-9]+:)?\/\//i;function rebase(e,r,s){if(e.startsWith("data:"))return!1;if(i.test(e))return!1;if(e.startsWith("/"))return e;if(e.startsWith("#"))return e;try{const r=new URL(e);if(r.port||r.protocol)return!1}catch{}const o=t.posix.resolve(t.posix.join(r,e));return t.posix.relative(s,o)}function serializeString(e){let r="";for(const t of e){const e=t.codePointAt(0);if(void 0!==e)switch(e){case 0:r+=String.fromCodePoint(65533);break;case 127:r+=`\\${e.toString(16)}`;break;case 34:case 39:case 92:r+=`\\${t}`;break;default:if(1<=e&&e<=31){r+=`\\${e.toString(16)} `;break}r+=t}else r+=String.fromCodePoint(65533)}return r}function normalizedDir(e){return t.parse(t.resolve(e.trim())).dir.split(t.sep).join(t.posix.sep)}const s=/^initial-value$/i,o=/^property$/i,n=/^syntax$/i,a=/url\(/i,u=/^url$/i,l=/<url>/i,creator=()=>({postcssPlugin:"postcss-rebase-url",prepare(){const t=new WeakSet,i=new Set;return{Once(e){e.walkAtRules(o,(e=>{if(!e.nodes)return;const r=e.nodes.find((e=>{if("decl"===e.type&&n.test(e.prop))return!0}));r&&l.test(r.value)&&i.add(e.params.trim())}))},Declaration(n,{result:l}){var c,p;if(t.has(n))return;if(n.variable&&!i.has(n.prop))return;if(s.test(n.prop)&&"atrule"===(null==(c=n.parent)?void 0:c.type)&&o.test(n.parent.name))return;const{from:f}=l.opts;if(!f)return;if(null==(p=n.source)||!p.input.from)return;if(!a.test(n.value))return;const v=normalizedDir(f),d=n.source.input.from.trim();if(!d)return;const m=normalizedDir(d),k=r.parseCommaSeparatedListOfComponentValues(e.tokenize({css:n.value})),g=r.replaceComponentValues(k,(t=>{if(r.isTokenNode(t)&&t.value[0]===e.TokenType.URL){const e=rebase(t.value[4].value.trim(),m,v);if(e)return t.value[4].value=e,t.value[1]=`url(${serializeString(e)})`,t}if(r.isFunctionNode(t)&&u.test(t.getName()))for(const i of t.value)if(!r.isWhitespaceNode(i)&&!r.isCommentNode(i)&&r.isTokenNode(i)&&i.value[0]===e.TokenType.String){const e=rebase(i.value[4].value.trim(),m,v);if(e)return i.value[4].value=e,i.value[1]=`"${serializeString(e)}"`,t;break}})),S=r.stringify(g);S!==n.value&&(n.value=S,t.add(n))}}}});creator.postcss=!0,module.exports=creator;
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
import{tokenize as r,TokenType as e}from"@csstools/css-tokenizer";import{parseCommaSeparatedListOfComponentValues as t,replaceComponentValues as s,isTokenNode as i,isFunctionNode as o,isWhitespaceNode as a,isCommentNode as n,stringify as u}from"@csstools/css-parser-algorithms";import l from"path";const c=/^([-_a-z0-9]+:)?\/\//i;function rebase(r,e,t){if(r.startsWith("data:"))return!1;if(c.test(r))return!1;if(r.startsWith("/"))return r;if(r.startsWith("#"))return r;try{const e=new URL(r);if(e.port||e.protocol)return!1}catch{}const s=l.posix.resolve(l.posix.join(e,r));return l.posix.relative(t,s)}function serializeString(r){let e="";for(const t of r){const r=t.codePointAt(0);if(void 0!==r)switch(r){case 0:e+=String.fromCodePoint(65533);break;case 127:e+=`\\${r.toString(16)}`;break;case 34:case 39:case 92:e+=`\\${t}`;break;default:if(1<=r&&r<=31){e+=`\\${r.toString(16)} `;break}e+=t}else e+=String.fromCodePoint(65533)}return e}function normalizedDir(r){return l.parse(l.resolve(r.trim())).dir.split(l.sep).join(l.posix.sep)}const f=/url\(/i,p=/url/i,creator=()=>({postcssPlugin:"postcss-rebase-url",prepare(){const l=new WeakSet,c=new Set;return{Once(r){r.walkAtRules(/property/i,(r=>{if(!r.nodes)return;const e=r.nodes.find((r=>{if("decl"===r.type&&/syntax/i.test(r.prop))return!0}));e&&/<url>/i.test(e.value)&&c.add(r.params.trim())}))},Declaration(v,{result:m}){var d;if(l.has(v))return;if(v.variable&&!c.has(v.prop))return;const{from:g}=m.opts;if(!g)return;if(null==(d=v.source)||!d.input.from)return;if(!f.test(v.value))return;const b=normalizedDir(g),S=v.source.input.from.trim();if(!S)return;const h=normalizedDir(S),k=t(r({css:v.value})),z=s(k,(r=>{if(i(r)&&r.value[0]===e.URL){const e=rebase(r.value[4].value.trim(),h,b);if(e)return r.value[4].value=e,r.value[1]=`url(${serializeString(e)})`,r}if(o(r)&&p.test(r.getName()))for(const t of r.value)if(!a(t)&&!n(t)&&i(t)&&t.value[0]===e.String){const e=rebase(t.value[4].value.trim(),h,b);if(e)return t.value[4].value=e,t.value[1]=`"${serializeString(e)}"`,r;break}})),x=u(z);x!==v.value&&(v.value=x,l.add(v))}}}});creator.postcss=!0;export{creator as default};
1+
import{tokenize as r,TokenType as e}from"@csstools/css-tokenizer";import{parseCommaSeparatedListOfComponentValues as t,replaceComponentValues as i,isTokenNode as s,isFunctionNode as o,isWhitespaceNode as a,isCommentNode as n,stringify as u}from"@csstools/css-parser-algorithms";import l from"path";const c=/^([-_a-z0-9]+:)?\/\//i;function rebase(r,e,t){if(r.startsWith("data:"))return!1;if(c.test(r))return!1;if(r.startsWith("/"))return r;if(r.startsWith("#"))return r;try{const e=new URL(r);if(e.port||e.protocol)return!1}catch{}const i=l.posix.resolve(l.posix.join(e,r));return l.posix.relative(t,i)}function serializeString(r){let e="";for(const t of r){const r=t.codePointAt(0);if(void 0!==r)switch(r){case 0:e+=String.fromCodePoint(65533);break;case 127:e+=`\\${r.toString(16)}`;break;case 34:case 39:case 92:e+=`\\${t}`;break;default:if(1<=r&&r<=31){e+=`\\${r.toString(16)} `;break}e+=t}else e+=String.fromCodePoint(65533)}return e}function normalizedDir(r){return l.parse(l.resolve(r.trim())).dir.split(l.sep).join(l.posix.sep)}const f=/^initial-value$/i,p=/^property$/i,v=/^syntax$/i,m=/url\(/i,d=/^url$/i,g=/<url>/i,creator=()=>({postcssPlugin:"postcss-rebase-url",prepare(){const l=new WeakSet,c=new Set;return{Once(r){r.walkAtRules(p,(r=>{if(!r.nodes)return;const e=r.nodes.find((r=>{if("decl"===r.type&&v.test(r.prop))return!0}));e&&g.test(e.value)&&c.add(r.params.trim())}))},Declaration(v,{result:g}){var b,S;if(l.has(v))return;if(v.variable&&!c.has(v.prop))return;if(f.test(v.prop)&&"atrule"===(null==(b=v.parent)?void 0:b.type)&&p.test(v.parent.name))return;const{from:h}=g.opts;if(!h)return;if(null==(S=v.source)||!S.input.from)return;if(!m.test(v.value))return;const $=normalizedDir(h),k=v.source.input.from.trim();if(!k)return;const z=normalizedDir(k),x=t(r({css:v.value})),w=i(x,(r=>{if(s(r)&&r.value[0]===e.URL){const e=rebase(r.value[4].value.trim(),z,$);if(e)return r.value[4].value=e,r.value[1]=`url(${serializeString(e)})`,r}if(o(r)&&d.test(r.getName()))for(const t of r.value)if(!a(t)&&!n(t)&&s(t)&&t.value[0]===e.String){const e=rebase(t.value[4].value.trim(),z,$);if(e)return t.value[4].value=e,t.value[1]=`"${serializeString(e)}"`,r;break}})),y=u(w);y!==v.value&&(v.value=y,l.add(v))}}}});creator.postcss=!0;export{creator as default};

plugins/postcss-rebase-url/src/index.ts

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { PluginCreator, Declaration } from 'postcss';
1+
import type { PluginCreator, Declaration, AtRule } from 'postcss';
22
import { TokenType, tokenize } from '@csstools/css-tokenizer';
33
import { isCommentNode, isFunctionNode, isTokenNode, isWhitespaceNode, parseCommaSeparatedListOfComponentValues, replaceComponentValues, stringify } from '@csstools/css-parser-algorithms';
44
import { rebase } from './rebase';
@@ -8,8 +8,12 @@ import { normalizedDir } from './normalized-dir';
88
/** postcss-rebase-url plugin options */
99
export type pluginOptions = never;
1010

11+
const INITIAL_VALUE_PROPERTY = /^initial-value$/i;
12+
const PROPERTY_NAME = /^property$/i;
13+
const SYNTAX_PROPERTY = /^syntax$/i;
1114
const URL_FUNCTION_CALL = /url\(/i;
12-
const URL_FUNCTION_NAME = /url/i;
15+
const URL_FUNCTION_NAME = /^url$/i;
16+
const URL_SYNTAX = /<url>/i;
1317

1418
const creator: PluginCreator<pluginOptions> = () => {
1519
return {
@@ -20,13 +24,13 @@ const creator: PluginCreator<pluginOptions> = () => {
2024

2125
return {
2226
Once(root) {
23-
root.walkAtRules(/property/i, (atRule) => {
27+
root.walkAtRules(PROPERTY_NAME, (atRule) => {
2428
if (!atRule.nodes) {
2529
return;
2630
}
2731

2832
const syntaxDescriptor = atRule.nodes.find((x) => {
29-
if (x.type === 'decl' && /syntax/i.test(x.prop)) {
33+
if (x.type === 'decl' && SYNTAX_PROPERTY.test(x.prop)) {
3034
return true;
3135
}
3236
}) as Declaration | undefined;
@@ -35,7 +39,7 @@ const creator: PluginCreator<pluginOptions> = () => {
3539
return;
3640
}
3741

38-
if (/<url>/i.test(syntaxDescriptor.value)) {
42+
if (URL_SYNTAX.test(syntaxDescriptor.value)) {
3943
registeredPropsWithURL_Type.add(atRule.params.trim());
4044
}
4145
});
@@ -49,6 +53,14 @@ const creator: PluginCreator<pluginOptions> = () => {
4953
return;
5054
}
5155

56+
if (
57+
INITIAL_VALUE_PROPERTY.test(decl.prop) &&
58+
decl.parent?.type === 'atrule' &&
59+
PROPERTY_NAME.test((decl.parent as AtRule).name)
60+
) {
61+
return;
62+
}
63+
5264
const { from: fromEntryPoint } = result.opts;
5365
if (!fromEntryPoint) {
5466
return;

plugins/postcss-rebase-url/test/basic.expect.css

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,17 @@
4747
initial-value: none;
4848
}
4949

50+
@property --background-image--strictly-typed-with-initial-value {
51+
syntax: '<url> | none';
52+
inherits: true;
53+
initial-value: url('./green.png#initial');
54+
}
55+
5056
.box {
5157
--background-image--strictly-typed: url(imports/green.png#1);
5258
--background-image--loosely-typed: url(./green.png#2);
5359
--background-image--untyped: url(./green.png#3);
60+
--background-image--strictly-typed-with-initial-value: url(imports/green.png#4);
5461
}
5562

5663
.root {

plugins/postcss-rebase-url/test/imports/props.css

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,15 @@
1010
initial-value: none;
1111
}
1212

13+
@property --background-image--strictly-typed-with-initial-value {
14+
syntax: '<url> | none';
15+
inherits: true;
16+
initial-value: url('./green.png#initial');
17+
}
18+
1319
.box {
1420
--background-image--strictly-typed: url(./green.png#1);
1521
--background-image--loosely-typed: url(./green.png#2);
1622
--background-image--untyped: url(./green.png#3);
23+
--background-image--strictly-typed-with-initial-value: url(./green.png#4);
1724
}

0 commit comments

Comments
 (0)