Metadata Header & Directives
The // ==UserScript== comment block at the top of the entry file declares all app metadata. Every app uses the same directives, and every app is compiled before it is injected into the page.
| Directive | Syntax | Repeatable | Description |
|---|---|---|---|
@name | text | No (the last one wins if repeated) | App name shown in the manager list and IDE header. Terminal app info/enable/disable <name> also resolves apps by it, so renaming invalidates the old name immediately (prefer the bundleId in scripts). Synced both ways with the project name: edit @name and save to rename the project; renaming in the list rewrites @name |
@description | text | No (the last one wins if repeated) | Short summary of what the app does |
@match | match pattern | Yes | Pages where the app runs, Chrome match-pattern syntax (e.g. https://*.commerce.com/*). Defaults to <all_urls> when omitted. The reserved word background declares background execution (see "Execution target: background"). An invalid pattern fails the compile |
@include | glob | Yes | Narrows the run scope inside @match (e.g. *://*.commerce.com/bg*). Once declared, a page must hit @match and at least one @include; without @match it narrows <all_urls> |
@exclude | glob | Yes | Excludes pages by glob |
@exclude-match | match pattern | Yes | Excludes pages with match-pattern semantics. An invalid pattern fails the compile |
@run-at | enum | No (the last one wins if repeated) | Injection timing: document_start / document_end / document_idle (default). Tampermonkey-style hyphen values (e.g. document-start) and any letter case are accepted; an unrecognized value falls back to document_idle with a compile warning |
@require | library id | Yes | Enables a built-in: dom / store / http / model / user / network / agent (agent only works inside plugincmd commands). Ids are case-sensitive and must be lowercase. builtin:dom and quoted values are accepted; runtime needs no declaration (ignored if present). Unknown ids fail the compile; local files are not supported — use ESM import for local modules and npm packages |
@resource | alias + path | Yes | Aliases a local file; read it with LFRuntime.getResource(alias). See "@resource" below for the resolution rules |
@intercept | URL substring or /regex/ | Yes | Declares page-request interception. If the code uses LFRuntime.network.onResponse / interceptRequest / interceptResponse and the entry does not declare it, "Compile" fails |
Syntax cheat sheet (the #1 pitfall)
@match / @exclude-match take Chrome MATCH PATTERNS; @include / @exclude take GLOBS. A glob inside @match (e.g. *commerce*, https://commerce.*/*) fails the compile with the reason; a match pattern inside @include does not error but is matched literally as a glob and may never hit.
Parsing & validation
- Plugin builds read directives from the entry file only, within its first 120 lines; directives in any other file are ignored by plugin builds (for the one use of
@requirein a non-entry header, see "Scope of @require" below). The// ==UserScript==wrapper is optional and unknown directives (e.g.@version,@grant,@namespace,@noframes) are ignored - Only single-line
// @key valuecomments are recognized;/* */block comments and JSDoc-style* @namelines are not. After the first directive, the first non-comment line of code ends the header, and any directive written after it has no effect. A few leading statements such as"use strict"may come before the header (the compile checks, including the@interceptcheck, use the same parser as the runtime), but keeping the header at the very top of the file is still recommended - A directive needs a value: lines such as
// @run-ator// @matchwith nothing after them are treated as plain comments, and an empty value such as// @intercept ""counts as not declared - One value per line: several values on one line, separated by spaces or commas, are read as a single value (e.g.
// @require dom storefails the compile as an unknown library id). Repeat the directive on separate lines instead - If
@name/@description/@run-atis repeated, the last one wins - Directive names are case-insensitive; values of
@match/@include/@exclude/@exclude-match/@requireetc. may be quoted - The compiler rejects the following, so an app never ends up "compiled but never injected":
- an
@match/@exclude-matchthat is not a valid match pattern: the scheme must be lowercase*/http/https/file/ftp, a host*may only stand alone or lead as*., and a path (at least/) is required - an
@requireof a built-in that does not exist - an
@requireof a local file (e.g../lib.js) - an
@resourcepointing to a file that does not exist - use of
LFRuntime.network.onResponse/interceptRequest/interceptResponsewithout an@interceptdeclaration (liveDebug andrun --tabinject the intercept host automatically and skip this check, so if debugging works but Compile fails, look here first)
- an
- Without
@namethe project name is used; without@matchthe default is<all_urls>
Differences from Tampermonkey
- Only the directives on this page and the PluginX built-in libraries exist — there are no
GM_*APIs and nounsafeWindow, and directives like@granthave no effect. To read the page's own JS variables, useLFRuntime.runInMainWorld - Apps are injected into the top-level page only, never into iframes (no need for
@noframes) @includenarrows the@matchscope instead of adding pages (see "Match Rules In Depth")
Scope of @require
@require also decides which globals the compiler allows: if the code uses a library's global (e.g. LFHttp) without declaring its id in the entry, the compile fails. @require takes the lowercase library id (e.g. user), not the global name: with // @require LFUser or any other unknown id, the editor marks that line as an error and shows the correct form, and both the compile and terminal run fail.
Built-ins are injected into plugin builds (Compile, liveDebug, enabled / installed apps) only when the @require is in the entry file. An @require in the header of a non-entry file only applies when you run <that file> directly in the terminal: the editor shows a Warning on such lines, and other files cannot borrow it — using that library in another file is still flagged red. Declare every library your components and helper modules use in the entry.
// tools/probe.js — not the entry; only used via `run tools/probe.js` in the terminal
// @require http
const data = await LFHttp.json("https://open.er-api.com/v6/latest/USD");
LFRuntime.print(data.rates.CNY);@require dom only guarantees what LFDOM needs internally (such as LFDOM.waitForResponse); using LFNetwork directly in your code still requires an explicit // @require network.
@resource
Syntax: // @resource <alias> <path>, with whitespace between the alias and the path; the path may be quoted.
- The path is looked up as a workspace-absolute path first, then relative to the entry file; a missing file fails the compile
- CSS resources are injected into the page automatically when the app starts — no
LFRuntime.appendStylecall needed - For text resources (HTML, CSS, JSON …)
getResourcereturns the file contents; binary resources such as images come back as adata:URL; an unknown alias returns an empty string instead of throwing - The alias
compiledStyleis reserved: styles youimportin source are bundled into the artifact under that alias, so do not use it yourself
Complete example
Entry file header
// ==UserScript==
// @name Order Helper
// @description Add bulk actions to the orders page
// @match https://*.commerce.com/*
// @include *://*.commerce.com/bg*
// @exclude *://*.commerce.com/bg/login*
// @exclude-match https://static.commerce.com/*
// @run-at document_idle
// @require dom
// @require store
// @resource panel ./panel.html
// @intercept /api/orders
// ==/UserScript==