From b84deecb76b54907ddd3f8392a01e96adab937ee Mon Sep 17 00:00:00 2001 From: "vivi-the-going-merry[bot]" <308115520+vivi-the-going-merry[bot]@users.noreply.github.com> Date: Thu, 17 Sep 2026 12:41:20 -0600 Subject: [PATCH 1/4] Generate default formidableforms.css for stylelint coverage css/formidableforms.css was excluded from stylelint because it's rendered at runtime (FrmStylesController::load_css()), not a build artifact - there was nothing on disk to lint. Boot wp-env in the stylelint job and generate it from the default style settings via `wp eval-file` before linting, then drop the ignoreFiles exclusion. wp eval-file avoids admin-ajax.php's one-time onboarding-wizard admin_init redirect entirely (it's a CLI request, not an admin one). The generator also sets $_GET['flat'], matching the one real caller of load_css() (FrmStylesController::enqueue_css()) - without it, FrmStylesHelper::get_settings_for_output() takes a different branch that leaves several settings (e.g. text_color) unset, producing invalid CSS like `color: ;`. Refs Strategy11/formidable-pro#6684 Co-Authored-By: Claude Sonnet 5 --- .github/workflows/stylelint.yml | 11 +++++++ .stylelintrc.json | 3 +- tests/bin/generate-default-stylesheet.php | 36 +++++++++++++++++++++++ 3 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 tests/bin/generate-default-stylesheet.php diff --git a/.github/workflows/stylelint.yml b/.github/workflows/stylelint.yml index 3547c7d357..293f573069 100644 --- a/.github/workflows/stylelint.yml +++ b/.github/workflows/stylelint.yml @@ -43,5 +43,16 @@ jobs: if: steps.cache-npm.outputs.cache-hit != 'true' run: npm ci --include=dev --legacy-peer-deps + - name: Set up WP environment + run: npm run env start + + # css/formidableforms.css is a runtime-rendered stylesheet (see + # FrmStylesController::load_css()), not a build artifact, so there's + # nothing on disk for stylelint to check without this. Generated via + # `wp eval-file` (a CLI request) rather than admin-ajax.php so it never + # hits the plugin's one-time onboarding-wizard admin_init redirect. + - name: Generate default stylesheet for lint coverage + run: npx wp-env run cli wp eval-file tests/bin/generate-default-stylesheet.php + - name: Run Stylelint run: npx stylelint "**/*.{css,scss}" diff --git a/.stylelintrc.json b/.stylelintrc.json index 443983e4c1..f3aa49d4b7 100644 --- a/.stylelintrc.json +++ b/.stylelintrc.json @@ -9,8 +9,7 @@ "**/frm-legacy-paypal.css", "**/font_icons.css", "**/frm_testing_mode.css", - "**/welcome-tour.css", - "**/css/formidableforms.css" + "**/welcome-tour.css" ], "rules": { "no-descending-specificity": null, diff --git a/tests/bin/generate-default-stylesheet.php b/tests/bin/generate-default-stylesheet.php new file mode 100644 index 0000000000..747969f0d7 --- /dev/null +++ b/tests/bin/generate-default-stylesheet.php @@ -0,0 +1,36 @@ +get_defaults(); +$style = ''; + +ob_start(); +include FrmAppHelper::plugin_path() . '/css/_single_theme.css.php'; +$css = ob_get_clean(); + +$target = FrmAppHelper::plugin_path() . '/css/formidableforms.css'; +file_put_contents( $target, $css ); + +WP_CLI::success( "Generated $target (" . strlen( $css ) . ' bytes)' ); From 854e91cbccfcf665eee338eb838008f91322dc0f Mon Sep 17 00:00:00 2001 From: "vivi-the-going-merry[bot]" <308115520+vivi-the-going-merry[bot]@users.noreply.github.com> Date: Thu, 17 Sep 2026 12:46:33 -0600 Subject: [PATCH 2/4] Use FrmStyle::save_settings() to generate the default stylesheet The previous version hand-rolled a render of _single_theme.css.php only (the per-style block), which is missing the base stylesheet the issue actually asked for and that stylelint needs to check (custom_theme.css.php's own rules, floating labels, grids, media queries). FrmStyle::save_settings() is the plugin's own static-file generator for exactly this - the same method a real save of the Styles settings triggers - so call that instead of reimplementing its render path by hand. Also move the script from tests/bin/ to bin/: phpcs.xml, phpstan.neon, psalm.xml, rector.php, and mago.toml all skip bin/ entirely, while tests/ is in scope for at least mago and rector. bin/ keeps this CI-only utility script out of the analysis tools it isn't meant for, matching how bin/set-php-version.php is already handled. Co-Authored-By: Claude Sonnet 5 --- .github/workflows/stylelint.yml | 8 ++--- bin/generate-default-stylesheet.php | 17 +++++++++++ tests/bin/generate-default-stylesheet.php | 36 ----------------------- 3 files changed, 20 insertions(+), 41 deletions(-) create mode 100644 bin/generate-default-stylesheet.php delete mode 100644 tests/bin/generate-default-stylesheet.php diff --git a/.github/workflows/stylelint.yml b/.github/workflows/stylelint.yml index 293f573069..895e063044 100644 --- a/.github/workflows/stylelint.yml +++ b/.github/workflows/stylelint.yml @@ -47,12 +47,10 @@ jobs: run: npm run env start # css/formidableforms.css is a runtime-rendered stylesheet (see - # FrmStylesController::load_css()), not a build artifact, so there's - # nothing on disk for stylelint to check without this. Generated via - # `wp eval-file` (a CLI request) rather than admin-ajax.php so it never - # hits the plugin's one-time onboarding-wizard admin_init redirect. + # FrmStyle::save_settings()), not a build artifact, so there's nothing + # on disk for stylelint to check without this. - name: Generate default stylesheet for lint coverage - run: npx wp-env run cli wp eval-file tests/bin/generate-default-stylesheet.php + run: npx wp-env run cli wp eval-file bin/generate-default-stylesheet.php - name: Run Stylelint run: npx stylelint "**/*.{css,scss}" diff --git a/bin/generate-default-stylesheet.php b/bin/generate-default-stylesheet.php new file mode 100644 index 0000000000..4e94244e8e --- /dev/null +++ b/bin/generate-default-stylesheet.php @@ -0,0 +1,17 @@ +save_settings(); + +WP_CLI::success( 'Generated ' . FrmAppHelper::plugin_path() . '/css/formidableforms.css' ); diff --git a/tests/bin/generate-default-stylesheet.php b/tests/bin/generate-default-stylesheet.php deleted file mode 100644 index 747969f0d7..0000000000 --- a/tests/bin/generate-default-stylesheet.php +++ /dev/null @@ -1,36 +0,0 @@ -get_defaults(); -$style = ''; - -ob_start(); -include FrmAppHelper::plugin_path() . '/css/_single_theme.css.php'; -$css = ob_get_clean(); - -$target = FrmAppHelper::plugin_path() . '/css/formidableforms.css'; -file_put_contents( $target, $css ); - -WP_CLI::success( "Generated $target (" . strlen( $css ) . ' bytes)' ); From b0d9b3290ddf7d44acf11d878c84b9ae0ad8c90d Mon Sep 17 00:00:00 2001 From: "vivi-the-going-merry[bot]" <308115520+vivi-the-going-merry[bot]@users.noreply.github.com> Date: Thu, 17 Sep 2026 12:50:32 -0600 Subject: [PATCH 3/4] Fix wp-env invocation and verify the file actually lands on disk wp eval-file bin/generate-default-stylesheet.php failed in CI ("does not exist") - the cli container's working directory is the WordPress root, not this plugin's checkout, so a checkout-relative path doesn't resolve. Use `wp eval` with FrmAppHelper::plugin_path() (available once WP-CLI bootstraps the active plugin) to locate the script instead of guessing the mounted folder name. Also verify save_settings() actually wrote the file where stylelint will look before reporting success - it can resolve to the uploads dir instead of the plugin's own css/ folder depending on file-mod permissions, which would otherwise pass silently with nothing linted. Co-Authored-By: Claude Sonnet 5 --- .github/workflows/stylelint.yml | 2 +- bin/generate-default-stylesheet.php | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.github/workflows/stylelint.yml b/.github/workflows/stylelint.yml index 895e063044..d93d0fc6c0 100644 --- a/.github/workflows/stylelint.yml +++ b/.github/workflows/stylelint.yml @@ -50,7 +50,7 @@ jobs: # FrmStyle::save_settings()), not a build artifact, so there's nothing # on disk for stylelint to check without this. - name: Generate default stylesheet for lint coverage - run: npx wp-env run cli wp eval-file bin/generate-default-stylesheet.php + run: npx wp-env run cli wp eval 'require FrmAppHelper::plugin_path() . "/bin/generate-default-stylesheet.php";' - name: Run Stylelint run: npx stylelint "**/*.{css,scss}" diff --git a/bin/generate-default-stylesheet.php b/bin/generate-default-stylesheet.php index 4e94244e8e..20b585be00 100644 --- a/bin/generate-default-stylesheet.php +++ b/bin/generate-default-stylesheet.php @@ -5,7 +5,10 @@ * generator (FrmStyle::save_settings(), the same method a real save of the * Styles settings triggers) rather than reimplementing its render logic. * - * Usage: wp eval-file bin/generate-default-stylesheet.php + * Usage: wp eval 'require FrmAppHelper::plugin_path() . "/bin/generate-default-stylesheet.php";' + * (not `wp eval-file` directly - wp-env's cli container's working + * directory is the WordPress root, not this plugin's checkout, and the + * mounted plugin folder name isn't guaranteed.) */ if ( ! defined( 'WP_CLI' ) || ! WP_CLI ) { @@ -14,4 +17,14 @@ ( new FrmStyle() )->save_settings(); -WP_CLI::success( 'Generated ' . FrmAppHelper::plugin_path() . '/css/formidableforms.css' ); +// save_settings() writes wherever add_css_to_uploads_dir() resolves to, +// which can be outside this checkout (wp_upload_dir()) depending on file +// mod permissions - confirm the file actually landed where stylelint +// will look, rather than letting a silent miss pass as a green run. +$target = FrmStyle::get_generated_css_file_path( FrmStyle::add_css_to_uploads_dir() ) . '/' . FrmStylesController::get_file_name(); + +if ( ! is_file( $target ) || ! filesize( $target ) ) { + WP_CLI::error( "No stylesheet generated at $target" ); +} + +WP_CLI::success( "Generated $target" ); From 738e0be8dff3c63de0e991454a25c7d1d4d12437 Mon Sep 17 00:00:00 2001 From: "vivi-the-going-merry[bot]" <308115520+vivi-the-going-merry[bot]@users.noreply.github.com> Date: Fri, 18 Sep 2026 07:09:08 -0600 Subject: [PATCH 4/4] Silence formatting-only stylelint errors on the generated stylesheet, strip empty rules The 400 stylelint errors on this PR's own generated css/formidableforms.css are all cosmetic notation on the minified single-line output, except one real block-no-empty hit: a style-template selector whose only declarations sit behind a single `! empty( $defaults[...] )` check with no fallback (a font-family rule that only prints when a custom font is set) renders empty under the stock defaults - inert in real output, but indistinguishable from a broken selector to stylelint without evaluating the template's own PHP. - .stylelintrc.json: add an override for **/css/formidableforms.css disabling the six notation-only rules that only ever fire on minified, machine-generated output (rule/at-rule-empty-line-before, selector-attribute-quotes, selector-pseudo-element-colon-notation, color-hex-length, font-weight-notation, length-zero-no-unit). - bin/generate-default-stylesheet.php: strip empty rule blocks from the generated file after writing it, so block-no-empty stays enabled and meaningful for this file instead of also being disabled - a future genuinely-broken selector still gets caught. Verified against a synthetic minified fixture reproducing all seven error classes plus an empty block (stylelint + @wordpress/stylelint-config installed standalone, since this repo's own devDependencies aren't installed locally): 12 errors before the override, 0 after both changes. --- .stylelintrc.json | 16 +++++++++++++++- bin/generate-default-stylesheet.php | 16 ++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/.stylelintrc.json b/.stylelintrc.json index f3aa49d4b7..0f87a25fbf 100644 --- a/.stylelintrc.json +++ b/.stylelintrc.json @@ -31,5 +31,19 @@ "font-family-no-missing-generic-family-keyword": null, "scss/at-import-no-partial-leading-underscore": null, "scss/load-no-partial-leading-underscore": null - } + }, + "overrides": [ + { + "files": ["**/css/formidableforms.css"], + "rules": { + "rule-empty-line-before": null, + "at-rule-empty-line-before": null, + "selector-attribute-quotes": null, + "selector-pseudo-element-colon-notation": null, + "color-hex-length": null, + "font-weight-notation": null, + "length-zero-no-unit": null + } + } + ] } diff --git a/bin/generate-default-stylesheet.php b/bin/generate-default-stylesheet.php index 20b585be00..d751c27d96 100644 --- a/bin/generate-default-stylesheet.php +++ b/bin/generate-default-stylesheet.php @@ -27,4 +27,20 @@ WP_CLI::error( "No stylesheet generated at $target" ); } +// Several of the style templates gate a selector's only declarations behind +// a single `! empty( $defaults[...] )` check with no fallback (e.g. a +// font-family rule that only prints when a custom font is set) - under the +// stock defaults that selector renders with nothing between its braces. +// That's inert in real output either way, but stylelint's block-no-empty +// has no way to tell "false setting produced this on purpose" from "typo'd +// selector" without evaluating the template's PHP, so strip empty rules +// (including any block left empty once its only content is removed, e.g. a +// media query whose sole rule was itself emptied) before lint sees the file. +$css = file_get_contents( $target ); +do { + $before = $css; + $css = preg_replace( '/[^{}]*\{\}/', '', $css ); +} while ( $css !== $before ); +file_put_contents( $target, $css ); + WP_CLI::success( "Generated $target" );