PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 4.9.2
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v4.9.2
4.9.2 4.9.1 4.9.0 4.8.2 4.8.1 4.8.0 4.7.0 4.6.2 4.6.1 4.6.0 4.5.6 4.5.5 4.5.4 4.5.3 4.5.2 4.5.1 4.5.0 4.4.1 4.4.0 3.3.4 3.4.0 3.4.1 3.4.2 3.5.0 3.5.1 All 200 releases
← All changes | includes/Utils/Enqueue.php +54 -3 4.5.24.9.2 View file →
@@ -63,8 +63,19 @@
63 63 if ( $is_js ) {
64 64 $dependencies = array_unique( array_merge( $asset_config['dependencies'], $dependencies ) );
65 65 }
66 66 $version = $asset_config['version'];
67 + } else {
68 + // webpack only emits `<name>.asset.php` (with its content hash) for JS
69 + // entries, so stylesheets used to fall through to the plugin version
70 + // and never cache-busted between builds — browsers kept serving a
71 + // stale CSS against freshly-changed markup. Fall back to the file's
72 + // mtime so any rebuilt asset gets a new URL.
73 + $asset_path = $this->dist_path( $filename );
74 +
75 + if ( file_exists( $asset_path ) ) {
76 + $version = (string) filemtime( $asset_path );
77 + }
67 78 }
68 79
69 80 return [
70 81 'url' => $url,
@@ -74,21 +85,61 @@
74 85 'args' => null !== $args ? $args : ( $is_js ? true : 'all' )
75 86 ];
76 87 }
77 88
89 + /**
90 + * Resolve the assets sub-folder for a file: webpack output lives under
91 + * assets/build/, committed static assets under assets/static/. Prefer the
92 + * build copy when present, otherwise fall back to static. This lets every
93 + * enqueue/register call use the same relative path regardless of which
94 + * folder the file ends up in.
95 + *
96 + * The final `assets/` fallback matters for the Free/Pro version skew this
97 + * class is shared across. Pro constructs this same Enqueue against its OWN
98 + * plugin path, so when Free 4.8.0 runs alongside a Pro that predates the
99 + * Node 24 restructure, Pro's files are still flat under assets/. Without
100 + * this branch every one of them resolved to a non-existent assets/static/
101 + * path and 404'd. Ordering is deliberate: a matched pair never reaches the
102 + * legacy branch, so this costs nothing in the normal case.
103 + *
104 + * Kept `protected` so a paired plugin can refine the search order.
105 + */
106 + protected function asset_base( $filename ) {
107 + foreach ( [ 'assets/build/', 'assets/static/', 'assets/' ] as $base ) {
108 + if ( file_exists( path_join( $this->plugin_path, $base . $filename ) ) ) {
109 + return $base;
110 + }
111 + }
112 +
113 + // Nothing on disk — keep the historical default so a genuinely missing
114 + // file produces the same (broken) URL it always did, not a new shape.
115 + return 'assets/static/';
116 + }
117 +
78 118 public function asset_url( $filename ) {
79 119 if ( filter_var( $filename, FILTER_VALIDATE_URL ) ) {
80 120 return $filename;
81 121 }
82 122
83 - return esc_url( $this->plugin_url . 'assets/' . $filename );
123 + return esc_url( $this->plugin_url . $this->asset_base( $filename ) . $filename );
84 124 }
85 125
126 + /**
127 + * Explicit URL for a committed static asset under assets/static/.
128 + */
129 + public function static_url( $filename ) {
130 + if ( filter_var( $filename, FILTER_VALIDATE_URL ) ) {
131 + return $filename;
132 + }
133 +
134 + return esc_url( $this->plugin_url . 'assets/static/' . $filename );
135 + }
136 +
86 137 public function dist_path( $file ) {
87 - return path_join( $this->plugin_path, 'assets/' . $file );
138 + return path_join( $this->plugin_path, $this->asset_base( $file ) . $file );
88 139 }
89 140
90 141 public function icon( $name, $is_admin = false ) {
91 - $_path = $is_admin ? 'assets/admin/images/' : 'assets/images/';
142 + $_path = $is_admin ? 'assets/static/admin/images/' : 'assets/static/images/';
92 143 return esc_url( $this->plugin_url . $_path . $name . '?v=' . $this->version );
93 144 }
94 145 }