diff --git a/README.md b/README.md index 642a059..744d32e 100644 --- a/README.md +++ b/README.md @@ -137,6 +137,21 @@ The template points PHP tooling at `.ddev/drupal-code-quality/tooling/bin` and J - ESLint config mode: - `ESLINT_CONFIG_MODE=nearest` (default) groups by nearest config file. - `ESLINT_CONFIG_MODE=fixed` forces `.eslintrc.passing.json`. +- ESLint warning visibility (GitLab CI parity): + - `DCQ_ESLINT_QUIET=1` (default) adds `--quiet` to `ddev eslint` and + `ddev eslint-fix`, so warnings are suppressed. + - Set `DCQ_ESLINT_QUIET=0` to include warnings in CLI output. Persist this in + `.ddev/config.yaml` (or `.ddev/config.yml`): + ```yaml + web_environment: + - DCQ_ESLINT_QUIET=0 + ``` + - VS Code uses its own setting for extension diagnostics. Set + `"eslint.quiet": false` in `.vscode/settings.json` to include warnings in + the IDE. + - Installer behavior: `overwrite` regenerates IDE settings from template; + `merge` only adds missing keys and will not change an existing + `eslint.quiet` value. - CSpell parity: - Run `ddev exec php /mnt/ddev_config/drupal-code-quality/tooling/scripts/prepare-cspell.php -s .prepared` once and replace `.cspell.json` after reviewing the diff. @@ -161,7 +176,7 @@ The template points PHP tooling at `.ddev/drupal-code-quality/tooling/bin` and J - If no project `phpstan.neon*` exists, the wrapper uses the GitLab template config shipped with the add-on. - PHPStan level: -- GitLab CI template defaults use level 0. The installer can set a local default level (0-10). + - GitLab CI template defaults use level 0. The installer can set a local default level (0-10). ## Installer environment variables @@ -169,6 +184,9 @@ The template points PHP tooling at `.ddev/drupal-code-quality/tooling/bin` and J - `DCQ_NONINTERACTIVE=true`: disable prompts; if no overrides are set, applies the recommended settings automatically. - `DCQ_PHPSTAN_LEVEL`: set `phpstan.neon` level (0-10) without prompting. +- `DCQ_ESLINT_QUIET`: `1`/unset to suppress ESLint warnings by default + (GitLab CI parity), `0` to include warnings. This can be set in + `.ddev/config.yaml` under `web_environment`. - `DCQ_INSTALL_DEPS`: `install`/`true` to auto-install missing `drupal/core-dev`, `skip`/`false` to skip, or unset to prompt when interactive. - `DCQ_INSTALL_NODE_DEPS`: `root` to install JS deps in the project root (creates diff --git a/commands/web/eslint b/commands/web/eslint index 4a6856f..98ba236 100755 --- a/commands/web/eslint +++ b/commands/web/eslint @@ -9,6 +9,9 @@ print_help() { Usage: ddev eslint [args] Runs ESLint inside the DDEV web container with Drupal.org GitLab CI template defaults. + +Defaults: + - Uses --quiet (GitLab CI parity). Set DCQ_ESLINT_QUIET=0 to include warnings. USAGE } @@ -28,6 +31,11 @@ NODE_PATH="" RESOLVE_PLUGINS_DIR="" ESLINT_TOOLCHAIN="${ESLINT_TOOLCHAIN:-auto}" ESLINT_CONFIG_MODE="${ESLINT_CONFIG_MODE:-nearest}" +DCQ_ESLINT_QUIET="${DCQ_ESLINT_QUIET:-1}" +QUIET_ENABLED=1 +case "$DCQ_ESLINT_QUIET" in + 0|false|FALSE|False|no|NO|off|OFF) QUIET_ENABLED=0 ;; +esac for arg in "$@"; do if [ "$seen_double_dash" = true ]; then @@ -110,7 +118,11 @@ if [ "$has_version" = true ]; then exit $? fi -CMD=(env "NODE_PATH=$NODE_PATH" node "$TOOLCHAIN_BIN" --quiet) +CMD=(env "NODE_PATH=$NODE_PATH" node "$TOOLCHAIN_BIN") +DEFAULT_ARGS=() +if [ "$QUIET_ENABLED" -eq 1 ]; then + DEFAULT_ARGS+=(--quiet) +fi if [ -n "$RESOLVE_PLUGINS_DIR" ]; then # Ensure ESLint resolves plugins from the selected toolchain. CMD+=(--resolve-plugins-relative-to "$RESOLVE_PLUGINS_DIR") @@ -125,6 +137,7 @@ if [ "$has_config" = false ] && [ "$ESLINT_CONFIG_MODE" != "nearest" ]; then fi fi CMD+=(--ext .js,.yml --ignore-pattern "**/node_modules/**") +CMD+=("${DEFAULT_ARGS[@]}") find_nearest_config() { local file_path="$1" @@ -312,11 +325,12 @@ if [ "$ESLINT_CONFIG_MODE" = "nearest" ]; then config_dir="$(dirname "$config_path")" plugins_dir="$(resolve_plugins_dir "$config_dir")" echo "ESLint nearest-mode: config ${config_path} (${#group_files[@]} file(s))." >&2 - group_cmd=(env "NODE_PATH=$NODE_PATH" node "$TOOLCHAIN_BIN" --quiet --config="$config_path") + group_cmd=(env "NODE_PATH=$NODE_PATH" node "$TOOLCHAIN_BIN" --config="$config_path") if [ -n "$plugins_dir" ]; then group_cmd+=(--resolve-plugins-relative-to "$plugins_dir") fi group_cmd+=(--ext .js,.yml --ignore-pattern "**/node_modules/**") + group_cmd+=("${DEFAULT_ARGS[@]}") "${group_cmd[@]}" "${FLAGS_ARGS[@]}" "${filtered_files[@]}" status=$? if [ $status -ne 0 ]; then diff --git a/commands/web/eslint-fix b/commands/web/eslint-fix index 1b4016a..bd76440 100755 --- a/commands/web/eslint-fix +++ b/commands/web/eslint-fix @@ -11,6 +11,9 @@ Usage: ddev eslint-fix [args] Applies ESLint fixes directly to files (matches standard eslint --fix behavior). +Defaults: + - Uses --quiet (GitLab CI parity). Set DCQ_ESLINT_QUIET=0 to include warnings. + Use --preview to see a patch preview and confirm before applying fixes. This generates a patch in dcq-reports/ and prompts for confirmation. @@ -31,6 +34,11 @@ preview_mode=false clean_args=() ESLINT_TOOLCHAIN="${ESLINT_TOOLCHAIN:-auto}" ESLINT_CONFIG_MODE="${ESLINT_CONFIG_MODE:-nearest}" +DCQ_ESLINT_QUIET="${DCQ_ESLINT_QUIET:-1}" +QUIET_ENABLED=1 +case "$DCQ_ESLINT_QUIET" in + 0|false|FALSE|False|no|NO|off|OFF) QUIET_ENABLED=0 ;; +esac for arg in "$@"; do if [ "$arg" = "--preview" ]; then @@ -191,7 +199,11 @@ else done fi -CMD_BASE=(env "NODE_PATH=$NODE_PATH" node "$TOOLCHAIN_BIN" --quiet) +CMD_BASE=(env "NODE_PATH=$NODE_PATH" node "$TOOLCHAIN_BIN") +DEFAULT_ARGS=() +if [ "$QUIET_ENABLED" -eq 1 ]; then + DEFAULT_ARGS+=(--quiet) +fi if [ -n "$RESOLVE_PLUGINS_DIR" ]; then CMD_BASE+=(--resolve-plugins-relative-to "$RESOLVE_PLUGINS_DIR") fi @@ -204,6 +216,7 @@ if [ "$has_config" = false ] && [ "$ESLINT_CONFIG_MODE" != "nearest" ]; then fi fi CMD_BASE+=(--ext .js,.yml --ignore-pattern "**/node_modules/**") +CMD_BASE+=("${DEFAULT_ARGS[@]}") find_nearest_config() { local file_path="$1" @@ -410,11 +423,12 @@ if [ "$preview_mode" = true ]; then config_dir="$(dirname "$config_path_abs")" plugins_dir="$(resolve_plugins_dir "$config_dir")" echo "ESLint-fix preview: config ${config_path_abs} (${#filtered_files[@]} file(s))." >&2 - group_cmd=(env "NODE_PATH=$NODE_PATH" node "$TOOLCHAIN_BIN" --quiet --config="$config_path_run") + group_cmd=(env "NODE_PATH=$NODE_PATH" node "$TOOLCHAIN_BIN" --config="$config_path_run") if [ -n "$plugins_dir" ]; then group_cmd+=(--resolve-plugins-relative-to "$plugins_dir") fi group_cmd+=(--ext .js,.yml --ignore-pattern "**/node_modules/**" --fix) + group_cmd+=("${DEFAULT_ARGS[@]}") (cd "$tmp_root" && "${group_cmd[@]}" "${filtered_files[@]}") status=$? if [ $status -ne 0 ]; then @@ -496,11 +510,12 @@ if [ "$preview_mode" = true ]; then config_dir="$(dirname "$config_path_abs")" plugins_dir="$(resolve_plugins_dir "$config_dir")" echo "Applying ESLint fixes: config ${config_path_abs} (${#filtered_files[@]} file(s))." >&2 - group_cmd=(env "NODE_PATH=$NODE_PATH" node "$TOOLCHAIN_BIN" --quiet --config="$config_path_run") + group_cmd=(env "NODE_PATH=$NODE_PATH" node "$TOOLCHAIN_BIN" --config="$config_path_run") if [ -n "$plugins_dir" ]; then group_cmd+=(--resolve-plugins-relative-to "$plugins_dir") fi group_cmd+=(--ext .js,.yml --ignore-pattern "**/node_modules/**" --fix) + group_cmd+=("${DEFAULT_ARGS[@]}") "${group_cmd[@]}" "${filtered_files[@]}" status=$? if [ $status -ne 0 ]; then @@ -567,11 +582,12 @@ if [ "$ESLINT_CONFIG_MODE" = "nearest" ]; then fi config_dir="$(dirname "$config_path_abs")" plugins_dir="$(resolve_plugins_dir "$config_dir")" - group_cmd=(env "NODE_PATH=$NODE_PATH" node "$TOOLCHAIN_BIN" --quiet --config="$config_path_run") + group_cmd=(env "NODE_PATH=$NODE_PATH" node "$TOOLCHAIN_BIN" --config="$config_path_run") if [ -n "$plugins_dir" ]; then group_cmd+=(--resolve-plugins-relative-to "$plugins_dir") fi group_cmd+=(--ext .js,.yml --ignore-pattern "**/node_modules/**" --fix) + group_cmd+=("${DEFAULT_ARGS[@]}") "${group_cmd[@]}" "${filtered_files[@]}" status=$? if [ $status -ne 0 ]; then diff --git a/dcq-install.sh b/dcq-install.sh index 0d9d55f..ae761e3 100644 --- a/dcq-install.sh +++ b/dcq-install.sh @@ -1074,6 +1074,40 @@ escape_sed_replacement() { printf '%s' "${1:-}" | sed 's/[&|]/\\&/g' } +eslint_quiet_disabled() { + case "${1:-}" in + 0|false|FALSE|False|no|NO|off|OFF) + return 0 + ;; + esac + return 1 +} + +resolve_eslint_quiet_setting() { + local app_root="$1" + local raw="${DCQ_ESLINT_QUIET:-}" + local config_file="" + local matched_line="" + + if [ -z "$raw" ]; then + for config_file in "${app_root%/}/.ddev/config.yaml" "${app_root%/}/.ddev/config.yml"; do + [ -f "$config_file" ] || continue + matched_line="$(grep -E '^[[:space:]-]*["'"'"']?DCQ_ESLINT_QUIET=' "$config_file" | tail -n 1 || true)" + [ -n "$matched_line" ] || continue + raw="${matched_line#*=}" + raw="${raw%%#*}" + raw="$(printf '%s' "$raw" | tr -d "\"'" | sed 's/^[[:space:]]*//; s/[[:space:]]*$//')" + [ -n "$raw" ] && break + done + fi + + if eslint_quiet_disabled "$raw"; then + printf 'false' + return + fi + printf 'true' +} + render_ide_template() { # Render IDE settings template with resolved shim and tool paths. local template="$1" @@ -1083,17 +1117,20 @@ render_ide_template() { local prettier_path="$5" local eslint_node_path="$6" local eslint_resolve_plugins="$7" + local eslint_quiet="$8" local escaped_shim local escaped_stylelint local escaped_prettier local escaped_node_path local escaped_resolve_plugins + local escaped_eslint_quiet escaped_shim="$(escape_sed_replacement "$shim_setting")" escaped_stylelint="$(escape_sed_replacement "$stylelint_path")" escaped_prettier="$(escape_sed_replacement "$prettier_path")" escaped_node_path="$(escape_sed_replacement "$eslint_node_path")" escaped_resolve_plugins="$(escape_sed_replacement "$eslint_resolve_plugins")" + escaped_eslint_quiet="$(escape_sed_replacement "$eslint_quiet")" sed \ -e '1{/^#ddev-generated$/d;}' \ @@ -1102,6 +1139,7 @@ render_ide_template() { -e "s|__DCQ_PRETTIER_PATH__|${escaped_prettier}|g" \ -e "s|__DCQ_ESLINT_NODE_PATH__|${escaped_node_path}|g" \ -e "s|__DCQ_ESLINT_RESOLVE_PLUGINS__|${escaped_resolve_plugins}|g" \ + -e "s|__DCQ_ESLINT_QUIET__|${escaped_eslint_quiet}|g" \ "$template" >"$output" } @@ -2190,6 +2228,7 @@ if [ -f "$ide_settings_template" ] || [ -f "$ide_extensions_template" ]; then js_modules="" eslint_node_path="" eslint_resolve_plugins="" + eslint_quiet="$(resolve_eslint_quiet_setting "$app_root")" if [ "$ide_node_mode" = "root" ] && [ "$has_root_node_modules" -eq 1 ]; then js_modules="./node_modules" eslint_node_path="node_modules" @@ -2205,7 +2244,7 @@ if [ -f "$ide_settings_template" ] || [ -f "$ide_extensions_template" ]; then fi render_ide_template "$ide_settings_template" "$ide_tmp" "$shim_setting" \ - "$stylelint_path" "$prettier_path" "$eslint_node_path" "$eslint_resolve_plugins" + "$stylelint_path" "$prettier_path" "$eslint_node_path" "$eslint_resolve_plugins" "$eslint_quiet" if [ "$ide_js_paths_set" -eq 0 ]; then strip_ide_js_settings "$ide_tmp" || true emit 'JS tool paths not configured (node_modules missing). Install JS deps and re-run the installer or update settings manually.\n' diff --git a/drupal-code-quality/ide-settings/vscode/README.md b/drupal-code-quality/ide-settings/vscode/README.md index b32e4a1..1f193da 100644 --- a/drupal-code-quality/ide-settings/vscode/README.md +++ b/drupal-code-quality/ide-settings/vscode/README.md @@ -19,4 +19,14 @@ Stylelint, Prettier, CSpell) use local `node_modules` paths. The installer uses the Node toolchain choice (prompt or `DCQ_INSTALL_NODE_DEPS`) to configure those paths when dependencies are available. Override the paths if you prefer a different location. Run `ddev ` in the terminal to use the containerized -CLI wrappers. +CLI wrappers. The generated settings also set `eslint.quiet` by default to +match GitLab CI behavior; set `DCQ_ESLINT_QUIET=0` before install to disable. +To persist this in DDEV commands, add `DCQ_ESLINT_QUIET=0` under +`web_environment` in `.ddev/config.yaml`. + +For existing projects, update `.vscode/settings.json` as well: +- Set `"eslint.quiet": false` to show warnings in the IDE. +- Set `"eslint.quiet": true` to hide warnings in the IDE. + +Note: installer `merge` mode only adds missing settings. It does not overwrite +an existing `eslint.quiet` value. diff --git a/drupal-code-quality/ide-settings/vscode/settings.json b/drupal-code-quality/ide-settings/vscode/settings.json index 4ec851d..3dfc099 100644 --- a/drupal-code-quality/ide-settings/vscode/settings.json +++ b/drupal-code-quality/ide-settings/vscode/settings.json @@ -14,6 +14,7 @@ "phpstan.configFile": "./phpstan.neon", "intelephense.environment.phpVersion": "8.3", "eslint.enable": true, + "eslint.quiet": __DCQ_ESLINT_QUIET__, "eslint.workingDirectories": [ { "directory": ".", diff --git a/tests/test.bats b/tests/test.bats index 3ede975..b8d2101 100644 --- a/tests/test.bats +++ b/tests/test.bats @@ -818,6 +818,7 @@ with open(path, encoding="utf-8") as fh: assert data.get("dcq.customSetting") == "keep" assert data.get("eslint.nodePath") == "custom" +assert data.get("eslint.quiet") is True PY assert_success @@ -899,12 +900,29 @@ PY assert_failure run grep -q '"eslint.options"' ".vscode/settings.json" assert_failure + run grep -q '"eslint.quiet": true' ".vscode/settings.json" + assert_success run grep -q '"stylelint.stylelintPath"' ".vscode/settings.json" assert_failure run grep -q '"prettier.prettierPath"' ".vscode/settings.json" assert_failure } +@test "VS Code settings respect DCQ_ESLINT_QUIET override" { + set -u -o pipefail + export DCQ_INSTALL_DEPS=skip + export DCQ_INSTALL_NODE_DEPS=skip + export DCQ_INSTALL_IDE_SETTINGS=overwrite + export DCQ_ESLINT_QUIET=0 + + run ddev add-on get "${DIR}" + assert_success + + assert_file_exist ".vscode/settings.json" + run grep -q '"eslint.quiet": false' ".vscode/settings.json" + assert_success +} + @test "path map prefers DDEV_HOST_PROJECT_ROOT" { set -u -o pipefail run ddev add-on get "${DIR}" @@ -1161,6 +1179,7 @@ PY assert_log_contains '"stylelint.stylelintPath": "./node_modules/stylelint"' ".vscode/settings.json" assert_log_contains '"prettier.prettierPath": "./node_modules/prettier"' ".vscode/settings.json" assert_log_contains '"eslint.nodePath": "node_modules"' ".vscode/settings.json" + assert_log_contains '"eslint.quiet": true' ".vscode/settings.json" assert_log_contains '"resolvePluginsRelativeTo": "."' ".vscode/settings.json" run ./.ddev/drupal-code-quality/tooling/bin/phpstan --version