Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace Linaria preprocessor with postcss to fix an error of postcss-transform-unit #241

Merged
merged 1 commit into from
May 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@
"postcss-nested": "^5.0.6",
"postcss-preset-env": "^7.0.2",
"postcss-reporter": "^7.0.4",
"postcss-value-parser": "^4.2.0",
"resolve": "^1.19.0",
"stylis": "^3.5.4",
"terser-webpack-plugin": "^5.3.0",
"thread-loader": "^3.0.4",
"tslib": "^2.3.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`postcss-linaria-preprocessor :global() selector 1`] = `
"
.a {
color: red
}
page {
width: 50vw;
}
"
`;

exports[`postcss-linaria-preprocessor escape breaking control characters 1`] = `
"
.a {
content: '\\\\feff';
}
"
`;

exports[`postcss-linaria-preprocessor keyframes rename 1`] = `
"
.a {
animation: 1s ease 1 backwards normal zoomIn-a;
@keyframes zoomIn-a {
from {
opacity: 0;
transform: scale(0.94);
}

50% {
opacity: var(--opacity);
}

to {
transform: scale(var(--scale));
}
}
}
"
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`postcss-transform-unit px to rpx 1`] = `
"
.a {
width: 20rpx;
height: 20rpx;
font-size: 30px;
}
"
`;

