From b1b00ab4dca34e078bd07469fe664d7fb452c53f Mon Sep 17 00:00:00 2001 From: Artur Arseniev Date: Tue, 7 Apr 2026 14:02:06 +0400 Subject: [PATCH] Add allowEmpty to getCss (#6742) --- docs/api/editor.md | 1 + .../src/code_manager/model/CssGenerator.ts | 7 +++- packages/core/src/editor/index.ts | 1 + packages/core/test/specs/grapesjs/index.ts | 32 +++++++++++++++++++ 4 files changed, 40 insertions(+), 1 deletion(-) diff --git a/docs/api/editor.md b/docs/api/editor.md index 35b30e20b..95c9e630f 100644 --- a/docs/api/editor.md +++ b/docs/api/editor.md @@ -175,6 +175,7 @@ Returns CSS built inside canvas * `opts.avoidProtected` **[Boolean][17]** Don't include protected CSS (optional, default `false`) * `opts.onlyMatched` **[Boolean][17]** Return only rules matched by the passed component. (optional, default `false`) * `opts.keepUnusedStyles` **[Boolean][17]** Force keep all defined rules. Toggle on in case output looks different inside/outside of the editor. (optional, default `false`) + * `opts.allowEmpty` **[Boolean][17]** Include rules with empty style declarations. (optional, default `false`) Returns **([String][18] | [Array][19]\)** CSS string or array of CssRules diff --git a/packages/core/src/code_manager/model/CssGenerator.ts b/packages/core/src/code_manager/model/CssGenerator.ts index ee98afc47..209aaeaed 100644 --- a/packages/core/src/code_manager/model/CssGenerator.ts +++ b/packages/core/src/code_manager/model/CssGenerator.ts @@ -29,6 +29,11 @@ export type CssGeneratorBuildOptions = { * Force keep all defined rules. Toggle on in case output looks different inside/outside of the editor. */ keepUnusedStyles?: boolean; + + /** + * Include rules with empty style declarations. + */ + allowEmpty?: boolean; rules?: CssRule[]; clearStyles?: boolean; }; @@ -172,7 +177,7 @@ export default class CssGenerator extends Model { }); if ((selectorStrNoAdd && found) || selectorsAdd || singleAtRule || !model) { - const block = rule.getDeclaration(); + const block = rule.getDeclaration({ allowEmpty: opts.allowEmpty }); block && (opts.json ? (result = rule) : (result += block)); } else { dump.push(rule); diff --git a/packages/core/src/editor/index.ts b/packages/core/src/editor/index.ts index 24fb7a9b3..f92bc836b 100644 --- a/packages/core/src/editor/index.ts +++ b/packages/core/src/editor/index.ts @@ -256,6 +256,7 @@ export default class Editor implements IBaseModule { * @param {Boolean} [opts.avoidProtected=false] Don't include protected CSS * @param {Boolean} [opts.onlyMatched=false] Return only rules matched by the passed component. * @param {Boolean} [opts.keepUnusedStyles=false] Force keep all defined rules. Toggle on in case output looks different inside/outside of the editor. + * @param {Boolean} [opts.allowEmpty=false] Include rules with empty style declarations. * @returns {String|Array} CSS string or array of CssRules */ getCss(opts?: EditorModelParam<'getCss', 0>) { diff --git a/packages/core/test/specs/grapesjs/index.ts b/packages/core/test/specs/grapesjs/index.ts index 725308648..f28b05520 100644 --- a/packages/core/test/specs/grapesjs/index.ts +++ b/packages/core/test/specs/grapesjs/index.ts @@ -302,6 +302,38 @@ describe('GrapesJS', () => { expect(css).toEqual(`${protCss}.test2{color:red;}.test3{color:blue;}`); }); + test('Allow empty css rules option for getCSS method', () => { + config.components = '
'; + config.style = '.test-empty{}'; + const editor = grapesjs.init(config); + const protCss = editor.getConfig().protectedCss; + + expect(editor.getStyle().length).toEqual(1); + expect(editor.getCss()).toEqual(protCss); + expect(editor.getCss({ allowEmpty: true })).toEqual(`${protCss}.test-empty{}`); + }); + + test('Allow empty css rules option for media rules', () => { + config.components = '
'; + config.style = '@media (max-width: 992px){.test-empty{}}'; + const editor = grapesjs.init(config); + const protCss = editor.getConfig().protectedCss; + + expect(editor.getStyle().length).toEqual(1); + expect(editor.getCss({ allowEmpty: true })).toEqual(`${protCss}@media (max-width: 992px){.test-empty{}}`); + }); + + test('Allow empty css rules option with keepUnusedStyles', () => { + config.components = '
'; + config.style = '.test-empty{}'; + const editor = grapesjs.init(config); + const protCss = editor.getConfig().protectedCss; + + expect(editor.getStyle().length).toEqual(1); + expect(editor.getCss({ allowEmpty: true })).toEqual(protCss); + expect(editor.getCss({ allowEmpty: true, keepUnusedStyles: true })).toEqual(`${protCss}.test-empty{}`); + }); + test('Keep unused css classes/selectors option for media rules', () => { cssString = '.test2{color:red}.test3{color:blue} @media only screen and (max-width: 620px) { .notused { color: red; } } ';