Skip to content

Commit dac16bc

Browse files
authored
Restore the published develop type docs and exempt them from lint (#574)
1 parent 28fd62a commit dac16bc

11 files changed

Lines changed: 353 additions & 2 deletions

File tree

eslint.config.mjs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,11 +111,22 @@ export default [
111111
}
112112
},
113113
{
114-
// The public config and `extension/types` surface: its JSDoc is what users
114+
// The public config and `extension/types` surface, and the sources behind
115+
// the d.ts files extension-develop publishes: their JSDoc is what users
115116
// read on hover in their own extension.config.js and source files.
116117
files: [
117118
'programs/extension/config-types.ts',
118-
'programs/extension/types/**'
119+
'programs/extension/types/**',
120+
'programs/develop/types.ts',
121+
'programs/develop/command-preview.ts',
122+
'programs/develop/lib/build-summary.ts',
123+
'programs/develop/plugin-browsers/index.ts',
124+
'programs/develop/plugin-reload/index.ts',
125+
'programs/develop/plugin-reload/classify-reload.ts',
126+
'programs/develop/plugin-special-folders/folder-extensions/types.ts',
127+
'programs/develop/dev-server/control-bridge/contracts.ts',
128+
'programs/develop/dev-server/control-bridge/consumer-client.ts',
129+
'programs/develop/dev-server/control-bridge/logs-query.ts'
119130
],
120131
rules: {
121132
'local/no-file-header-comment': 'off',

programs/develop/command-preview.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ import type {CompanionExtensionsConfig} from './plugin-special-folders/folder-ex
4040
import {getSpecialFoldersDataForProjectRoot} from './plugin-special-folders/get-data'
4141
import type {BrowserConfig, PreviewOptions} from './types'
4242

43+
/**
44+
* Resolved browser launch options returned by extensionPreview.
45+
* The caller is responsible for actually launching the browser.
46+
*/
4347
export interface ResolvedPreviewOptions {
4448
browser: string
4549
outPath: string
@@ -67,6 +71,10 @@ export interface ResolvedPreviewOptions {
6771
logTab?: number | string
6872
}
6973

74+
/**
75+
* Browser launcher callback. When provided, extensionPreview calls it
76+
* instead of requiring plugin-browsers internally.
77+
*/
7078
export type PreviewLauncherFn = (opts: ResolvedPreviewOptions) => Promise<void>
7179

7280
function readRunIdFromReadyFile(readyPath: string): string | undefined {

programs/develop/dev-server/control-bridge/consumer-client.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,26 @@ export interface ReadyContractInfo {
2323
controlPort: number
2424
instanceId: string
2525
runId: string
26+
/** Ready-contract version, `ReadyMetadata['schemaVersion']` on current engines. */
2627
schemaVersion?: number
28+
/** Result-envelope capability advertisement, `1` on current engines. */
2729
schema?: number
2830
logsPath?: string
2931
status?: string
32+
/** Dev-server pid; absent in pre-4.1 contracts. */
3033
pid?: number
34+
/** Browser CDP port, stamped post-launch, may lag `status: 'ready'`. */
3135
cdpPort?: number
36+
/** Stamped when the launched browser exits while the server keeps running. */
3237
browserExitedAt?: string
3338
browserExitCode?: number
39+
/** When the compile finished (ISO), the meaning of `status: 'ready'`. */
3440
compiledAt?: string
41+
/** When the extension's service worker attached to the control channel (ISO). */
3542
executorAttachedAt?: string
43+
/** `'attached'` once the SW has connected; absent while still launching. */
3644
runtime?: string
45+
/** Last contract write time (ISO). */
3746
ts?: string
3847
binary?: string
3948
binaryProvenance?: string
@@ -141,6 +150,7 @@ export class BridgeConsumer {
141150
this.opts = options
142151
}
143152

153+
/** Why the last socket closed; null until a close has happened. */
144154
get lastClose(): ConsumerCloseInfo | null {
145155
return this.lastCloseInfo
146156
}

programs/develop/dev-server/control-bridge/contracts.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,22 @@ export type CommandOp =
7878
| 'tabs.query'
7979
| 'inspect'
8080

81+
/**
82+
* Granularity of a dev-loop reload. Mirrors `ReloadType` in
83+
* plugin-reload/classify-reload.ts: the launched-browser path feeds this
84+
* decision to the CDP controller; the `--no-browser` path feeds the same
85+
* decision to the broker, which broadcasts a {@link ReloadFrame} to the SW
86+
* producer.
87+
*/
8188
export type ReloadType = 'full' | 'service-worker' | 'content-scripts'
8289

90+
/**
91+
* Everything a {@link ReloadFrame} can carry in `reloadType`: the extension
92+
* reload granularities plus `'page'`, a notify-only signal for page-only
93+
* edits (popup/options/sidebar/devtools/newtab). For `'page'` the producer
94+
* performs NO reload (livereload owns the refresh); it only forwards the
95+
* announcement so the devtools pill mirrors the dev loop.
96+
*/
8397
export type DevReloadKind = ReloadType | 'page'
8498

8599
export type GapReason =
@@ -160,12 +174,27 @@ export interface ResultFrame {
160174
}
161175
}
162176

177+
/**
178+
* Dev-loop reload broadcast, server → producer. Sent by the broker on a compile
179+
* that completed without a CDP controller (`--no-browser`, headless/CI, remote)
180+
* so the service-worker producer can self-reload. Unlike a `reload` CommandFrame
181+
* (a controller-issued, `--allow-control`-gated act verb that expects a result),
182+
* this is a fire-and-forget dev-server signal, no cmdId, no result.
183+
*/
163184
export interface ReloadFrame {
164185
type: 'reload'
165186
reloadType: DevReloadKind
166187
changedContentScriptEntries?: string[]
188+
/**
189+
* Server-built human context label, e.g. "content_script (content/scripts.tsx)".
190+
* Shown VERBATIM by every announcement surface (CLI stdout, the page's
191+
* devtools console line, the devtools-extension pill) so the three can
192+
* never disagree about what is reloading.
193+
*/
167194
label?: string
195+
/** Project-relative source files that triggered this reload. */
168196
changedFiles?: string[]
197+
/** Emitted scripts/ bundles the SW should replay its executeScript calls for. */
169198
changedScriptFiles?: string[]
170199
}
171200

@@ -177,6 +206,15 @@ export interface ReloadAckFrame {
177206
label?: string
178207
}
179208

209+
/**
210+
* Server → producer keepalive. An MV3 service worker idles out after ~30s
211+
* without events, and a stopped SW holds no control socket, reload
212+
* broadcasts would reach zero producers and silently apply to nothing
213+
* (quiet extensions lost SW/manifest reloads once >30s passed between
214+
* edits). Receiving any WebSocket message resets the SW idle timer
215+
* (Chrome 116+), so a periodic ping keeps the dev extension's SW
216+
* responsive for the whole dev session. Producers ignore the frame.
217+
*/
180218
export interface PingFrame {
181219
type: 'ping'
182220
}
@@ -198,7 +236,11 @@ export const CONTROL_WS_PATH = '/extjs-control'
198236
// so it never has to spell 4002 in its own source. A close in this range is
199237
// always a deliberate refusal, never a transport failure.
200238

239+
/** The hello named an instanceId from a previous dev session. */
201240
export const CLOSE_BAD_INSTANCE = 4001
241+
/** The hello was malformed: wrong envelope version, or an unknown role. */
202242
export const CLOSE_BAD_HELLO = 4002
243+
/** A controller dialed a session started without `--allow-control`. */
203244
export const CLOSE_CONTROL_UNAVAILABLE = 4003
245+
/** The socket fell far enough behind that it was dropped to protect the broker. */
204246
export const CLOSE_SLOW_CONSUMER = 4008

programs/develop/dev-server/control-bridge/logs-query.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {logsPath} from '../../lib/session-paths'
1414
// the CLI's observable behavior. `extension logs` and any programmatic reader
1515
// must agree on what a filter selects, or the same query answers twice.
1616

17+
/** Increasing verbosity; a level selects itself plus everything more severe. */
1718
export const LOG_LEVEL_ORDER = [
1819
'error',
1920
'warn',
@@ -29,14 +30,22 @@ export type LogLevelFilter =
2930
| (string & {})
3031

3132
export interface LogQuery {
33+
/** One context, a comma-separated list, an array, or 'all'. */
3234
context?: string | string[]
35+
/** Minimum severity. 'all' and 'off' select every level. */
3336
level?: LogLevelFilter
37+
/** Only structured dx.signal diagnostics. */
3438
signalsOnly?: boolean
39+
/** Only events after this point: a sequence number, or an ISO timestamp
40+
* compared against the event's own clock. */
3541
since?: number | string
42+
/** Glob (`*` = any run of chars) or plain substring over url then hostname. */
3643
url?: string
44+
/** Only events carrying this tab id. */
3745
tab?: number | string
3846
}
3947

48+
/** A bridge log line as read off disk: dynamic, so the probed fields only. */
4049
export interface LogEventLike {
4150
type?: unknown
4251
eventType?: unknown
@@ -86,6 +95,7 @@ function makeUrlMatcher(pattern: string): (event: LogEventLike) => boolean {
8695
}
8796
}
8897

98+
/** How a `since` value is read: a sequence number, or a point in time. */
8999
export type LogSince = {seq: number} | {time: number}
90100

91101
// A bare number is a sequence number; anything else must parse as a date, so
@@ -135,6 +145,7 @@ function toFiniteNumber(value: unknown): number | null {
135145
return Number.isFinite(parsed) ? parsed : null
136146
}
137147

148+
/** True when the event passes every clause of the query. */
138149
export function matchesLogQuery(event: LogEventLike, query: LogQuery): boolean {
139150
if (!event || typeof event !== 'object') return false
140151
// The first line of a logs.ndjson generation is a header record, never a log.
@@ -164,6 +175,11 @@ export function matchesLogQuery(event: LogEventLike, query: LogQuery): boolean {
164175
return true
165176
}
166177

178+
/**
179+
* One-shot read of a session's logs.ndjson. Returns an empty array when the
180+
* session has never written one: an absent file is "nothing logged yet", and
181+
* making that a throw would force every caller to guard it.
182+
*/
167183
export function readLogEvents(
168184
projectPath: string,
169185
browser = 'chrome',

programs/develop/lib/build-summary.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@
66
// ╚═════╝ ╚══════╝ ╚═══╝ ╚══════╝╚══════╝ ╚═════╝ ╚═╝
77
// MIT License (c) 2020–present Cezar Augusto & the Extension.js authors, presence implies inheritance
88

9+
/**
10+
* What the injected Safari packager reports back about the app it produced.
11+
* `bundleIdDerived` is the load-bearing one: a generated `dev.extensionjs.*`
12+
* identifier comes from the app name, so every project built from the same
13+
* source shares it and the first registration takes it.
14+
*/
915
export interface SafariPackageSummary {
1016
appName?: string
1117
bundleId?: string
@@ -17,13 +23,18 @@ export interface SafariPackageSummary {
1723

1824
export type BuildSummary = {
1925
browser: string
26+
/** Absolute dist directory the build emitted into. Hosts that shell out
27+
* would otherwise have to re-derive `<project>/dist/<browser>` themselves. */
2028
output_path?: string
2129
total_assets: number
2230
total_bytes: number
2331
largest_asset_bytes: number
2432
warnings_count: number
2533
errors_count: number
34+
/** Plain-text warning messages (ANSI-stripped, capped) so programmatic
35+
* consumers get a structured channel instead of scraping stdout. */
2636
warnings?: string[]
37+
/** Present only for safari/webkit-based builds that ran the packager. */
2738
safari?: SafariPackageSummary
2839
}
2940

programs/develop/plugin-browsers/index.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ export interface BrowserLaunchOptions {
8080
logSink?: BrowserLogSink
8181
}
8282

83+
/** Browser-generated log entry normalized by the launcher's CDP controller. */
8384
export interface BrowserLogSinkEvent {
8485
level: 'log' | 'info' | 'warn' | 'error' | 'debug'
8586
text: string
@@ -107,7 +108,9 @@ export interface BrowserController {
107108
urlFilter?: string
108109
tabFilter?: number | string
109110
}): Promise<void>
111+
/** The browser's refusal reason for this session, or null when it loaded. */
110112
getExtensionLoadRefusal?(): string | null
113+
/** Re-offer the current dist. Only ever called while the session is refused. */
111114
retryExtensionLoad?(): Promise<ExtensionLoadRetryResult>
112115
}
113116

@@ -122,18 +125,38 @@ export interface RunnerPlugin {
122125
}
123126

124127
export interface BrowsersPluginOptions {
128+
/** Injected browser launcher, provided by the CLI from programs/extension/browsers/ */
125129
launcher: BrowserLauncherFn
130+
/** Browser-related options forwarded to the launcher (outputPath/contextDir/extensionsToLoad are filled at compile time) */
126131
browserOptions: Omit<
127132
BrowserLaunchOptions,
128133
'outputPath' | 'contextDir' | 'extensionsToLoad'
129134
>
130135
}
131136

137+
/**
138+
* BrowsersPlugin
139+
*
140+
* An rspack plugin that manages the browser lifecycle for extension development.
141+
* On first successful compilation it launches a browser via the injected launcher
142+
* function; on subsequent compilations it classifies changed files and triggers
143+
* the appropriate reload strategy (full / service-worker / content-scripts).
144+
*
145+
* A `BuildEmitter` is exposed as `plugin.emitter` so that external consumers
146+
* (CLI telemetry, wait-mode, etc.) can subscribe to build events without
147+
* coupling to rspack.
148+
*/
132149
export class BrowsersPlugin implements RunnerPlugin {
133150
static readonly name = 'plugin-browsers'
134151

152+
/** EventEmitter for build lifecycle events (compiled, error, close). */
135153
readonly emitter = new BuildEmitter()
136154

155+
/**
156+
* Extension directories to load alongside the user extension.
157+
* Set externally by webpack-config after computing companion extensions,
158+
* before the first compilation.
159+
*/
137160
extensionsToLoad: string[] = []
138161

139162
private isFirstCompile = true
@@ -145,6 +168,11 @@ export class BrowsersPlugin implements RunnerPlugin {
145168

146169
constructor(private readonly options: BrowsersPluginOptions) {}
147170

171+
/**
172+
* The dev server injects the control-bridge broker so a launched
173+
* Chromium reloads through the SW producer (the same path as `--no-browser`),
174+
* not the CDP controller. Called once, before the first compile.
175+
*/
148176
setReloadBroker(broker: ReloadBroker): void {
149177
this.reloadBroker = broker
150178
}

programs/develop/plugin-reload/classify-reload.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export interface ReloadInstruction {
2828
label?: string
2929
}
3030

31+
/** "context (fileA, fileB +2 more)", the one label every reload surface shows. */
3132
export function formatReloadContextLabel(
3233
context: string,
3334
files: string[]
@@ -65,9 +66,12 @@ export function pageContextFromSources(changedSources: string[]): string {
6566
// Name-pattern heuristics are NOT trustworthy for this decision.
6667
export interface SourceFeatureIndex {
6768
swSources: Set<string>
69+
/** Source → canonical content_scripts entry names whose chunks contain it. */
6870
contentEntriesBySource: Map<string, Set<string>>
6971
pageSources: Set<string>
72+
/** Source → emitted scripts/ bundle names whose chunks contain it. */
7073
scriptFilesBySource?: Map<string, Set<string>>
74+
/** Project-relative public/ roots the copier ships at the dist root. */
7175
publicRoots?: string[]
7276
}
7377

programs/develop/plugin-reload/index.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,24 @@ export {
3939
} from './reload-dispatch'
4040
export {SetupChunkLoadingTarget} from './steps/setup-chunk-loading-target'
4141

42+
/**
43+
* ReloadPlugin owns the dev-only reload/HMR strategy end to end:
44+
*
45+
* - build-time injection of the reload runtime (SetupReloadStrategy and the
46+
* vendored webpack-target-webextension fork), the SW scripts-replay shim,
47+
* and the control-bridge producer/relay instrumentation
48+
* - the reload classifier and dispatch seam consumed by plugin-browsers'
49+
* BrowsersPlugin and the dev server's `--no-browser` broadcast path
50+
* (re-exported above from classify-reload.ts / reload-dispatch.ts)
51+
*
52+
* Registration order matters: this plugin must be applied AFTER
53+
* plugin-web-extension, SetupReloadStrategy decorates the background and
54+
* content-script entries that feature-scripts' AddScripts declares.
55+
*
56+
* The whole pipeline is dev-only; `EXTENSION_NO_RELOAD=true` opts out. The
57+
* every-mode content-script wrapper (mount lifecycle) is NOT part of this
58+
* plugin. It lives in feature-scripts/steps/add-content-script-wrapper.
59+
*/
4260
export class ReloadPlugin {
4361
public static readonly name = 'plugin-reload'
4462

programs/develop/plugin-special-folders/folder-extensions/types.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,22 @@
66
// ╚══════╝╚═╝ ╚══════╝ ╚═════╝╚═╝╚═╝ ╚═╝╚══════╝ ╚═╝ ╚═════╝ ╚══════╝╚═════╝ ╚══════╝╚═╝ ╚═╝╚══════╝
77
// MIT License (c) 2020–present Cezar Augusto & the Extension.js authors, presence implies inheritance
88

9+
/**
10+
* Companion extensions are "load-only" unpacked extension directories that
11+
* should be loaded alongside the user extension in dev/preview/start.
12+
*
13+
* Each directory MUST be an unpacked extension root containing a manifest.json.
14+
*/
915
export type CompanionExtensionsConfig =
1016
| string[]
1117
| {
18+
/**
19+
* Folder to scan for subfolders that contain a manifest.json.
20+
* Example: "./extensions" -> loads "./extensions/*" (one level deep)
21+
*/
1222
dir?: string
23+
/**
24+
* Explicit extension directories to load (absolute or relative to projectRoot).
25+
*/
1326
paths?: string[]
1427
}

0 commit comments

Comments
 (0)