-
-
Notifications
You must be signed in to change notification settings - Fork 32.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[pigment-css][nextjs] Allow usage of url() CSS function (#41758)
- Loading branch information
1 parent
53cc684
commit 300a60e
Showing
10 changed files
with
194 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"rules": { | ||
"import/prefer-default-export": "off" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import * as path from 'node:path'; | ||
|
||
export type AsyncResolver = (what: string, importer: string, stack: string[]) => Promise<string>; | ||
|
||
/** | ||
* There might be a better way to do this in future, but due to the async | ||
* nature of the resolver, this is the best way currently to replace url() | ||
* content references with the absolute path of the referenced file. | ||
* This is because WyW-in-JS's preprocessor is a sync call. So we can't resolve | ||
* paths in this call asyncronously. | ||
* The upside is that we can use aliases in the url() references as well | ||
* alongside relative paths. | ||
*/ | ||
export const handleUrlReplacement = async ( | ||
cssText: string, | ||
filename: string, | ||
asyncResolver: AsyncResolver, | ||
projectPath: string, | ||
) => { | ||
// [0] [1][2] [3] [4] | ||
const urlRegex = /\b(url\((["']?))(\.?[^)]+?)(\2\))/g; | ||
let newCss = ''; | ||
let match = urlRegex.exec(cssText); | ||
let lastIndex = 0; | ||
while (match) { | ||
newCss += cssText.substring(lastIndex, match.index); | ||
const mainItem = match[3]; | ||
// no need to handle data uris or absolute paths | ||
if ( | ||
mainItem[0] === '/' || | ||
mainItem.startsWith('data:') || | ||
mainItem.startsWith('http:') || | ||
mainItem.startsWith('https:') | ||
) { | ||
newCss += `url(${mainItem})`; | ||
} else { | ||
// eslint-disable-next-line no-await-in-loop | ||
const resolvedAbsolutePath = await asyncResolver(mainItem, filename, []); | ||
if (!resolvedAbsolutePath) { | ||
newCss += `url(${mainItem})`; | ||
} else { | ||
let pathFromRoot = resolvedAbsolutePath.replace(projectPath, ''); | ||
// Need to do this for Windows paths | ||
pathFromRoot = pathFromRoot.split(path.sep).join('/'); | ||
// const relativePathToProjectRoot = path.relative() | ||
// Next.js expects the path to be relative to the project root and starting with ~ | ||
newCss += `url(~${pathFromRoot})`; | ||
} | ||
} | ||
lastIndex = match.index + match[0].length; | ||
match = urlRegex.exec(cssText); | ||
} | ||
newCss += cssText.substring(lastIndex); | ||
if (!newCss) { | ||
return cssText; | ||
} | ||
return newCss; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
import { expect } from 'chai'; | ||
import { handleUrlReplacement } from '../src/utils'; | ||
|
||
const DATA_URI = | ||
'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTIwLjUgMTMuN2'; | ||
const HTML_LOGO_URL = 'https://mui.com/static/logo.svg'; | ||
const ABSOLUTE_PATH = '/static/logo.svg'; | ||
const dummyResolver = (url: string) => { | ||
return Promise.resolve(url); | ||
}; | ||
|
||
describe('utils', () => { | ||
describe('handleUrlReplacement', () => { | ||
it('should not replace http/data/absolute urls', async () => { | ||
[DATA_URI, HTML_LOGO_URL, ABSOLUTE_PATH].forEach(async (url) => { | ||
const cssString1 = `.className { | ||
background-image: url(${url}); | ||
}`; | ||
const cssString2 = `.className { | ||
background-image: url('${url}'); | ||
}`; | ||
expect( | ||
await handleUrlReplacement( | ||
cssString1, | ||
'/path/to/project/filename.ts', | ||
dummyResolver, | ||
'/path/to/project', | ||
), | ||
).to.equal(cssString1); | ||
expect( | ||
await handleUrlReplacement( | ||
cssString2, | ||
'/path/to/project/filename.ts', | ||
dummyResolver, | ||
'/path/to/project', | ||
), | ||
).to.equal(cssString1); | ||
}); | ||
}); | ||
|
||
it('should replace relative or aliased paths with paths relative to the current working directory', async () => { | ||
const projectPath = '/path/to/project'; | ||
const filePath = `${projectPath}/src/components/Slider.tsx`; | ||
const resolver = (url: string): Promise<string> => { | ||
return new Promise((resolve) => { | ||
if (url.startsWith('@/')) { | ||
resolve(`${projectPath}/src${url.slice(1)}`); | ||
} else if (url.startsWith('../../')) { | ||
resolve(`${projectPath}/src/${url.replace('../../', '')}`); | ||
} else if (url.startsWith('/')) { | ||
resolve(url); | ||
} | ||
}); | ||
}; | ||
const cssString = `.className_c17ksbvo{ | ||
background-color:var(--mui-palette-background-default, #fff); | ||
color:var(--mui-palette-text-primary, rgba(0, 0, 0, 0.87)); | ||
background-image: url('${DATA_URI}'); | ||
background-image: url('${HTML_LOGO_URL}'); | ||
background-image: url(${ABSOLUTE_PATH}); | ||
background-image: url('../../assets/mui.svg'); | ||
background-image: url('@/assets/mui.svg'); | ||
background-image: url('/assets/mui.svg'); | ||
background-image: url('@/assets/mui.svg'); | ||
display: flex; | ||
position: absolute; | ||
}`; | ||
const expectedCssString = `.className_c17ksbvo{ | ||
background-color:var(--mui-palette-background-default, #fff); | ||
color:var(--mui-palette-text-primary, rgba(0, 0, 0, 0.87)); | ||
background-image: url(${DATA_URI}); | ||
background-image: url(${HTML_LOGO_URL}); | ||
background-image: url(${ABSOLUTE_PATH}); | ||
background-image: url(~/src/assets/mui.svg); | ||
background-image: url(~/src/assets/mui.svg); | ||
background-image: url(/assets/mui.svg); | ||
background-image: url(~/src/assets/mui.svg); | ||
display: flex; | ||
position: absolute; | ||
}`; | ||
expect(await handleUrlReplacement(cssString, filePath, resolver, projectPath)).to.equal( | ||
expectedCssString, | ||
); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.