@adguard/scriptlets
v2.0.1
Published
AdGuard's JavaScript library of Scriptlets and Redirect resources
Downloads
4,246
Readme
AdGuard Scriptlets and Redirect Resources
AdGuard's Scriptlets and Redirect resources library which provides extended capabilities for content blocking.
- Scriptlets
- Redirect resources
- Development
- Usage
- API description
- Browser compatibility
- Projects using Scriptlets
Scriptlets
Scriptlet is a JavaScript function which can be used in a declarative manner in AdGuard filtering rules.
AdGuard supports a lot of different scriptlets. Please note, that in order to achieve cross-blocker compatibility, we also support syntax of uBO and ABP.
Syntax
Blocking rules
[domains]#%#//scriptlet(name[, arguments])
domains
— optional, a list of domains where the rule should be applied;name
— required, a name of the scriptlet from AdGuard Scriptlets library;arguments
— optional, a list ofstring
arguments (no other types of arguments are supported).
Remarks
The meaning of the arguments depends on the scriptlet.
Special characters in scriptlet argument must be escaped properly:
- valid:
'prop["nested"]'
"prop['nested']"
'prop[\'nested\']'
"prop[\"nested\"]"
- not valid:
'prop['nested']'
"prop["nested"]"
Scriptlet
name
and each of thearguments
should be wrapped in quotes. You can use either single or double quotes for the scriptlet name and arguments. Single quote is recommended but not for cases when its usage makes readability worse, e.g.".css('display','block');"
is more preferred then'.css(\'display\',\'block\');'
.
Exception rules
[domains]#@%#//scriptlet([name[, arguments]])
domains
— optional, a list of domains where the rule should be applied;name
— optional, a name of the scriptlet to except from the applying; if not set, all scriptlets will not be applied;arguments
— optional, a list ofstring
arguments to match the same blocking rule and disable it.
Examples
Apply the
abort-on-property-read
scriptlet on all pages ofexample.org
and its subdomains, and passes one argument to it (alert
):example.org#%#//scriptlet('abort-on-property-read', 'alert')
Remove the
branding
class from alldiv[class^="inner"]
elements on all pages ofexample.org
and its subdomains:example.org#%#//scriptlet('remove-class', 'branding', 'div[class^="inner"]')
Apply
set-constant
andset-cookie
on any webpage, but because of specific scriptlet exception rule onlyset-constant
scriptlet will be applied onexample.org
and its subdomains:#%#//scriptlet('set-constant', 'adList', 'emptyArr') #%#//scriptlet('set-cookie', 'accepted', 'true') example.org#@%#//scriptlet('set-cookie')
Apply
adjust-setInterval
on any webpage, andset-local-storage-item
on all pages ofexample.com
and its subdomains, but there is also multiple scriptlet exception rule, so no scriptlet rules will be applied onexample.com
and its subdomains:#%#//scriptlet('adjust-setInterval', 'count', '*', '0.001') example.com#%#//scriptlet('set-local-storage-item', 'ALLOW_COOKIES', 'false') example.com#@%#//scriptlet()
Trusted scriptlets
Trusted scriptlets are scriptlets with extended functionality.
Their names are prefixed with trusted-
, e.g trusted-click-element
,
to be easily distinguished from common scriptlets.
Restriction
Trusted scriptlets application must be restricted due to dangerous nature of their capabilities. Allowed sources of trusted scriptlets are:
- filters created by AdGuard Team,
- custom filters which were installed as
trusted
, - user rules.
Trusted scriptlets has no compatibility table as they are not compatible with any other blocker.
Redirect resources
AdGuard is able to redirect web requests to a local "resource".
Syntax
AdGuard uses the same filtering rule syntax as uBlock Origin.
Also, it is compatible with ABP $rewrite=abp-resource
modifier.
$redirect
is a modifier for the basic filtering rules
so rules with this modifier support all other basic modifiers like $domain
, $third-party
, $script
, etc.
The value of the $redirect
modifier must be the name of the resource that will be used for redirection.
See the list of available redirect resources.
Priority of
$redirect
rules is described in the Knowledge Base.
Examples
||example.org/script.js$script,redirect=noopjs
— redirects all requests toscript.js
to the resource namednoopjs
.||example.org/test.mp4$media,redirect=noopmp4-1s
— requests toexample.org/test.mp4
will be redirected to the resource namednoopmp4-1s
.
uBlock Origin specifies additional resource name
none
that can disable other redirect rules. AdGuard does not support it, use$badfilter
to disable specific rules.
Development
How to build
Install dependencies:
pnpm install
Build dist:
pnpm build
In tsurlfilter directory install and link dependencies, link scriptlets, move into package and build, and create tsurlfilter link.
lerna bootstrap
pnpm link --global "@adguard/scriptlets"
cd ./packages/tsurlfilter
pnpm build
yarn link
In extension directory install dependencies, link packages and build
yarn
yarn link @adguard/scriptlets
yarn link @adguard/tsurlfilter
yarn dev
How to test
Some tests are run in QUnit, some in Jest.
Run all tests:
pnpm test
QUnit is used for testing of scriptlets, redirects, and helpers:
pnpm test:qunit [scriptlets | redirects | helpers]
For scriptlets and redirects test run can be more specific:
// node test run pnpm test:qunit scriptlets --name set-cookie pnpm test:qunit redirects --name ati-smarttag // gui test run pnpm test:qunit scriptlets --name set-cookie --gui pnpm test:qunit redirects --name ati-smarttag --gui
For debugging purposes after some test is running in gui mode, you may change your scriptlet/redirect code, and without stopping the server run in new terminal:
pnpm test:qunit scriptlets --name set-cookie --build
Run all jest tests:
pnpm test:jest
or limit the testing —
testRegex
may be specified injest.config.js
or specify test name in command line, e.g.:pnpm test:jest -t isValidScriptletRule
To run browserstack tests create .env
file or copy and rename .env-example
.
Fill in <username>
and <key>
with data from your Browserstack profile.
Run next command:
pnpm browserstack
Tests run by jest
should be named .spec.js
, so they will be not included in the QUnit
tests.
Debugging
Use debugger;
statement where you need it, run
pnpm test
and open needed HTML file from tests/dist
in your browser with devtools
How to link packages
Scriptlets library relies on external packages, such as @adguard/agtree
.
During development, you might need to link these packages using pnpm link
.
How to update wiki
There are two scripts to update wiki:
pnpm wiki:build-table
— checks compatibility data updates and updates the compatibility table. Should be run manually while the release preparation.pnpm wiki:build-docs
— updates wiki pagesabout-scriptlets.md
andabout-redirects.md
. They are being generated from JSDoc-type comments of corresponding scriptlets and redirects source files due to@scriptlet
/@redirect
and@description
tags. Runs automatically while the release build.
Usage
CoreLibs
For CoreLibs usage you should use dist/scriptlets.corelibs.json
and dist/redirects.json
.
File example:
{
"version": "1.0.0",
"scriptlets": [
{
"names": [
"abort-on-property-read",
"ubo-abort-on-property-read.js",
"abp-abort-on-property-read"
],
"scriptlet": "function() { ...code... }"
}
]
}
Schema:
{
"type": "object",
"properties": {
"version": {
"type": "string"
},
"scriptlets": {
"type": "array",
"items": {
"type": "object",
"properties": {
"names": {
"type": "array",
"items": {
"type": "string"
}
},
"scriptlet": {
"type": "string"
}
}
}
}
}
}
NPM module
Installation
You can install the library using
- Yarn:
yarn add @adguard/scriptlets
- NPM:
npm install @adguard/scriptlets
- PNPM:
pnpm add @adguard/scriptlets
API description
Scriptlets API
You are welcome to use scriptlets as a CJS modules or ESM modules:
const { scriptlets } = require('@adguard/scriptlets');
// or
import { scriptlets } from '@adguard/scriptlets';
invoke()
/**
* Returns scriptlet code by `source`.
*
* @param source Scriptlet properties.
*
* @returns Scriptlet code.
* @throws An error on unknown scriptlet name.
*/
declare function getScriptletCode(source: Source): string;
declare const scriptlets: {
invoke: typeof getScriptletCode;
};
where Source
is:
interface Source {
/**
* Scriptlet name
*/
name: string;
/**
* Arguments for scriptlet function
*/
args: string[];
/**
* {'extension'|'corelibs'} engine Defines the final form of scriptlet string presentation
*/
engine: string;
/**
* Version
*/
version: string;
/**
* flag to enable printing to console debug information
*/
verbose: boolean;
/**
* Source rule text is used for debugging purposes.
*
* @deprecated since it is not used in the code anymore.
*/
ruleText?: string;
/**
* Domain name, used to improve logging
*/
domainName?: string;
/**
* Optional unique identifier for a scriptlet instance.
*
* This identifier is used to prevent multiple executions of the same scriptlet on the page.
* If provided, this `uniqueId` will be combined with the scriptlet's `name` and `args`
* to create a unique identifier for the scriptlet call. This identifier is
* stored in the `Window.prototype.toString` object to ensure the scriptlet
* is not executed more than once in the same context.
*
* By avoiding multiple executions, it helps in reducing redundant operations and
* potential side effects that might occur if the same scriptlet is called multiple times.
*
* If `uniqueId` is not specified, no such unique identifier is created, and the
* scriptlet can be called multiple times.
*/
uniqueId?: string;
}
getScriptletFunction()
/**
* Returns scriptlet function by `name`.
*
* @param {string} name Scriptlet name
*
* @returns {Function} — Scriptlet function.
*/
declare function getScriptletFunction(name: any): string;
declare const scriptlets: {
getScriptletFunction: typeof getScriptletFunction;
};
Properties
SCRIPTLETS_VERSION
type: string
Current version of scriptlets library.
Redirects API
You are welcome to use redirects as a CJS modules or ESM modules:
const { Redirects } = require('@adguard/scriptlets/redirects');
// or
import { Redirects, getRedirectFilename } from '@adguard/scriptlets/redirects';
Redirects class
import { Redirects } from '@adguard/scriptlets';
/**
* Converts rawYaml into JS object with sources titles used as keys
*/
const redirects = new Redirects(rawYaml)
where rawYaml
is a string with YAML content located in dist/redirects.yml
.
getRedirect()
/**
* Returns redirect source object by title
*/
const redirect = redirects.getRedirect('noopjs');
/**
* Redirect is an object with following props:
* {
* title: 1x1-transparent.gif
* comment: http://probablyprogramming.com/2009/03/15/the-tiniest-gif-ever
* contentType: image/gif;base64
* content: R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
* }
*/
isBlocking()
/**
* Check if redirect is blocking, e.g. click2load.html
*/
const isBlocking = redirect.isBlocking('click2load.html');
getRedirectFilename()
import { getRedirectFilename } from '@adguard/scriptlets/redirects';
/**
* For a given name or alias of redirect returns the corresponding filename
* @param name Name or alias of redirect
* @returns Redirect's filename with extension
*/
declare function getRedirectFilename(name: string): string;
Validators API
isValidScriptletName()
import { isValidScriptletName } from '@adguard/scriptlets/validators';
/**
* Checks whether the `name` is valid scriptlet name.
* Uses cache for better performance.
*
* @param name Scriptlet name.
* @returns True if scriptlet name is a valid one or an empty string,
* otherwise false.
*/
declare function isValidScriptletName(name: string | null): boolean;
isValidScriptletRule()
/**
* 1. For ADG scriptlet checks whether the scriptlet syntax and name are valid.
* 2. For UBO and ABP scriptlet first checks their compatibility with ADG
* by converting them into ADG syntax, and after that checks the name.
*
* ADG or UBO rules are "single-scriptlet", but ABP rule may contain more than one snippet
* so if at least one of them is not valid — whole `ruleText` rule is not valid too.
*
* @param rule Any scriptlet rule — ADG or UBO or ABP.
*
* @returns True if scriptlet name is valid in rule.
*/
declare function isValidScriptletRule(rule: string | ScriptletInjectionRule): boolean;
isAdgScriptletRule()
, isUboScriptletRule()
, isAbpSnippetRule()
/**
* Checks if the `rule` is AdGuard scriptlet rule
*
* @param rule - rule text
* @returns true if given rule is adg rule
*/
declare function isAdgScriptletRule(rule: string): boolean;
/**
* Checks if the `rule` is uBO scriptlet rule
*
* @param rule rule text
* @returns true if given rule is ubo rule
*/
declare function isUboScriptletRule(rule: string): boolean;
/**
* Checks if the `rule` is AdBlock Plus snippet
*
* @param rule rule text
* @returns true if given rule is abp rule
*/
declare function isAbpSnippetRule(rule: string): boolean;
isValidAdgRedirectRule()
/**
* Checks if the `rule` is **valid** AdGuard redirect resource rule
*
* @param rule - rule text
* @returns true if given rule is valid adg redirect
*/
declare function isValidAdgRedirectRule(rule: string): boolean
isRedirectResourceCompatibleWithAdg()
/**
* Checks if the specified redirect resource is compatible with AdGuard
*
* @param redirectName - Redirect resource name to check
* @returns - true if the redirect resource is compatible with AdGuard
*/
declare function isRedirectResourceCompatibleWithAdg(redirectName: string): boolean;
Converters API
import {
convertUboToAdg,
convertAbpToAdg,
convertScriptletToAdg,
convertAdgToUbo
} from '@adguard/scriptlets/converters';
convertUboToAdg()
/**
* Converts string of UBO scriptlet rule to AdGuard scriptlet rule
*
* @param rule UBO scriptlet rule
* @returns array with one AdGuard scriptlet rule
*
* @deprecated
*/
declare function convertUboToAdg(rule: string | ScriptletInjectionRule): string[];
Note that parameters in UBO rule should be separated by comma + space. Otherwise, the rule is not valid.
convertAbpToAdg()
/**
* Convert string of ABP snippet rule to AdGuard scriptlet rule
*
* @param rule ABP snippet rule
* @returns array of AdGuard scriptlet rules, one or few items depends on Abp-rule
*/
declare function convertAbpToAdg(rule: string | ScriptletInjectionRule): string[];
convertScriptletToAdg()
/**
* Converts any scriptlet rule into AdGuard syntax rule.
* Comments and non-scriptlet rules are returned without changes.
*
* @param rule Rule.
*
* @returns Array of AdGuard scriptlet rules: one array item for ADG and UBO or few items for ABP.
* For the ADG `rule` validates its syntax, and returns an empty array if it is invalid.
*/
declare function convertScriptletToAdg(rule: string | ScriptletInjectionRule): string[];
convertAdgToUbo()
/**
* Converts AdGuard scriptlet rule to UBO syntax.
*
* @param rule AdGuard scriptlet rule
* @returns UBO scriptlet rule
* or undefined if `rule` is not valid AdGuard scriptlet rule.
*/
declare function convertAdgToUbo(rule: string | ScriptletInjectionRule): string | undefined;
convertAdgRedirectToUbo()
/**
* Converts Adg redirect rule to Ubo one
* 1. Checks if there is Ubo analog for Adg rule
* 2. Parses the rule and checks if there are any source type modifiers which are required by Ubo
* and if there are no one we add it manually to the end.
* Source types are chosen according to redirect name
* e.g. ||ad.com^$redirect=<name>,important ->> ||ad.com^$redirect=<name>,important,script
* 3. Replaces Adg redirect name by Ubo analog
*
* Note: if adg redirect uses UBO's priority syntax, it will be lost on conversion, e.g:
* ||example.com$redirect=noopjs:99 => ||example.com$redirect=noop.js
*
* @param rule adg rule
* @returns converted ubo rule
* @throws on incompatible rule
*/
declare function convertAdgRedirectToUbo(rule: string): string;
Browser Compatibility
| Browser | Version | |-----------------------|:--------| | Chrome | ✅ 55 | | Firefox | ✅ 52 | | Edge | ✅ 15 | | Opera | ✅ 42 | | Safari | ✅ 13 | | Internet Explorer | ❌ |
Projects using Scriptlets
- CoreLibs (updates automatically)
- TSUrlFilter
- FiltersCompiler
(
tsurlfilter
's update might be required as well) - AdguardBrowserExtension
(
tsurlfilter
also should be updated) - AdguardForSafari (
adguard-resources
should be updated) - AdguardForiOS
(
tsurlfilter
should be updated inadvanced-adblocker-web-extension
)