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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ensure @charset is at the top of generated css #4019

Merged
merged 1 commit into from
Feb 25, 2021
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions UNRELEASED.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ Use [the changelog guidelines](https://git.io/polaris-changelog-guidelines) to f
- Fixed `CheckableButton` missing border when focused ([#3987](https://github.com/Shopify/polaris-react/issues/3987))
- Removed all `outline` and `border`instances of `-ms-high-contrast` as it is non-standard ([#3962](https://github.com/Shopify/polaris-react/pull/3962)).
- Fixed `Autocomplete` popover height not being calculated correctly ([#4015](https://github.com/Shopify/polaris-react/pull/4015)).
- Ensured `@charset` declaration is the first thing in our styles.css file ([#4019](https://github.com/Shopify/polaris-react/pull/4019))

### Documentation

Expand Down
40 changes: 39 additions & 1 deletion config/rollup/plugin-styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,11 +107,17 @@ export function styles({
);
}

const css = bundleModuleIds
let css = bundleModuleIds
.filter((id) => id in cssByFile)
.map((id) => cssByFile[id])
.join('\n\n');

try {
css = hoistCharsetDeclaration(css);
} catch (err) {
rollup.error(err.message);
}

// Regular css file
rollup.emitFile({type: 'asset', fileName: output, source: css});
}
Expand Down Expand Up @@ -174,3 +180,35 @@ export function styles({
function flatMap(array, fn) {
return array.reduce((memo, item) => memo.concat(fn(item)), []);
}

// An @charset declaration must be at the top of a css file rather than part
// way through. Because we're combining multiple files we need to make sure
// that's handled correctly.
function hoistCharsetDeclaration(css) {
let result = css;

const charsetRegex = /(?<=\n|^)@charset .*;\n/;
let standaloneCssFileCharset = '';
let charsetMatch;

// This would be a lot more readable with String.matchAll in node v12
while ((charsetMatch = charsetRegex.exec(result)) !== null) {
// If multiple source files have a charset but they differ then we've
// got a problem when it comes to combining them. This shouldn't ever
// happen though as prettier/editorconfig should force all our source
// files to be UTF-8
if (
standaloneCssFileCharset !== '' &&
charsetMatch[0] === standaloneCssFileCharset
) {
throw new Error(
'Found multiple conflicting @charset declarations in css content',
);
}

standaloneCssFileCharset = charsetMatch[0];
result = result.replace(charsetMatch[0], '');
}

return `${standaloneCssFileCharset}${result}`;
}