exports[`postcss-transform-unit rpx to px 1`] = `
"
.a {
width: 10px;
height: 10px;
font-size: 30rpx;
}
"
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import postcss from 'postcss';

const transform = async (css: string) => {
// eslint-disable-next-line global-require
const result = await postcss([require('../postcssLinariaPreprocessor')()]).process(css, {
from: '/path/to/file.css',
});
return result.css;
};

describe('postcss-linaria-preprocessor', () => {
test(':global() selector', () => {
const css = `
.a {
color: red;
:global() {
page {
width: 50vw;
}
}
}
`;
expect(transform(css)).resolves.toMatchSnapshot();
});

test('keyframes rename', () => {
const css = `
.a {
animation: 1s ease 1 backwards normal zoomIn;
@keyframes zoomIn {
from {
opacity: 0;
transform: scale(0.94);
}

50% {
opacity: var(--opacity);
}

to {
transform: scale(var(--scale));
}
}
}
`;
expect(transform(css)).resolves.toMatchSnapshot();
});

test('escape breaking control characters', () => {
const css = `
.a {
content: '\feff';
}
`;
expect(transform(css)).resolves.toMatchSnapshot();
});
});
49 changes: 49 additions & 0 deletions packages/cli/src/config/__tests__/postcssTransformUnit.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import postcss from 'postcss';

describe('postcss-transform-unit', () => {
test('px to rpx', async () => {
const css = `
.a {
width: 10px;
height: 20rpx;
font-size: 30px; /* no */
}
`;

const result = await postcss([
// eslint-disable-next-line global-require
require('../postcssTransformUnit')({
divisor: 1,
multiple: 2,
sourceUnit: 'px',
targetUnit: 'rpx',
}),
]).process(css, {
from: '/path/to/file.css',
});
expect(result.css).toMatchSnapshot();
});

test('rpx to px', async () => {
const css = `
.a {
width: 10px;
height: 20rpx;
font-size: 30rpx; /* no */
}
`;

const result = await postcss([
// eslint-disable-next-line global-require
require('../postcssTransformUnit')({
divisor: 2,
multiple: 1,
sourceUnit: 'rpx',
targetUnit: 'px',
}),
]).process(css, {
from: '/path/to/file.css',
});
expect(result.css).toMatchSnapshot();
});
});
3 changes: 2 additions & 1 deletion packages/cli/src/config/postcssConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,13 @@ const getTransformUnitsPlugin = (unit: TransformUnit) => {
// to improve PostCSS performance, we should always use `require(name)(options)` rather than `[name, options]`
// also we should set `postcssOptions.config` to `false` to avoid loading any `postcss.config.js`
// TODO: hope PostCSS could fix this issue https://github.com/csstools/postcss-preset-env/issues/232#issuecomment-992263741
export default ({ unit }: { unit: TransformUnit }) => {
export default ({ unit, linaria }: { unit: TransformUnit; linaria: boolean }) => {
const transformUnitPlugin = getTransformUnitsPlugin(unit);

return {
plugins: [
require('postcss-each')(),
...(linaria ? [require('./postcssLinariaPreprocessor')()] : []),
// TODO: `postcss-nesting` from `postcss-preset-env` output `:is` pseudo-class that Mini Programs don't support.
// We have to use `postcss-nested` manually before them to prevent `:is` being created.
require('postcss-nested')(),
Expand Down
91 changes: 91 additions & 0 deletions packages/cli/src/config/postcssLinariaPreprocessor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/* eslint-disable import/no-import-module-exports */
import type { PluginCreator } from 'postcss';
import valueParser from 'postcss-value-parser';

const reserved = [
'none',
'inherited',
'initial',
'unset',
/* single-timing-function */
'linear',
'ease',
'ease-in',
'ease-in-out',
'ease-out',
'step-start',
'step-end',
'start',
'end',
/* single-animation-iteration-count */
'infinite',
/* single-animation-direction */
'normal',
'reverse',
'alternate',
'alternate-reverse',
/* single-animation-fill-mode */
'forwards',
'backwards',
'both',
/* single-animation-play-state */
'running',
'paused',
];

interface Options {}

const postcssLinariaPreprocessor: PluginCreator<Options> = () => ({
postcssPlugin: 'postcss-linaria-preprocessor',
Once(root, postcss) {
const nodes = [...root.nodes];
for (const node of nodes) {
if (node.type === 'rule' && node.selector.startsWith('.')) {
// use unique keyframe name to avoid conflict
// inspired from https://github.com/css-modules/postcss-icss-keyframes/blob/5f890e4068820daa80025d88a4f750a3a085dcc8/src/index.js
const keyframeNameMapping = new Map<string, string>();
node.walkAtRules(/keyframes$/, atRule => {
const name = atRule.params;
if (reserved.includes(name)) {
postcss.result.warn(`Unable to use reserve '${name}' animation name`, {
node: atRule,
});

return;
}
const newName = `${name}-${node.selector.replace(/^\./, '')}`;
keyframeNameMapping.set(name, newName);
atRule.params = newName;
});
node.walkDecls(/animation$|animation-name$/, decl => {
const parsed = valueParser(decl.value);
for (const item of parsed.nodes) {
if (item.type === 'word' && keyframeNameMapping.has(item.value)) {
item.value = keyframeNameMapping.get(item.value)!;
}
}
decl.value = parsed.toString();
});

// extract the global rule to the top of the root
node.walkRules(/^:global\(\)$/, globalRule => {
globalRule.remove();
for (const globalNode of globalRule.nodes) {
root.insertAfter(node, globalNode);
}
});
}
}
},
Declaration(decl) {
// escape breaking control characters
// from: https://github.com/thysultan/stylis/blob/v3.5.4/tests/spec.js#L113C3-L116
if (decl.value.match(/[\0\r\f]/)) {
decl.value = decl.value.replace(/\0/g, '\\0').replace(/\r/g, '\\r').replace(/\f/g, '\\f');
}
},
});

postcssLinariaPreprocessor.postcss = true;

module.exports = postcssLinariaPreprocessor;
5 changes: 3 additions & 2 deletions packages/cli/src/config/webpack.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ export const getWebpackConfig = ({
configFile: require.resolve('./linaria.config'),
sourceMap: true,
cacheProvider: require.resolve('./linariaFileCache'),
preprocessor: 'none',
babelOptions: {
// always use internal babel.config.js file
configFile: require.resolve('./babel.config'),
Expand Down Expand Up @@ -232,7 +233,7 @@ export const getWebpackConfig = ({
implementation: require.resolve('postcss'),
postcssOptions: {
config: false,
...postcssConfig({ unit: cssUnit }),
...postcssConfig({ unit: cssUnit, linaria: false }),
},
},
},
Expand Down Expand Up @@ -269,7 +270,7 @@ export const getWebpackConfig = ({
implementation: require.resolve('postcss'),
postcssOptions: {
config: false,
...postcssConfig({ unit: cssUnit }),
...postcssConfig({ unit: cssUnit, linaria: true }),
},
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const main = css`
const todosHeader = css`
padding: 10px 0;
width: 100%;
font-size: 100px;
font-size: 203rpx;
font-weight: 100;
text-align: center;
color: rgba(175, 47, 47, 0.15);
Expand Down
10 changes: 1 addition & 9 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2857,7 +2857,7 @@
"@docusaurus/theme-search-algolia" "2.4.3"
"@docusaurus/types" "2.4.3"

"@docusaurus/[email protected]":
"@docusaurus/[email protected]", "react-loadable@npm:@docusaurus/[email protected]":
version "5.5.2"
resolved "https://registry.yarnpkg.com/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz#81aae0db81ecafbdaee3651f12804580868fa6ce"
integrity sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ==
Expand Down Expand Up @@ -14224,14 +14224,6 @@ react-loadable-ssr-addon-v5-slorber@^1.0.1:
dependencies:
"@babel/runtime" "^7.10.3"

"react-loadable@npm:@docusaurus/[email protected]":
version "5.5.2"
resolved "https://registry.yarnpkg.com/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz#81aae0db81ecafbdaee3651f12804580868fa6ce"
integrity sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ==
dependencies:
"@types/react" "*"
prop-types "^15.6.2"

react-reconciler@^0.26.2:
version "0.26.2"
resolved "https://registry.yarnpkg.com/react-reconciler/-/react-reconciler-0.26.2.tgz#bbad0e2d1309423f76cf3c3309ac6c96e05e9d91"
Expand Down
Loading