From 46722bac993308d8e4f1bb3d0b3086b802013d3d Mon Sep 17 00:00:00 2001 From: illright Date: Wed, 3 Jun 2020 16:06:11 +0300 Subject: [PATCH] feat: add the @global {} rule support --- src/autoProcess.ts | 11 ++++++++ src/processors/globalRule.ts | 13 +++++++++ src/transformers/globalRule.ts | 28 +++++++++++++++++++ src/transformers/globalStyle.ts | 2 +- test/transformers/globalRule.test.ts | 42 ++++++++++++++++++++++++++++ 5 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 src/processors/globalRule.ts create mode 100644 src/transformers/globalRule.ts create mode 100644 test/transformers/globalRule.test.ts diff --git a/src/autoProcess.ts b/src/autoProcess.ts index 62fcbd6b..2f822f01 100644 --- a/src/autoProcess.ts +++ b/src/autoProcess.ts @@ -27,6 +27,7 @@ interface Transformers { coffeescript?: TransformerOptions; pug?: TransformerOptions; globalStyle?: TransformerOptions; + globalRule?: TransformerOptions; replace?: Options.Replace; [languageName: string]: TransformerOptions; } @@ -55,6 +56,7 @@ type AutoPreprocessOptions = { coffeescript?: TransformerOptions; pug?: TransformerOptions; globalStyle?: TransformerOptions; + globalRule?: TransformerOptions; // workaround while we don't have this // https://github.com/microsoft/TypeScript/issues/17867 [languageName: string]: @@ -270,6 +272,15 @@ export function autoPreprocess( map = transformed.map; } + const transformed = await runTransformer('globalRule', null, { + content: code, + map, + filename, + }); + + code = transformed.code; + map = transformed.map; + return { code, map, dependencies }; }, }; diff --git a/src/processors/globalRule.ts b/src/processors/globalRule.ts new file mode 100644 index 00000000..1a7a5fe7 --- /dev/null +++ b/src/processors/globalRule.ts @@ -0,0 +1,13 @@ +import { PreprocessorGroup } from '../types'; + +export default (): PreprocessorGroup => { + return { + async style({ content, filename }) { + const { default: transformer } = await import( + '../transformers/globalRule' + ); + + return transformer({ content, filename }); + }, + }; +}; diff --git a/src/transformers/globalRule.ts b/src/transformers/globalRule.ts new file mode 100644 index 00000000..27af4377 --- /dev/null +++ b/src/transformers/globalRule.ts @@ -0,0 +1,28 @@ +import postcss from 'postcss'; + +import { Transformer } from '../types'; +import { globalifyPlugin } from './globalStyle'; + +const globalifyRulePlugin = (root: any) => { + root.walkAtRules(/^global$/, (atrule: any) => { + globalifyPlugin(atrule); + let after = atrule; + + atrule.each(function (child: any) { + after.after(child); + after = child; + }); + + atrule.remove(); + }); +}; + +const transformer: Transformer = async ({ content, filename }) => { + const { css, map: newMap } = await postcss() + .use(globalifyRulePlugin) + .process(content, { from: filename, map: true }); + + return { code: css, map: newMap }; +}; + +export default transformer; diff --git a/src/transformers/globalStyle.ts b/src/transformers/globalStyle.ts index ee01ecf6..9d03aa2b 100644 --- a/src/transformers/globalStyle.ts +++ b/src/transformers/globalStyle.ts @@ -2,7 +2,7 @@ import postcss from 'postcss'; import { Transformer } from '../types'; -const globalifyPlugin = (root: any) => { +export const globalifyPlugin = (root: any) => { root.walkAtRules(/keyframes$/, (atrule: any) => { if (!atrule.params.startsWith('-global-')) { atrule.params = '-global-' + atrule.params; diff --git a/test/transformers/globalRule.test.ts b/test/transformers/globalRule.test.ts new file mode 100644 index 00000000..aa21fe5b --- /dev/null +++ b/test/transformers/globalRule.test.ts @@ -0,0 +1,42 @@ +import autoProcess from '../../src'; +import { preprocess } from '../utils'; + +describe('transformer - globalRule', () => { + it('wraps selector in :global(...) modifier', async () => { + const template = ``; + const opts = autoProcess(); + const preprocessed = await preprocess(template, opts); + expect(preprocessed.toString()).toContain( + `:global(div){color:red}:global(.test){}`, + ); + }); + + it('wraps selector in :global(...) modifier only inside the rule', async () => { + const template = ``; + const opts = autoProcess(); + const preprocessed = await preprocess(template, opts); + expect(preprocessed.toString()).toContain( + `:global(div){color:red}.test{}`, + ); + }); + + it('wraps selector in :global(...) only if needed', async () => { + const template = ``; + const opts = autoProcess(); + const preprocessed = await preprocess(template, opts); + expect(preprocessed.toString()).toContain( + `:global(.test){}:global(.foo){}`, + ); + }); + + it("prefixes @keyframes names with '-global-' only if needed", async () => { + const template = ``; + const opts = autoProcess(); + const preprocessed = await preprocess(template, opts); + expect(preprocessed.toString()).toContain( + `@keyframes -global-a {from{} to{}}@keyframes -global-b {from{} to{}}`, + ); + }); +});