(url: string, ...loaders: FeatureLoader[])
| 154 | } |
| 155 | |
| 156 | async function add(url: string, ...loaders: FeatureLoader[]): Promise<void> { |
| 157 | const id = getFeatureId(url); |
| 158 | |
| 159 | /* Feature filtering and running */ |
| 160 | const options = await globalReady; |
| 161 | |
| 162 | // Skip disabled features, unless the feature is private |
| 163 | if (isFeatureDisabled(options, id) && !isFeaturePrivate(id)) { |
| 164 | if (loaders.length === 0) { |
| 165 | // CSS-only https://github.com/refined-github/refined-github/issues/7944 |
| 166 | // GitHub cleans up the CSS disabling attributes after navigation. |
| 167 | // https://github.com/refined-github/refined-github/issues/8172 |
| 168 | do { |
| 169 | document.documentElement.setAttribute('rgh-OFF-' + id, ''); |
| 170 | log.info('↩️', 'Skipping', id); |
| 171 | } while (await oneEvent(document, 'turbo:render')); |
| 172 | } else { |
| 173 | log.info('↩️', 'Skipping', id); |
| 174 | } |
| 175 | |
| 176 | return; |
| 177 | } |
| 178 | |
| 179 | if (loaders.length === 0) { |
| 180 | // CSS-only |
| 181 | return; |
| 182 | } |
| 183 | |
| 184 | void asyncForEach(loaders, async loader => { |
| 185 | // Input defaults and validation |
| 186 | const { |
| 187 | shortcuts = {}, |
| 188 | asLongAs, |
| 189 | include, |
| 190 | exclude, |
| 191 | init, |
| 192 | awaitDomReady = false, |
| 193 | requiresToken = false, |
| 194 | deduplicate = false, |
| 195 | } = loader; |
| 196 | |
| 197 | if (include?.length === 0) { |
| 198 | throw new Error(`${id}: \`include\` cannot be an empty array, it means "run nowhere"`); |
| 199 | } |
| 200 | |
| 201 | let isFirstLoop = true; |
| 202 | do { |
| 203 | if (awaitDomReady) { |
| 204 | await domLoaded; |
| 205 | } |
| 206 | |
| 207 | if (isFirstLoop) { |
| 208 | isFirstLoop = false; |
| 209 | } else if (deduplicate !== false && elementExists(deduplicate)) { |
| 210 | continue; |
| 211 | } |
| 212 | |
| 213 | if (!await shouldFeatureRun({asLongAs, include, exclude})) { |
no test coverage detected