extension-develop 4.0.26 → 4.0.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/101.mjs CHANGED
@@ -3,12 +3,12 @@ import { createRequire } from "node:module";
3
3
  import { pathToFileURL } from "node:url";
4
4
  import dotenv from "dotenv";
5
5
  import pintor from "pintor";
6
+ import { createHash } from "node:crypto";
6
7
  import { fetchExtensionFromStore } from "extension-from-store";
7
8
  import node_fs from "node:fs";
8
9
  import node_path from "node:path";
9
10
  import { getSpecialFoldersData } from "browser-extension-manifest-fields";
10
- import { createHash } from "node:crypto";
11
- import { fmt, browserRowValue, isDebug, humanWarn, artifactNoun, prefix as messaging_prefix, card } from "./349.mjs";
11
+ import { fmt, humanWarn, isDebug, browserRowValue, artifactNoun, prefix as messaging_prefix, card } from "./349.mjs";
12
12
  import { findNearestDenoConfigSync, validateDenoConfig, PROJECT_MANIFEST_FILENAMES, readProjectDependencies } from "./80.mjs";
13
13
  import { stripBom, parseJsonSafe } from "./23.mjs";
14
14
  import { readyContractPath, browserArtifactsDir, eventsPath as session_paths_eventsPath } from "./494.mjs";
@@ -634,7 +634,7 @@ async function config_loader_isUsingExperimentalConfig(projectPath) {
634
634
  }
635
635
  return false;
636
636
  }
637
- var package_namespaceObject = /*#__PURE__*/ JSON.parse('{"rE":"4.0.26","El":{"@prefresh/core":"1.5.9","@prefresh/utils":"1.2.1","@rspack/core":"^2.1.1","@rspack/dev-server":"2.1.0","@rspack/plugin-preact-refresh":"2.0.1","@rspack/plugin-react-refresh":"2.0.2","@vue/compiler-sfc":"3.5.26","acorn":"^8.16.0","adm-zip":"^0.6.0","browser-extension-manifest-fields":"^2.2.9","case-sensitive-paths-webpack-plugin":"^2.4.0","content-security-policy-parser":"^0.6.0","dotenv":"^17.2.3","es-module-lexer":"^2.1.0","extension-from-store":"^0.1.1","go-git-it":"^5.1.5","ignore":"^7.0.5","less":"4.6.7","less-loader":"13.0.0","parse5-utilities":"^1.0.0","pintor":"0.3.0","postcss":"8.5.18","postcss-loader":"8.2.1","postcss-preset-env":"11.1.1","postcss-scss":"4.0.9","preact":"10.27.3","prefers-yarn":"2.0.1","react-refresh":"0.18.0","sass-loader":"17.0.0","schema-utils":"^4.3.3","svelte-loader":"3.2.4","tiny-glob":"^0.2.9","vue":"3.5.26","vue-loader":"17.4.2","webextension-polyfill":"^0.12.0","webpack-merge":"^6.0.1","webpack-target-webextension":"^2.1.3","ws":"^8.20.1"}}');
637
+ var package_namespaceObject = /*#__PURE__*/ JSON.parse('{"rE":"4.0.27","El":{"@prefresh/core":"1.5.9","@prefresh/utils":"1.2.1","@rspack/core":"^2.1.1","@rspack/dev-server":"2.1.0","@rspack/plugin-preact-refresh":"2.0.1","@rspack/plugin-react-refresh":"2.0.2","@vue/compiler-sfc":"3.5.26","acorn":"^8.16.0","adm-zip":"^0.6.0","browser-extension-manifest-fields":"^2.2.9","case-sensitive-paths-webpack-plugin":"^2.4.0","content-security-policy-parser":"^0.6.0","dotenv":"^17.2.3","es-module-lexer":"^2.1.0","extension-from-store":"^0.1.1","go-git-it":"^5.1.5","ignore":"^7.0.5","less":"4.6.7","less-loader":"13.0.0","parse5-utilities":"^1.0.0","pintor":"0.3.0","postcss":"8.5.18","postcss-loader":"8.2.1","postcss-preset-env":"11.1.1","postcss-scss":"4.0.9","preact":"10.27.3","prefers-yarn":"2.0.1","react-refresh":"0.18.0","sass-loader":"17.0.0","schema-utils":"^4.3.3","svelte-loader":"3.2.4","tiny-glob":"^0.2.9","vue":"3.5.26","vue-loader":"17.4.2","webextension-polyfill":"^0.12.0","webpack-merge":"^6.0.1","webpack-target-webextension":"^2.1.3","ws":"^8.20.1"}}');
638
638
  function asAbsolute(p) {
639
639
  return __rspack_external_node_path_c5b9b54f.isAbsolute(p) ? p : __rspack_external_node_path_c5b9b54f.resolve(p);
640
640
  }
@@ -752,10 +752,101 @@ function devtoolsEngineFor(browser) {
752
752
  case 'waterfox':
753
753
  case 'librewolf':
754
754
  return 'firefox';
755
+ case 'safari':
756
+ case 'webkit-based':
757
+ return 'safari';
755
758
  default:
756
759
  return 'chrome';
757
760
  }
758
761
  }
762
+ function sanitize(obj) {
763
+ return Object.fromEntries(Object.entries(obj || {}).filter(([, value])=>void 0 !== value));
764
+ }
765
+ const DEV_COMMAND_DEFAULTS = {
766
+ polyfill: true,
767
+ logFormat: 'pretty',
768
+ logTimestamps: true,
769
+ logColor: true,
770
+ logLevel: 'off',
771
+ noOpen: false
772
+ };
773
+ const BUILD_COMMAND_DEFAULTS = {
774
+ polyfill: false,
775
+ zip: false,
776
+ zipSource: false,
777
+ silent: false
778
+ };
779
+ const SERVE_COMMAND_DEFAULTS = {
780
+ logFormat: 'pretty',
781
+ logTimestamps: true,
782
+ logColor: true,
783
+ logLevel: 'off'
784
+ };
785
+ const START_BUILD_DEFAULTS = {
786
+ polyfill: true,
787
+ silent: true
788
+ };
789
+ const CONCAT_ARRAY_KEYS = new Set([
790
+ 'browserFlags',
791
+ 'excludeBrowserFlags'
792
+ ]);
793
+ const DEEP_MERGE_OBJECT_KEYS = new Set([
794
+ 'preferences'
795
+ ]);
796
+ function isPlainObject(value) {
797
+ return 'object' == typeof value && null !== value && !Array.isArray(value) && '[object Object]' === Object.prototype.toString.call(value);
798
+ }
799
+ function deepMergeObjects(base, overlay) {
800
+ const result = {
801
+ ...base
802
+ };
803
+ for (const [key, value] of Object.entries(overlay)){
804
+ if (void 0 === value) continue;
805
+ const existing = result[key];
806
+ if (isPlainObject(existing) && isPlainObject(value)) result[key] = deepMergeObjects(existing, value);
807
+ else result[key] = value;
808
+ }
809
+ return result;
810
+ }
811
+ function concatUnique(prev, next) {
812
+ const prevArr = Array.isArray(prev) ? prev : [];
813
+ const seen = new Set();
814
+ const combined = [];
815
+ for (const item of [
816
+ ...prevArr,
817
+ ...next
818
+ ])if (!seen.has(item)) {
819
+ seen.add(item);
820
+ combined.push(item);
821
+ }
822
+ return combined;
823
+ }
824
+ function mergeLayer(base, layer) {
825
+ const clean = sanitize(layer);
826
+ const result = {
827
+ ...base
828
+ };
829
+ for (const [key, value] of Object.entries(clean)){
830
+ if (CONCAT_ARRAY_KEYS.has(key) && Array.isArray(value)) {
831
+ result[key] = concatUnique(result[key], value);
832
+ continue;
833
+ }
834
+ if (DEEP_MERGE_OBJECT_KEYS.has(key) && isPlainObject(value)) {
835
+ const prev = result[key];
836
+ result[key] = isPlainObject(prev) ? deepMergeObjects(prev, value) : {
837
+ ...value
838
+ };
839
+ continue;
840
+ }
841
+ result[key] = value;
842
+ }
843
+ return result;
844
+ }
845
+ function mergeOptionLayers(defaults, ...layers) {
846
+ let result = mergeLayer({}, defaults);
847
+ for (const layer of layers)result = mergeLayer(result, layer || {});
848
+ return result;
849
+ }
759
850
  async function findUpLocal(filename, options) {
760
851
  const root = __rspack_external_node_path_c5b9b54f.parse(options.cwd).root;
761
852
  let currentDir = options.cwd;
@@ -1105,6 +1196,9 @@ const GECKO_BASED_BROWSERS = [
1105
1196
  'waterfox',
1106
1197
  'librewolf'
1107
1198
  ];
1199
+ const WEBKIT_BASED_BROWSERS = [
1200
+ 'safari'
1201
+ ];
1108
1202
  const CHROMIUM_FAMILY_ALIASES = [
1109
1203
  'chromium',
1110
1204
  'chrome',
@@ -1125,1066 +1219,1094 @@ function isChromiumBasedBrowser(browser) {
1125
1219
  function isGeckoBasedBrowser(browser) {
1126
1220
  return GECKO_BASED_BROWSERS.includes(browser) || String(browser).includes('gecko') || String(browser).includes('firefox');
1127
1221
  }
1128
- function isDir(p) {
1129
- try {
1130
- return __rspack_external_node_fs_5ea92f0c.existsSync(p) && __rspack_external_node_fs_5ea92f0c.statSync(p).isDirectory();
1131
- } catch {
1132
- return false;
1133
- }
1222
+ function isWebkitBasedBrowser(browser) {
1223
+ return WEBKIT_BASED_BROWSERS.includes(browser) || String(browser).includes('webkit');
1134
1224
  }
1135
- function isFile(p) {
1225
+ function idFromHash(hash) {
1226
+ const hex = hash.subarray(0, 16).toString('hex');
1227
+ let id = '';
1228
+ for (const char of hex)id += String.fromCharCode('a'.charCodeAt(0) + parseInt(char, 16));
1229
+ return id;
1230
+ }
1231
+ function chromiumExtensionIdFromKey(manifestKey) {
1232
+ const decoded = Buffer.from(manifestKey, 'base64');
1233
+ return idFromHash(createHash('sha256').update(decoded).digest());
1234
+ }
1235
+ function chromiumExtensionIdFromPath(extensionDir) {
1236
+ const absolute = __rspack_external_node_path_c5b9b54f.resolve(extensionDir);
1237
+ const bytes = 'win32' === process.platform ? Buffer.from(absolute.replace(/[A-Z]/g, (char)=>char.toLowerCase()), 'utf16le') : Buffer.from(absolute, 'utf8');
1238
+ return idFromHash(createHash('sha256').update(bytes).digest());
1239
+ }
1240
+ function readManifest(extensionDir) {
1136
1241
  try {
1137
- return __rspack_external_node_fs_5ea92f0c.existsSync(p) && __rspack_external_node_fs_5ea92f0c.statSync(p).isFile();
1242
+ const raw = __rspack_external_node_fs_5ea92f0c.readFileSync(__rspack_external_node_path_c5b9b54f.join(extensionDir, 'manifest.json'), 'utf-8');
1243
+ const parsed = JSON.parse(stripBom(raw));
1244
+ return parsed && 'object' == typeof parsed ? parsed : null;
1138
1245
  } catch {
1139
- return false;
1246
+ return null;
1140
1247
  }
1141
1248
  }
1142
- function toAbs(projectRoot, p) {
1143
- return __rspack_external_node_path_c5b9b54f.isAbsolute(p) ? p : __rspack_external_node_path_c5b9b54f.resolve(projectRoot, p);
1144
- }
1145
- function isValidExtensionRoot(dir) {
1146
- if (!isDir(dir)) return false;
1147
- return isFile(__rspack_external_node_path_c5b9b54f.join(dir, 'manifest.json'));
1249
+ function chromiumExtensionId(extensionDir) {
1250
+ const manifest = readManifest(extensionDir);
1251
+ const key = manifest?.key;
1252
+ if ('string' == typeof key && key.trim().length > 0) try {
1253
+ return chromiumExtensionIdFromKey(key);
1254
+ } catch {}
1255
+ return chromiumExtensionIdFromPath(extensionDir);
1148
1256
  }
1149
- function normalizeCompanionConfig(config) {
1150
- const explicitPaths = [];
1151
- let scanDir;
1152
- if (Array.isArray(config)) explicitPaths.push(...config.filter((p)=>'string' == typeof p));
1153
- else if (config && 'object' == typeof config) {
1154
- if (Array.isArray(config.paths)) explicitPaths.push(...config.paths.filter((p)=>'string' == typeof p));
1155
- if ('string' == typeof config.dir && config.dir.trim().length > 0) scanDir = config.dir.trim();
1156
- }
1157
- return {
1158
- dir: scanDir,
1159
- paths: explicitPaths
1160
- };
1257
+ function geckoExtensionId(extensionDir) {
1258
+ const manifest = readManifest(extensionDir);
1259
+ const settings = manifest?.browser_specific_settings ?? manifest?.applications;
1260
+ const id = settings?.gecko?.id;
1261
+ return 'string' == typeof id && id.trim().length > 0 ? id : void 0;
1161
1262
  }
1162
- function resolve_config_isPathLike(value) {
1163
- if (__rspack_external_node_path_c5b9b54f.isAbsolute(value)) return true;
1164
- if (value.startsWith('./') || value.startsWith('../')) return true;
1165
- return value.includes('/') || value.includes('\\');
1263
+ function managedExtensionRecords(browser, extensionDirs) {
1264
+ const gecko = isGeckoBasedBrowser(browser);
1265
+ const webkit = isWebkitBasedBrowser(browser);
1266
+ return extensionDirs.map((dir)=>{
1267
+ const absolute = __rspack_external_node_path_c5b9b54f.resolve(dir);
1268
+ if (webkit) return {
1269
+ path: absolute
1270
+ };
1271
+ const id = gecko ? geckoExtensionId(absolute) : chromiumExtensionId(absolute);
1272
+ return id ? {
1273
+ path: absolute,
1274
+ id
1275
+ } : {
1276
+ path: absolute
1277
+ };
1278
+ });
1166
1279
  }
1167
- function isSubpathOf(parent, child) {
1168
- const rel = __rspack_external_node_path_c5b9b54f.relative(parent, child);
1169
- return '' !== rel && !rel.startsWith('..') && !__rspack_external_node_path_c5b9b54f.isAbsolute(rel);
1280
+ function nowISO() {
1281
+ return new Date().toISOString();
1170
1282
  }
1171
- function getBrowserFolder(browser) {
1172
- if (browser && isGeckoBasedBrowser(String(browser))) return 'firefox';
1173
- if ('edge' === browser) return 'edge';
1174
- return 'chrome';
1283
+ const MAX_CONTRACT_ERRORS = 10;
1284
+ const ANSI_PATTERN = /\u001b\[[0-9;]*m/g;
1285
+ function formatStatsErrors(errors) {
1286
+ if (!Array.isArray(errors)) return [];
1287
+ return errors.slice(0, MAX_CONTRACT_ERRORS).map((error)=>{
1288
+ const message = error && 'object' == typeof error ? String(error.message ?? '') : String(error ?? '');
1289
+ return message.replace(ANSI_PATTERN, '').trim();
1290
+ }).filter(Boolean);
1175
1291
  }
1176
- function parseChromeWebStoreId(url) {
1177
- if ('chromewebstore.google.com' !== url.hostname) return null;
1178
- const match = url.pathname.match(/\/([a-z]{32})(?:\/|$)/i);
1179
- return match ? match[1] : null;
1292
+ function createRunId() {
1293
+ return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
1180
1294
  }
1181
- function parseEdgeAddonsId(url) {
1182
- if ('microsoftedge.microsoft.com' !== url.hostname) return null;
1183
- const match = url.pathname.match(/\/([a-z]{32})(?:\/|$)/i);
1184
- return match ? match[1] : null;
1295
+ const runIdByMetadataDir = new Map();
1296
+ function getRunIdForSession(metadataDir) {
1297
+ const existing = runIdByMetadataDir.get(metadataDir);
1298
+ if (existing) return existing;
1299
+ const runId = createRunId();
1300
+ runIdByMetadataDir.set(metadataDir, runId);
1301
+ return runId;
1185
1302
  }
1186
- function parseAmoSlug(url) {
1187
- if ('addons.mozilla.org' !== url.hostname) return null;
1188
- const match = url.pathname.match(/\/addon\/([^/]+)(?:\/|$)/i);
1189
- return match ? match[1] : null;
1303
+ function getSessionRunId(packageJsonDir, browser) {
1304
+ return getRunIdForSession(getPlaywrightMetadataDir(packageJsonDir, browser));
1190
1305
  }
1191
- function parseStoreUrl(raw) {
1192
- let url;
1306
+ function deriveDistExtensionId(browser, distPath) {
1193
1307
  try {
1194
- url = new URL(raw);
1308
+ if (isWebkitBasedBrowser(browser)) return;
1309
+ if (!__rspack_external_node_fs_5ea92f0c.existsSync(__rspack_external_node_path_c5b9b54f.join(distPath, 'manifest.json'))) return;
1310
+ const id = isGeckoBasedBrowser(browser) ? geckoExtensionId(distPath) : chromiumExtensionId(distPath);
1311
+ return id || void 0;
1195
1312
  } catch {
1196
- return null;
1313
+ return;
1197
1314
  }
1198
- const chromeId = parseChromeWebStoreId(url);
1199
- if (chromeId) return {
1200
- browser: 'chrome',
1201
- id: chromeId
1202
- };
1203
- const edgeId = parseEdgeAddonsId(url);
1204
- if (edgeId) return {
1205
- browser: 'edge',
1206
- id: edgeId
1207
- };
1208
- const amoSlug = parseAmoSlug(url);
1209
- if (amoSlug) return {
1210
- browser: 'firefox',
1211
- id: amoSlug
1212
- };
1213
- return null;
1214
1315
  }
1215
- function ensurePathsUnderExtensions(projectRoot, paths) {
1216
- const extensionsRoot = __rspack_external_node_path_c5b9b54f.resolve(projectRoot, 'extensions');
1217
- return paths.map((p)=>{
1218
- const abs = toAbs(projectRoot, p);
1219
- if (abs !== extensionsRoot && !isSubpathOf(extensionsRoot, abs) && __rspack_external_node_path_c5b9b54f.resolve(abs) !== extensionsRoot) throw new Error(`Companion extensions must be inside ${extensionsRoot}.\nInvalid path: ${abs}`);
1220
- return abs;
1221
- });
1316
+ function stampReadyExtensionIdIfAbsent(packageJsonDir, browser, extensionId) {
1317
+ try {
1318
+ const readyPath = readyContractPath(packageJsonDir, browser);
1319
+ if (!__rspack_external_node_fs_5ea92f0c.existsSync(readyPath)) return;
1320
+ const prev = JSON.parse(__rspack_external_node_fs_5ea92f0c.readFileSync(readyPath, 'utf-8'));
1321
+ if ('string' == typeof prev.extensionId && prev.extensionId) return;
1322
+ prev.extensionId = extensionId;
1323
+ prev.ts = nowISO();
1324
+ writeJsonAtomic(readyPath, prev);
1325
+ } catch {}
1222
1326
  }
1223
- function findExtensionRoots(dir, maxDepth = 3) {
1224
- const found = [];
1225
- function walk(current, depth) {
1226
- if (depth > maxDepth) return;
1227
- if (!isDir(current)) return;
1228
- if (isValidExtensionRoot(current)) return void found.push(current);
1229
- let entries = [];
1230
- try {
1231
- entries = __rspack_external_node_fs_5ea92f0c.readdirSync(current, {
1232
- withFileTypes: true
1233
- });
1234
- } catch {
1235
- return;
1236
- }
1237
- for (const ent of entries)if (ent.isDirectory()) {
1238
- if (!ent.name.startsWith('.')) walk(__rspack_external_node_path_c5b9b54f.join(current, ent.name), depth + 1);
1239
- }
1240
- }
1241
- walk(dir, 0);
1242
- return found;
1327
+ function stampReadyDistExtensionId(packageJsonDir, browser, distPath) {
1328
+ const derived = deriveDistExtensionId(browser, distPath);
1329
+ if (!derived) return;
1330
+ stampReadyExtensionIdIfAbsent(packageJsonDir, browser, derived);
1243
1331
  }
1244
- async function runExtensionFromStore(url, outDir) {
1245
- const isAuthor = isDebug();
1246
- await fetchExtensionFromStore(url, {
1247
- outDir,
1248
- extract: true,
1249
- logger: isAuthor ? {
1250
- onInfo: (message)=>console.log(message),
1251
- onWarn: (message)=>console.warn(message),
1252
- onError: (message, error)=>console.error(message, error)
1253
- } : void 0
1254
- });
1332
+ function stampReadyKnownExtensionId(packageJsonDir, browser, extensionId) {
1333
+ if (!extensionId) return;
1334
+ stampReadyExtensionIdIfAbsent(packageJsonDir, browser, extensionId);
1255
1335
  }
1256
- async function resolveStoreExtensionToPath(opts) {
1257
- const { projectRoot, storeUrl, browser, id } = opts;
1258
- const extensionsRoot = __rspack_external_node_path_c5b9b54f.resolve(projectRoot, 'extensions');
1259
- const targetRoot = __rspack_external_node_path_c5b9b54f.join(extensionsRoot, browser, id);
1260
- const manifestPath = __rspack_external_node_path_c5b9b54f.join(targetRoot, 'manifest.json');
1261
- if (isFile(manifestPath)) return targetRoot;
1262
- await runExtensionFromStore(storeUrl, __rspack_external_node_path_c5b9b54f.join(extensionsRoot, browser));
1263
- const candidates = findExtensionRoots(__rspack_external_node_path_c5b9b54f.join(extensionsRoot, browser));
1264
- let selected;
1265
- if (1 === candidates.length) selected = candidates[0];
1266
- else if (candidates.length > 1) {
1267
- const directMatch = candidates.find((c)=>__rspack_external_node_path_c5b9b54f.basename(c) === id);
1268
- const versionedMatch = candidates.find((c)=>__rspack_external_node_path_c5b9b54f.basename(c).startsWith(`${id}@`));
1269
- if (directMatch) selected = directMatch;
1270
- else if (versionedMatch) selected = versionedMatch;
1336
+ function readManifestProvenance(manifestPath) {
1337
+ try {
1338
+ const manifest = parseJsonSafe(__rspack_external_node_fs_5ea92f0c.readFileSync(manifestPath, 'utf-8'));
1339
+ return {
1340
+ extensionName: 'string' == typeof manifest?.name ? manifest.name : void 0,
1341
+ extensionVersion: 'string' == typeof manifest?.version ? manifest.version : void 0
1342
+ };
1343
+ } catch {
1344
+ return {};
1271
1345
  }
1272
- if (!selected) throw new Error(`Could not locate an unpacked extension from ${storeUrl}.`);
1273
- __rspack_external_node_fs_5ea92f0c.mkdirSync(__rspack_external_node_path_c5b9b54f.dirname(targetRoot), {
1274
- recursive: true
1275
- });
1276
- if (__rspack_external_node_path_c5b9b54f.basename(selected).startsWith(`${id}@`)) __rspack_external_node_fs_5ea92f0c.renameSync(selected, targetRoot);
1277
- return targetRoot;
1278
1346
  }
1279
- async function resolveCompanionExtensionsConfig(opts) {
1280
- const { projectRoot, browser, config } = opts;
1281
- if (!config) return;
1282
- const normalized = normalizeCompanionConfig(config);
1283
- const runtimeBrowser = getBrowserFolder(browser);
1284
- const resolvedPaths = [];
1285
- const localPaths = [];
1286
- for (const entry of normalized.paths){
1287
- const parsedStore = parseStoreUrl(entry);
1288
- if (parsedStore) {
1289
- if (parsedStore.browser !== runtimeBrowser) continue;
1290
- const resolvedPath = await resolveStoreExtensionToPath({
1291
- projectRoot,
1292
- storeUrl: entry,
1293
- browser: parsedStore.browser,
1294
- id: parsedStore.id
1295
- });
1296
- resolvedPaths.push(resolvedPath);
1297
- continue;
1298
- }
1299
- if (resolve_config_isPathLike(entry)) localPaths.push(entry);
1300
- }
1301
- if (localPaths.length > 0) {
1302
- const absLocalPaths = ensurePathsUnderExtensions(projectRoot, localPaths);
1303
- resolvedPaths.push(...absLocalPaths);
1304
- }
1305
- const output = {};
1306
- if ('string' == typeof normalized.dir) {
1307
- const absDir = toAbs(projectRoot, normalized.dir);
1308
- const extensionsRoot = __rspack_external_node_path_c5b9b54f.resolve(projectRoot, 'extensions');
1309
- if (absDir !== extensionsRoot && !isSubpathOf(extensionsRoot, absDir)) throw new Error(`extensions.dir must be inside ${extensionsRoot}.\nInvalid dir: ${absDir}`);
1310
- output.dir = normalized.dir;
1311
- }
1312
- if (resolvedPaths.length > 0) output.paths = resolvedPaths;
1313
- return output;
1347
+ function plugin_playwright_ensureDirSync(dirPath) {
1348
+ try {
1349
+ __rspack_external_node_fs_5ea92f0c.mkdirSync(dirPath, {
1350
+ recursive: true
1351
+ });
1352
+ } catch {}
1314
1353
  }
1315
- const NODE_BUILTINS = new Set([
1316
- 'assert',
1317
- 'buffer',
1318
- 'child_process',
1319
- 'cluster',
1320
- 'console',
1321
- 'constants',
1322
- 'crypto',
1323
- 'dgram',
1324
- 'dns',
1325
- 'domain',
1326
- 'events',
1327
- 'fs',
1328
- 'fs/promises',
1329
- 'http',
1330
- 'http2',
1331
- 'https',
1332
- 'inspector',
1333
- 'module',
1334
- 'net',
1335
- 'os',
1336
- 'path',
1337
- 'perf_hooks',
1338
- 'process',
1339
- 'punycode',
1340
- 'querystring',
1341
- 'readline',
1342
- 'repl',
1343
- 'stream',
1344
- 'string_decoder',
1345
- 'timers',
1346
- 'tls',
1347
- 'trace_events',
1348
- 'tty',
1349
- 'url',
1350
- 'util',
1351
- 'v8',
1352
- 'vm',
1353
- 'worker_threads',
1354
- 'zlib'
1355
- ]);
1356
- const NODE_BUILD_TOOLS = new Set([
1357
- 'fs-extra',
1358
- 'esbuild',
1359
- 'playwright',
1360
- 'playwright-core',
1361
- 'puppeteer',
1362
- 'puppeteer-core',
1363
- 'webpack',
1364
- 'rollup',
1365
- 'vite',
1366
- 'replace-in-file',
1367
- 'zip-dir',
1368
- 'archiver',
1369
- 'adm-zip',
1370
- 'chokidar',
1371
- 'glob',
1372
- 'fast-glob',
1373
- 'rimraf',
1374
- 'yargs',
1375
- 'execa',
1376
- 'cross-spawn',
1377
- 'shelljs',
1378
- 'web-ext',
1379
- 'dotenv',
1380
- 'node-fetch',
1381
- 'minimist',
1382
- 'ora',
1383
- 'chalk',
1384
- 'gulp',
1385
- 'grunt',
1386
- 'ncp',
1387
- 'del',
1388
- 'cpy',
1389
- 'tsx',
1390
- 'ts-node',
1391
- 'nodemon',
1392
- 'concurrently'
1393
- ]);
1394
- function importsNodeOnly(specifier) {
1395
- if (specifier.startsWith('node:')) return true;
1396
- const bare = specifier.startsWith('@') ? specifier.split('/').slice(0, 2).join('/') : specifier.split('/')[0];
1397
- return NODE_BUILTINS.has(specifier) || NODE_BUILTINS.has(bare) || NODE_BUILD_TOOLS.has(bare);
1354
+ function writeJsonAtomic(filePath, value) {
1355
+ try {
1356
+ const tmpPath = `${filePath}.tmp-${process.pid}`;
1357
+ __rspack_external_node_fs_5ea92f0c.writeFileSync(tmpPath, `${JSON.stringify(value, null, 2)}\n`, 'utf-8');
1358
+ __rspack_external_node_fs_5ea92f0c.renameSync(tmpPath, filePath);
1359
+ } catch {}
1398
1360
  }
1399
- function isNodeToolingScript(absPath) {
1400
- let source;
1361
+ function detectLiveDevSessionOwner(readyPath, isAlive = (pid)=>{
1401
1362
  try {
1402
- source = node_fs.readFileSync(absPath, 'utf8');
1363
+ process.kill(pid, 0);
1364
+ return true;
1403
1365
  } catch {
1404
1366
  return false;
1405
1367
  }
1406
- if (/^#!.*\bnode\b/.test(source)) return true;
1407
- const specifierRe = /(?:require\s*\(\s*|(?:import|export)\b[^'"()]*?\bfrom\s*|import\s*)['"]([^'"]+)['"]/g;
1408
- let match;
1409
- while(null !== (match = specifierRe.exec(source)))if (importsNodeOnly(match[1])) return true;
1410
- return false;
1411
- }
1412
- function filterNodeToolingScripts(list) {
1413
- const next = {};
1414
- for (const [key, value] of Object.entries(list || {})){
1415
- const paths = Array.isArray(value) ? value : value ? [
1416
- value
1417
- ] : [];
1418
- const kept = paths.filter((entry)=>{
1419
- const abs = String(entry);
1420
- if (!node_path.isAbsolute(abs)) return true;
1421
- return !isNodeToolingScript(abs);
1422
- });
1423
- if (0 !== kept.length) next[key] = Array.isArray(value) ? kept : kept[0];
1368
+ }) {
1369
+ try {
1370
+ if (!__rspack_external_node_fs_5ea92f0c.existsSync(readyPath)) return null;
1371
+ const prev = JSON.parse(__rspack_external_node_fs_5ea92f0c.readFileSync(readyPath, 'utf-8'));
1372
+ if (prev?.command !== 'dev') return null;
1373
+ if ('number' != typeof prev.pid || prev.pid === process.pid) return null;
1374
+ if ('ready' !== prev.status && 'starting' !== prev.status) return null;
1375
+ if (!isAlive(prev.pid)) return null;
1376
+ return {
1377
+ pid: prev.pid,
1378
+ runId: 'string' == typeof prev.runId ? prev.runId : '',
1379
+ instanceId: 'string' == typeof prev.instanceId ? prev.instanceId : void 0,
1380
+ instanceExplicit: true === prev.instanceExplicit
1381
+ };
1382
+ } catch {
1383
+ return null;
1424
1384
  }
1425
- return next;
1426
1385
  }
1427
- const REFERENCE_SOURCE_EXTS = new Set([
1428
- '.html',
1429
- '.htm',
1430
- '.js',
1431
- '.jsx',
1432
- '.mjs',
1433
- '.cjs',
1434
- '.ts',
1435
- '.tsx',
1436
- '.mts',
1437
- '.cts',
1438
- '.vue',
1439
- '.svelte'
1440
- ]);
1441
- const REFERENCE_SKIP_DIRS = new Set([
1442
- 'node_modules',
1443
- 'dist',
1444
- 'build',
1445
- 'out',
1446
- '.output',
1447
- 'coverage',
1448
- '.next',
1449
- '.cache',
1450
- '.git',
1451
- '.turbo',
1452
- "scripts"
1453
- ]);
1454
- const REFERENCE_MAX_FILES = 4000;
1455
- const REFERENCE_MAX_BYTES = 12582912;
1456
- function collectReferenceCorpus(projectRoot) {
1457
- const parts = [];
1458
- let files = 0;
1459
- let bytes = 0;
1460
- const walk = (dir)=>{
1461
- if (files >= REFERENCE_MAX_FILES || bytes >= REFERENCE_MAX_BYTES) return;
1462
- let entries;
1463
- try {
1464
- entries = node_fs.readdirSync(dir, {
1465
- withFileTypes: true
1466
- });
1467
- } catch {
1468
- return;
1469
- }
1470
- for (const entry of entries){
1471
- if (files >= REFERENCE_MAX_FILES || bytes >= REFERENCE_MAX_BYTES) return;
1472
- const full = node_path.join(dir, entry.name);
1473
- if (entry.isDirectory()) {
1474
- if (REFERENCE_SKIP_DIRS.has(entry.name) || entry.name.startsWith('.')) continue;
1475
- walk(full);
1476
- continue;
1477
- }
1478
- if (!entry.isFile()) continue;
1479
- const ext = node_path.extname(entry.name).toLowerCase();
1480
- const isManifest = /^manifest\b.*\.json$/i.test(entry.name);
1481
- if (isManifest || REFERENCE_SOURCE_EXTS.has(ext)) try {
1482
- const text = node_fs.readFileSync(full, 'utf8');
1483
- parts.push(text);
1484
- files += 1;
1485
- bytes += text.length;
1486
- } catch {}
1487
- }
1488
- };
1489
- walk(projectRoot);
1490
- return 0 === files ? '' : parts.join('\n');
1386
+ function shouldWarnDevOverDev(owner, my) {
1387
+ if (owner.instanceExplicit && my.instanceExplicit && owner.instanceId && my.instanceId && owner.instanceId !== my.instanceId) return false;
1388
+ return true;
1491
1389
  }
1492
- function filterUnreferencedScripts(list, projectRoot) {
1493
- const entries = Object.entries(list || {});
1494
- if (0 === entries.length) return list || {};
1495
- const corpus = collectReferenceCorpus(projectRoot);
1496
- if ('' === corpus) return list || {};
1497
- const isReferenced = (entry)=>{
1498
- const abs = String(entry);
1499
- if (!node_path.isAbsolute(abs)) return true;
1500
- const rel = node_path.relative(projectRoot, abs).split(node_path.sep).join('/');
1501
- return corpus.includes(rel);
1502
- };
1503
- const next = {};
1504
- for (const [key, value] of entries){
1505
- const paths = Array.isArray(value) ? value : value ? [
1506
- value
1507
- ] : [];
1508
- const kept = paths.filter(isReferenced);
1509
- if (0 !== kept.length) next[key] = Array.isArray(value) ? kept : kept[0];
1510
- }
1511
- return next;
1390
+ function getPlaywrightMetadataDir(packageJsonDir, browser) {
1391
+ return asAbsolute(browserArtifactsDir(packageJsonDir, browser));
1512
1392
  }
1513
- function isUnderPublicDir(entry, projectRoot, publicDir) {
1514
- if (!entry) return false;
1515
- const normalizedEntry = String(entry);
1516
- const candidate = node_path.isAbsolute(normalizedEntry) ? normalizedEntry : node_path.join(projectRoot, normalizedEntry);
1517
- const rel = node_path.relative(publicDir, candidate);
1518
- return Boolean(rel && !rel.startsWith('..') && !node_path.isAbsolute(rel));
1519
- }
1520
- function filterPublicEntrypoints(list, projectRoot, publicDir) {
1521
- const next = {};
1522
- for (const [key, value] of Object.entries(list || {})){
1523
- if (Array.isArray(value)) {
1524
- const filtered = value.filter((entry)=>!isUnderPublicDir(String(entry), projectRoot, publicDir));
1525
- if (filtered.length > 0) next[key] = filtered;
1526
- continue;
1527
- }
1393
+ function createPlaywrightMetadataWriter(options) {
1394
+ const metadataDir = getPlaywrightMetadataDir(options.packageJsonDir, options.browser);
1395
+ const readyPath = asAbsolute(readyContractPath(options.packageJsonDir, options.browser));
1396
+ const eventsPath = asAbsolute(session_paths_eventsPath(options.packageJsonDir, options.browser));
1397
+ const toPort = (value)=>{
1398
+ if ('number' == typeof value && Number.isFinite(value)) return value;
1528
1399
  if ('string' == typeof value) {
1529
- if (!isUnderPublicDir(value, projectRoot, publicDir)) next[key] = value;
1400
+ const parsed = parseInt(value, 10);
1401
+ return Number.isFinite(parsed) ? parsed : null;
1530
1402
  }
1403
+ return null;
1404
+ };
1405
+ const liveOwner = detectLiveDevSessionOwner(readyPath);
1406
+ const foreignLiveDevSession = 'dev' !== options.command && liveOwner ? liveOwner : null;
1407
+ if (foreignLiveDevSession) console.warn(`[extension] a live dev session (pid ${foreignLiveDevSession.pid}) owns ${readyPath}; this ${options.command} run will not rewrite the session's ready.json/events.ndjson. The output dir is shared, so the dev browser may pick up freshly ${options.command}-built files. Stop the dev session first for a clean ${options.command} receipt.`);
1408
+ if ('dev' === options.command && liveOwner && shouldWarnDevOverDev(liveOwner, {
1409
+ instanceId: options.instanceId,
1410
+ instanceExplicit: options.instanceExplicit
1411
+ })) humanWarn(anotherDevSessionActive(options.browser, liveOwner.pid, liveOwner.runId));
1412
+ const toManagedRecords = (dirs)=>Array.isArray(dirs) && dirs.length > 0 ? managedExtensionRecords(options.browser, dirs) : void 0;
1413
+ let managedExtensions = toManagedRecords(options.managedExtensionDirs);
1414
+ const base = {
1415
+ schemaVersion: 2,
1416
+ schema: 1,
1417
+ command: options.command,
1418
+ browser: options.browser,
1419
+ runId: getRunIdForSession(metadataDir),
1420
+ startedAt: nowISO(),
1421
+ distPath: options.distPath,
1422
+ manifestPath: options.manifestPath,
1423
+ port: toPort(options.port),
1424
+ host: options.host,
1425
+ instanceId: options.instanceId,
1426
+ ...options.instanceExplicit ? {
1427
+ instanceExplicit: true
1428
+ } : {},
1429
+ controlPort: toPort(options.controlPort),
1430
+ controlPath: options.controlPath,
1431
+ logsPath: options.logsPath,
1432
+ toolchainVersion: package_namespaceObject.rE,
1433
+ ...readManifestProvenance(options.manifestPath)
1434
+ };
1435
+ function writeReady(status, extra) {
1436
+ if (foreignLiveDevSession) return;
1437
+ plugin_playwright_ensureDirSync(metadataDir);
1438
+ const payload = {
1439
+ ...base,
1440
+ status,
1441
+ pid: process.pid,
1442
+ ts: nowISO(),
1443
+ compiledAt: extra?.compiledAt ?? null,
1444
+ errors: Array.isArray(extra?.errors) ? extra.errors : []
1445
+ };
1446
+ if (extra?.code) payload.code = extra.code;
1447
+ if (extra?.message) payload.message = extra.message;
1448
+ if (managedExtensions) payload.managedExtensions = managedExtensions;
1449
+ const derivedExtensionId = deriveDistExtensionId(options.browser, options.distPath);
1450
+ if (derivedExtensionId) payload.extensionId = derivedExtensionId;
1451
+ try {
1452
+ if (__rspack_external_node_fs_5ea92f0c.existsSync(readyPath)) {
1453
+ const prev = JSON.parse(__rspack_external_node_fs_5ea92f0c.readFileSync(readyPath, 'utf-8'));
1454
+ if ('number' == typeof prev.cdpPort) payload.cdpPort = prev.cdpPort;
1455
+ if ('number' == typeof prev.rdpPort) payload.rdpPort = prev.rdpPort;
1456
+ if (!payload.managedExtensions && Array.isArray(prev.managedExtensions)) payload.managedExtensions = prev.managedExtensions;
1457
+ if ('string' == typeof prev.profilePath) payload.profilePath = prev.profilePath;
1458
+ if ('number' == typeof prev.browserPid) payload.browserPid = prev.browserPid;
1459
+ if ('string' == typeof prev.extensionId && prev.extensionId) payload.extensionId = prev.extensionId;
1460
+ if ('string' == typeof prev.browserExitedAt) {
1461
+ payload.browserExitedAt = prev.browserExitedAt;
1462
+ payload.browserExitCode = prev.browserExitCode ?? null;
1463
+ }
1464
+ if ('string' == typeof prev.executorAttachedAt) {
1465
+ payload.executorAttachedAt = prev.executorAttachedAt;
1466
+ payload.runtime = 'attached';
1467
+ }
1468
+ if ('starting' !== status && 'string' == typeof prev.extensionLoadRefusedAt) {
1469
+ const target = payload;
1470
+ target.extensionLoadRefusedAt = prev.extensionLoadRefusedAt;
1471
+ if ('string' == typeof prev.extensionLoadRefusedReason) target.extensionLoadRefusedReason = prev.extensionLoadRefusedReason;
1472
+ if ('ready' === status) {
1473
+ payload.status = 'error';
1474
+ payload.code = 'extension_load_refused';
1475
+ payload.message = String(prev.message || 'the browser refused to load the extension');
1476
+ }
1477
+ }
1478
+ }
1479
+ } catch {}
1480
+ writeJsonAtomic(readyPath, payload);
1481
+ }
1482
+ function appendEvent(event) {
1483
+ if (foreignLiveDevSession) return;
1484
+ plugin_playwright_ensureDirSync(metadataDir);
1485
+ try {
1486
+ __rspack_external_node_fs_5ea92f0c.appendFileSync(eventsPath, `${JSON.stringify({
1487
+ ...event,
1488
+ runId: event.runId ?? base.runId
1489
+ })}\n`, 'utf-8');
1490
+ } catch {}
1531
1491
  }
1532
- return next;
1533
- }
1534
- function getSpecialFoldersDataForCompiler(compiler) {
1535
- const projectRoot = compiler.options.context || '';
1536
- const publicDir = node_path.join(projectRoot, 'public');
1537
- const data = getSpecialFoldersData({
1538
- manifestPath: node_path.join(projectRoot, 'package.json')
1539
- });
1540
- return finalizeSpecialFoldersData(data, projectRoot, publicDir);
1541
- }
1542
- function getSpecialFoldersDataForProjectRoot(projectRoot) {
1543
- const publicDir = node_path.join(projectRoot, 'public');
1544
- const data = getSpecialFoldersData({
1545
- manifestPath: node_path.join(projectRoot, 'package.json')
1546
- });
1547
- return finalizeSpecialFoldersData(data, projectRoot, publicDir);
1548
- }
1549
- function finalizeSpecialFoldersData(data, projectRoot, publicDir) {
1550
1492
  return {
1551
- ...data,
1552
- pages: filterPublicEntrypoints(data.pages, projectRoot, publicDir),
1553
- scripts: filterPublicEntrypoints(filterUnreferencedScripts(filterNodeToolingScripts(data.scripts), projectRoot), projectRoot, publicDir),
1554
- extensions: {
1555
- dir: './extensions'
1556
- }
1493
+ metadataDir,
1494
+ readyPath,
1495
+ eventsPath,
1496
+ setManagedExtensionDirs (dirs) {
1497
+ managedExtensions = toManagedRecords(dirs);
1498
+ },
1499
+ writeStarting () {
1500
+ if (foreignLiveDevSession) return;
1501
+ plugin_playwright_ensureDirSync(metadataDir);
1502
+ try {
1503
+ __rspack_external_node_fs_5ea92f0c.writeFileSync(eventsPath, '', 'utf-8');
1504
+ } catch {}
1505
+ writeReady('starting');
1506
+ },
1507
+ writeReady (compiledAt) {
1508
+ writeReady('ready', {
1509
+ compiledAt: compiledAt || nowISO()
1510
+ });
1511
+ },
1512
+ writeError (code, message, errors) {
1513
+ writeReady('error', {
1514
+ code,
1515
+ message,
1516
+ errors: Array.isArray(errors) ? errors : [],
1517
+ compiledAt: null
1518
+ });
1519
+ },
1520
+ writeShutdown () {
1521
+ if (foreignLiveDevSession) return;
1522
+ try {
1523
+ if (!__rspack_external_node_fs_5ea92f0c.existsSync(readyPath)) return;
1524
+ const prev = JSON.parse(__rspack_external_node_fs_5ea92f0c.readFileSync(readyPath, 'utf-8'));
1525
+ prev.status = 'stopped';
1526
+ prev.code = 'shutdown';
1527
+ prev.message = 'the dev session ended (watch closed)';
1528
+ prev.ts = nowISO();
1529
+ writeJsonAtomic(readyPath, prev);
1530
+ } catch {}
1531
+ },
1532
+ stampExecutorAttached () {
1533
+ try {
1534
+ if (!__rspack_external_node_fs_5ea92f0c.existsSync(readyPath)) return;
1535
+ const prev = JSON.parse(__rspack_external_node_fs_5ea92f0c.readFileSync(readyPath, 'utf-8'));
1536
+ if ('string' == typeof prev.executorAttachedAt) return;
1537
+ prev.executorAttachedAt = nowISO();
1538
+ prev.runtime = 'attached';
1539
+ if ('string' == typeof prev.extensionLoadRefusedAt) {
1540
+ delete prev.extensionLoadRefusedAt;
1541
+ delete prev.extensionLoadRefusedReason;
1542
+ if ('extension_load_refused' === prev.code) {
1543
+ prev.status = 'ready';
1544
+ delete prev.code;
1545
+ delete prev.message;
1546
+ }
1547
+ }
1548
+ prev.ts = nowISO();
1549
+ writeJsonAtomic(readyPath, prev);
1550
+ } catch {}
1551
+ },
1552
+ appendEvent
1557
1553
  };
1558
1554
  }
1559
- function sanitize(obj) {
1560
- return Object.fromEntries(Object.entries(obj || {}).filter(([, value])=>void 0 !== value));
1561
- }
1562
- const cjsRequire = createRequire(import.meta.url);
1563
- function messages_getLoggingPrefix(type) {
1564
- return messaging_prefix(type);
1565
- }
1566
- function messages_ready(mode, browser) {
1567
- const noun = artifactNoun(browser);
1568
- const state = pintor.green(`ready for ${mode}`);
1569
- const watching = 'development' === mode ? ' Watching for file changes.' : '';
1570
- return `${messages_getLoggingPrefix('success')} ${noun} ${state}.${watching}`;
1555
+ class PlaywrightPlugin {
1556
+ static name = 'plugin-playwright';
1557
+ writer;
1558
+ command;
1559
+ browser;
1560
+ constructor(options){
1561
+ this.browser = String(options.browser || 'chromium');
1562
+ this.command = options.command || ('development' === options.mode ? 'dev' : 'start');
1563
+ this.writer = createPlaywrightMetadataWriter({
1564
+ packageJsonDir: options.packageJsonDir,
1565
+ browser: this.browser,
1566
+ command: this.command,
1567
+ distPath: options.outputPath,
1568
+ manifestPath: options.manifestPath,
1569
+ port: options.port,
1570
+ host: options.host,
1571
+ instanceId: options.instanceId,
1572
+ instanceExplicit: options.instanceExplicit,
1573
+ controlPort: options.controlPort,
1574
+ controlPath: options.controlPath,
1575
+ logsPath: options.logsPath,
1576
+ managedExtensionDirs: options.managedExtensionDirs
1577
+ });
1578
+ }
1579
+ apply(compiler) {
1580
+ this.writer.writeStarting();
1581
+ compiler.hooks.compile.tap(PlaywrightPlugin.name, ()=>{
1582
+ this.writer.appendEvent({
1583
+ type: 'compile_start',
1584
+ ts: nowISO(),
1585
+ command: this.command,
1586
+ browser: this.browser
1587
+ });
1588
+ });
1589
+ compiler.hooks.done.tap(PlaywrightPlugin.name, (stats)=>{
1590
+ const durationMs = Number((stats?.compilation?.endTime || 0) - (stats?.compilation?.startTime || 0));
1591
+ const hasErrors = Boolean(stats?.hasErrors?.());
1592
+ const errorsJson = stats?.toJson?.({
1593
+ all: false,
1594
+ errors: true
1595
+ });
1596
+ const errorsCount = Array.isArray(errorsJson?.errors) ? errorsJson.errors.length : 0;
1597
+ if (hasErrors) {
1598
+ const errorMessages = formatStatsErrors(errorsJson?.errors);
1599
+ const contractErrors = errorMessages.length ? errorMessages : [
1600
+ `errors: ${String(errorsCount || 1)}`
1601
+ ];
1602
+ this.writer.appendEvent({
1603
+ type: 'compile_error',
1604
+ ts: nowISO(),
1605
+ command: this.command,
1606
+ browser: this.browser,
1607
+ durationMs: Number.isFinite(durationMs) ? durationMs : void 0,
1608
+ errorCount: Number.isFinite(errorsCount) ? errorsCount : 1,
1609
+ errors: contractErrors
1610
+ });
1611
+ this.writer.writeError('compile_error', 'Compilation failed', contractErrors);
1612
+ return;
1613
+ }
1614
+ this.writer.appendEvent({
1615
+ type: 'compile_success',
1616
+ ts: nowISO(),
1617
+ command: this.command,
1618
+ browser: this.browser,
1619
+ durationMs: Number.isFinite(durationMs) ? durationMs : void 0,
1620
+ errorCount: 0
1621
+ });
1622
+ this.writer.writeReady(nowISO());
1623
+ });
1624
+ compiler.hooks.failed.tap(PlaywrightPlugin.name, (error)=>{
1625
+ this.writer.appendEvent({
1626
+ type: 'compile_error',
1627
+ ts: nowISO(),
1628
+ command: this.command,
1629
+ browser: this.browser,
1630
+ errorCount: 1
1631
+ });
1632
+ this.writer.writeError('compile_failed', error instanceof Error ? error.message : String(error));
1633
+ });
1634
+ compiler.hooks.watchClose.tap(PlaywrightPlugin.name, ()=>{
1635
+ this.writer.appendEvent({
1636
+ type: 'shutdown',
1637
+ ts: nowISO(),
1638
+ command: this.command,
1639
+ browser: this.browser
1640
+ });
1641
+ if ('dev' === this.command) this.writer.writeShutdown();
1642
+ });
1643
+ }
1571
1644
  }
1572
- function readJsonRecord(filePath) {
1645
+ function isDir(p) {
1573
1646
  try {
1574
- return JSON.parse(__rspack_external_node_fs_5ea92f0c.readFileSync(filePath, 'utf8'));
1647
+ return __rspack_external_node_fs_5ea92f0c.existsSync(p) && __rspack_external_node_fs_5ea92f0c.statSync(p).isDirectory();
1575
1648
  } catch {
1576
- return null;
1649
+ return false;
1577
1650
  }
1578
1651
  }
1579
- function capitalizeToken(value) {
1580
- return value.split('-').filter(Boolean).map((token)=>token.charAt(0).toUpperCase() + token.slice(1)).join('-');
1581
- }
1582
- function getExtensionVersion() {
1583
- return process.env.EXTENSION_DEVELOP_VERSION || process.env.EXTENSION_CLI_VERSION || (()=>{
1584
- try {
1585
- return cjsRequire('../package.json').version;
1586
- } catch {
1587
- return 'unknown';
1588
- }
1589
- })();
1590
- }
1591
- function collapseHomeDirInCardValue(value) {
1592
- const raw = String(value || '');
1593
- const home = __rspack_external_node_os_74b4b876.homedir();
1594
- if (!home || !raw.startsWith(home)) return raw;
1595
- const rest = raw.slice(home.length);
1596
- if ('' === rest) return '~';
1597
- if (rest.startsWith(__rspack_external_node_path_c5b9b54f.sep) || rest.startsWith('/')) return `~${rest}`;
1598
- return raw;
1599
- }
1600
- function browserRunnerDisabled(args) {
1601
- const manifest = readJsonRecord(args.manifestPath);
1602
- const ready = readJsonRecord(args.readyPath);
1603
- const browserLabel = capitalizeToken(String(args.browser || 'unknown'));
1604
- const runId = String(ready?.runId || '').trim();
1605
- const pid = Number.isInteger(ready?.pid) ? String(ready?.pid) : '';
1606
- const runLabel = runId ? `${runId}${pid ? ` · PID ${pid}` : ''}` : pid ? `PID ${pid}` : '';
1607
- const extensionName = String(manifest?.name || 'Extension');
1608
- const extensionVersion = String(manifest?.version || '').trim();
1609
- const extensionLabel = extensionVersion ? `${extensionName} ${extensionVersion}` : extensionName;
1610
- const extensionJsVersion = getExtensionVersion();
1611
- const updateSuffix = process.env.EXTENSION_CLI_UPDATE_SUFFIX || '';
1612
- if (updateSuffix) delete process.env.EXTENSION_CLI_UPDATE_SUFFIX;
1613
- return card({
1614
- version: extensionJsVersion,
1615
- suffix: updateSuffix,
1616
- rows: [
1617
- {
1618
- label: 'Browser',
1619
- value: browserRowValue(String(args.browser || 'unknown'), `${browserLabel} (no-browser mode)`)
1620
- },
1621
- {
1622
- label: 'Extension',
1623
- value: extensionLabel
1624
- },
1625
- {
1626
- label: 'Output',
1627
- value: collapseHomeDirInCardValue(String(args.distPath || '').trim())
1628
- },
1629
- {
1630
- label: 'Run ID',
1631
- value: runLabel
1632
- }
1633
- ]
1634
- });
1635
- }
1636
- function shouldWarnPortConflict(requested, actual) {
1637
- const requestedPort = Number(requested);
1638
- return Number.isFinite(requestedPort) && 0 !== requestedPort && requestedPort !== actual;
1639
- }
1640
- function portInUse(requestedPort, newPort) {
1641
- return `${messages_getLoggingPrefix('warn')} Port ${requestedPort} is in use.\nThe dev server listens on port ${newPort} instead.`;
1652
+ function isFile(p) {
1653
+ try {
1654
+ return __rspack_external_node_fs_5ea92f0c.existsSync(p) && __rspack_external_node_fs_5ea92f0c.statSync(p).isFile();
1655
+ } catch {
1656
+ return false;
1657
+ }
1642
1658
  }
1643
- function extensionJsRunnerError(error) {
1644
- return `${messages_getLoggingPrefix('error')} Extension.js couldn't start the runner.\n${pintor.red(String(error))}`;
1659
+ function toAbs(projectRoot, p) {
1660
+ return __rspack_external_node_path_c5b9b54f.isAbsolute(p) ? p : __rspack_external_node_path_c5b9b54f.resolve(projectRoot, p);
1645
1661
  }
1646
- function autoExitModeEnabled(ms) {
1647
- return `${messages_getLoggingPrefix('info')} Auto-exit is enabled.\nThe process exits after ${ms} ms of idle time.`;
1662
+ function isValidExtensionRoot(dir) {
1663
+ if (!isDir(dir)) return false;
1664
+ return isFile(__rspack_external_node_path_c5b9b54f.join(dir, 'manifest.json'));
1648
1665
  }
1649
- function autoExitTriggered(ms) {
1650
- return `${messages_getLoggingPrefix('info')} Auto-exit triggered after ${ms} ms.\nThe process cleans up and exits now.`;
1666
+ function normalizeCompanionConfig(config) {
1667
+ const explicitPaths = [];
1668
+ let scanDir;
1669
+ if (Array.isArray(config)) explicitPaths.push(...config.filter((p)=>'string' == typeof p));
1670
+ else if (config && 'object' == typeof config) {
1671
+ if (Array.isArray(config.paths)) explicitPaths.push(...config.paths.filter((p)=>'string' == typeof p));
1672
+ if ('string' == typeof config.dir && config.dir.trim().length > 0) scanDir = config.dir.trim();
1673
+ }
1674
+ return {
1675
+ dir: scanDir,
1676
+ paths: explicitPaths
1677
+ };
1651
1678
  }
1652
- function autoExitForceKill(ms) {
1653
- return `${messages_getLoggingPrefix('warn')} The process didn't exit within ${ms} ms, so it exits forcibly now.`;
1679
+ function resolve_config_isPathLike(value) {
1680
+ if (__rspack_external_node_path_c5b9b54f.isAbsolute(value)) return true;
1681
+ if (value.startsWith('./') || value.startsWith('../')) return true;
1682
+ return value.includes('/') || value.includes('\\');
1654
1683
  }
1655
- function devServerStartTimeout(ms) {
1656
- return [
1657
- `${messages_getLoggingPrefix('warn')} The dev server didn't start within ${ms} ms.`,
1658
- "The bundler may have hit an error before emitting the first build.",
1659
- `If nothing else prints, set ${pintor.blue('EXTENSION_VERBOSE=1')} to see more logs.`
1660
- ].join('\n');
1684
+ function isSubpathOf(parent, child) {
1685
+ const rel = __rspack_external_node_path_c5b9b54f.relative(parent, child);
1686
+ return '' !== rel && !rel.startsWith('..') && !__rspack_external_node_path_c5b9b54f.isAbsolute(rel);
1661
1687
  }
1662
- function bundlerFatalError(error) {
1663
- const text = error instanceof Error ? error.stack || error.message : String(error);
1664
- return `${messages_getLoggingPrefix('error')} The build failed to start.\n${pintor.red(text)}`;
1688
+ function getBrowserFolder(browser) {
1689
+ if (browser && isGeckoBasedBrowser(String(browser))) return 'firefox';
1690
+ if ('edge' === browser) return 'edge';
1691
+ return 'chrome';
1665
1692
  }
1666
- function bundlerRecompiling() {
1667
- return `${messages_getLoggingPrefix('info')} Recompiling due to file changes…`;
1693
+ function parseChromeWebStoreId(url) {
1694
+ if ('chromewebstore.google.com' !== url.hostname) return null;
1695
+ const match = url.pathname.match(/\/([a-z]{32})(?:\/|$)/i);
1696
+ return match ? match[1] : null;
1668
1697
  }
1669
- function noEntrypointsDetected(port) {
1670
- return [
1671
- `${messages_getLoggingPrefix('warn')} The first compilation produced no entrypoints or assets.`,
1672
- `The dev server is running on 127.0.0.1:${port}, but nothing is being built.`,
1673
- "Possible causes:",
1674
- "- Empty or missing entry configuration.",
1675
- "- Extension plugins are disabled, so entries aren't derived from the manifest.",
1676
- "- All sources are ignored or excluded.",
1677
- `Set ${pintor.blue('EXTENSION_VERBOSE=1')} to see verbose logs, or review your extension config.`
1678
- ].join('\n');
1698
+ function parseEdgeAddonsId(url) {
1699
+ if ('microsoftedge.microsoft.com' !== url.hostname) return null;
1700
+ const match = url.pathname.match(/\/([a-z]{32})(?:\/|$)/i);
1701
+ return match ? match[1] : null;
1679
1702
  }
1680
- function spacerLine() {
1681
- return ' ';
1703
+ function parseAmoSlug(url) {
1704
+ if ('addons.mozilla.org' !== url.hostname) return null;
1705
+ const match = url.pathname.match(/\/addon\/([^/]+)(?:\/|$)/i);
1706
+ return match ? match[1] : null;
1682
1707
  }
1683
- function getDarkModeDefaults(browser) {
1684
- if (isChromiumBasedBrowser(String(browser))) return {
1685
- browserFlags: [
1686
- '--force-dark-mode',
1687
- '--enable-features=WebUIDarkMode'
1688
- ],
1689
- preferences: {}
1708
+ function parseStoreUrl(raw) {
1709
+ let url;
1710
+ try {
1711
+ url = new URL(raw);
1712
+ } catch {
1713
+ return null;
1714
+ }
1715
+ const chromeId = parseChromeWebStoreId(url);
1716
+ if (chromeId) return {
1717
+ browser: 'chrome',
1718
+ id: chromeId
1690
1719
  };
1691
- if (isGeckoBasedBrowser(String(browser))) return {
1692
- browserFlags: [],
1693
- preferences: {
1694
- 'ui.systemUsesDarkTheme': 1,
1695
- 'layout.css.prefers-color-scheme.content-override': 2,
1696
- 'devtools.theme': 'dark'
1697
- }
1720
+ const edgeId = parseEdgeAddonsId(url);
1721
+ if (edgeId) return {
1722
+ browser: 'edge',
1723
+ id: edgeId
1698
1724
  };
1699
- return {
1700
- browserFlags: [],
1701
- preferences: {}
1725
+ const amoSlug = parseAmoSlug(url);
1726
+ if (amoSlug) return {
1727
+ browser: 'firefox',
1728
+ id: amoSlug
1702
1729
  };
1730
+ return null;
1703
1731
  }
1704
- function withDarkMode(config) {
1705
- const defaults = getDarkModeDefaults(config.browser);
1706
- const existingFlags = Array.isArray(config.browserFlags) ? [
1707
- ...config.browserFlags
1708
- ] : [];
1709
- const nextFlags = [
1710
- ...existingFlags
1711
- ];
1712
- for (const flag of defaults.browserFlags || [])if (!nextFlags.some((f)=>String(f).trim() === flag)) nextFlags.push(flag);
1713
- const nextPreferences = {
1714
- ...config.preferences || {},
1715
- ...Object.fromEntries(Object.entries(defaults.preferences || {}).filter(([k])=>!(k in (config.preferences || {}))))
1716
- };
1717
- return {
1718
- ...config,
1719
- browserFlags: nextFlags,
1720
- preferences: nextPreferences
1721
- };
1732
+ function ensurePathsUnderExtensions(projectRoot, paths) {
1733
+ const extensionsRoot = __rspack_external_node_path_c5b9b54f.resolve(projectRoot, 'extensions');
1734
+ return paths.map((p)=>{
1735
+ const abs = toAbs(projectRoot, p);
1736
+ if (abs !== extensionsRoot && !isSubpathOf(extensionsRoot, abs) && __rspack_external_node_path_c5b9b54f.resolve(abs) !== extensionsRoot) throw new Error(`Companion extensions must be inside ${extensionsRoot}.\nInvalid path: ${abs}`);
1737
+ return abs;
1738
+ });
1722
1739
  }
1723
- function hasNewTabOverride(extensionDir) {
1724
- const manifestPath = __rspack_external_node_path_c5b9b54f.join(extensionDir, 'manifest.json');
1725
- if (!__rspack_external_node_fs_5ea92f0c.existsSync(manifestPath)) return false;
1726
- try {
1727
- const raw = __rspack_external_node_fs_5ea92f0c.readFileSync(manifestPath, 'utf-8');
1728
- const manifest = JSON.parse(stripBom(raw));
1729
- const newtab = manifest?.chrome_url_overrides?.newtab;
1730
- return 'string' == typeof newtab && newtab.trim().length > 0;
1731
- } catch {
1732
- return false;
1740
+ function findExtensionRoots(dir, maxDepth = 3) {
1741
+ const found = [];
1742
+ function walk(current, depth) {
1743
+ if (depth > maxDepth) return;
1744
+ if (!isDir(current)) return;
1745
+ if (isValidExtensionRoot(current)) return void found.push(current);
1746
+ let entries = [];
1747
+ try {
1748
+ entries = __rspack_external_node_fs_5ea92f0c.readdirSync(current, {
1749
+ withFileTypes: true
1750
+ });
1751
+ } catch {
1752
+ return;
1753
+ }
1754
+ for (const ent of entries)if (ent.isDirectory()) {
1755
+ if (!ent.name.startsWith('.')) walk(__rspack_external_node_path_c5b9b54f.join(current, ent.name), depth + 1);
1756
+ }
1733
1757
  }
1758
+ walk(dir, 0);
1759
+ return found;
1734
1760
  }
1735
- function resolveBuiltInExtensionForBrowser(input) {
1736
- const engine = devtoolsEngineFor(input.browser);
1737
- const packageRelativeCandidates = [
1738
- __rspack_external_node_path_c5b9b54f.join('dist', input.packageName, engine)
1739
- ];
1740
- const normalizedBaseDir = __rspack_external_node_path_c5b9b54f.normalize(input.baseDir);
1741
- const parentName = __rspack_external_node_path_c5b9b54f.basename(__rspack_external_node_path_c5b9b54f.dirname(normalizedBaseDir));
1742
- const baseName = __rspack_external_node_path_c5b9b54f.basename(normalizedBaseDir);
1743
- if ('programs' === parentName && 'develop' === baseName) packageRelativeCandidates.push(__rspack_external_node_path_c5b9b54f.join('..', '..', 'extensions', input.packageName, 'dist', engine));
1744
- for (const rel of packageRelativeCandidates){
1745
- const candidate = __rspack_external_node_path_c5b9b54f.resolve(input.baseDir, rel);
1746
- if (__rspack_external_node_fs_5ea92f0c.existsSync(candidate)) return candidate;
1761
+ async function runExtensionFromStore(url, outDir) {
1762
+ const isAuthor = isDebug();
1763
+ await fetchExtensionFromStore(url, {
1764
+ outDir,
1765
+ extract: true,
1766
+ logger: isAuthor ? {
1767
+ onInfo: (message)=>console.log(message),
1768
+ onWarn: (message)=>console.warn(message),
1769
+ onError: (message, error)=>console.error(message, error)
1770
+ } : void 0
1771
+ });
1772
+ }
1773
+ async function resolveStoreExtensionToPath(opts) {
1774
+ const { projectRoot, storeUrl, browser, id } = opts;
1775
+ const extensionsRoot = __rspack_external_node_path_c5b9b54f.resolve(projectRoot, 'extensions');
1776
+ const targetRoot = __rspack_external_node_path_c5b9b54f.join(extensionsRoot, browser, id);
1777
+ const manifestPath = __rspack_external_node_path_c5b9b54f.join(targetRoot, 'manifest.json');
1778
+ if (isFile(manifestPath)) return targetRoot;
1779
+ await runExtensionFromStore(storeUrl, __rspack_external_node_path_c5b9b54f.join(extensionsRoot, browser));
1780
+ const candidates = findExtensionRoots(__rspack_external_node_path_c5b9b54f.join(extensionsRoot, browser));
1781
+ let selected;
1782
+ if (1 === candidates.length) selected = candidates[0];
1783
+ else if (candidates.length > 1) {
1784
+ const directMatch = candidates.find((c)=>__rspack_external_node_path_c5b9b54f.basename(c) === id);
1785
+ const versionedMatch = candidates.find((c)=>__rspack_external_node_path_c5b9b54f.basename(c).startsWith(`${id}@`));
1786
+ if (directMatch) selected = directMatch;
1787
+ else if (versionedMatch) selected = versionedMatch;
1747
1788
  }
1789
+ if (!selected) throw new Error(`Could not locate an unpacked extension from ${storeUrl}.`);
1790
+ __rspack_external_node_fs_5ea92f0c.mkdirSync(__rspack_external_node_path_c5b9b54f.dirname(targetRoot), {
1791
+ recursive: true
1792
+ });
1793
+ if (__rspack_external_node_path_c5b9b54f.basename(selected).startsWith(`${id}@`)) __rspack_external_node_fs_5ea92f0c.renameSync(selected, targetRoot);
1794
+ return targetRoot;
1748
1795
  }
1749
- const RESERVED_BUILT_IN_NAMES = new Set([
1750
- 'extension-js-devtools',
1751
- 'extension-js-theme'
1796
+ async function resolveCompanionExtensionsConfig(opts) {
1797
+ const { projectRoot, browser, config } = opts;
1798
+ if (!config) return;
1799
+ const normalized = normalizeCompanionConfig(config);
1800
+ const runtimeBrowser = getBrowserFolder(browser);
1801
+ const resolvedPaths = [];
1802
+ const localPaths = [];
1803
+ for (const entry of normalized.paths){
1804
+ const parsedStore = parseStoreUrl(entry);
1805
+ if (parsedStore) {
1806
+ if (parsedStore.browser !== runtimeBrowser) continue;
1807
+ const resolvedPath = await resolveStoreExtensionToPath({
1808
+ projectRoot,
1809
+ storeUrl: entry,
1810
+ browser: parsedStore.browser,
1811
+ id: parsedStore.id
1812
+ });
1813
+ resolvedPaths.push(resolvedPath);
1814
+ continue;
1815
+ }
1816
+ if (resolve_config_isPathLike(entry)) localPaths.push(entry);
1817
+ }
1818
+ if (localPaths.length > 0) {
1819
+ const absLocalPaths = ensurePathsUnderExtensions(projectRoot, localPaths);
1820
+ resolvedPaths.push(...absLocalPaths);
1821
+ }
1822
+ const output = {};
1823
+ if ('string' == typeof normalized.dir) {
1824
+ const absDir = toAbs(projectRoot, normalized.dir);
1825
+ const extensionsRoot = __rspack_external_node_path_c5b9b54f.resolve(projectRoot, 'extensions');
1826
+ if (absDir !== extensionsRoot && !isSubpathOf(extensionsRoot, absDir)) throw new Error(`extensions.dir must be inside ${extensionsRoot}.\nInvalid dir: ${absDir}`);
1827
+ output.dir = normalized.dir;
1828
+ }
1829
+ if (resolvedPaths.length > 0) output.paths = resolvedPaths;
1830
+ return output;
1831
+ }
1832
+ const NODE_BUILTINS = new Set([
1833
+ 'assert',
1834
+ 'buffer',
1835
+ 'child_process',
1836
+ 'cluster',
1837
+ 'console',
1838
+ 'constants',
1839
+ 'crypto',
1840
+ 'dgram',
1841
+ 'dns',
1842
+ 'domain',
1843
+ 'events',
1844
+ 'fs',
1845
+ 'fs/promises',
1846
+ 'http',
1847
+ 'http2',
1848
+ 'https',
1849
+ 'inspector',
1850
+ 'module',
1851
+ 'net',
1852
+ 'os',
1853
+ 'path',
1854
+ 'perf_hooks',
1855
+ 'process',
1856
+ 'punycode',
1857
+ 'querystring',
1858
+ 'readline',
1859
+ 'repl',
1860
+ 'stream',
1861
+ 'string_decoder',
1862
+ 'timers',
1863
+ 'tls',
1864
+ 'trace_events',
1865
+ 'tty',
1866
+ 'url',
1867
+ 'util',
1868
+ 'v8',
1869
+ 'vm',
1870
+ 'worker_threads',
1871
+ 'zlib'
1872
+ ]);
1873
+ const NODE_BUILD_TOOLS = new Set([
1874
+ 'fs-extra',
1875
+ 'esbuild',
1876
+ 'playwright',
1877
+ 'playwright-core',
1878
+ 'puppeteer',
1879
+ 'puppeteer-core',
1880
+ 'webpack',
1881
+ 'rollup',
1882
+ 'vite',
1883
+ 'replace-in-file',
1884
+ 'zip-dir',
1885
+ 'archiver',
1886
+ 'adm-zip',
1887
+ 'chokidar',
1888
+ 'glob',
1889
+ 'fast-glob',
1890
+ 'rimraf',
1891
+ 'yargs',
1892
+ 'execa',
1893
+ 'cross-spawn',
1894
+ 'shelljs',
1895
+ 'web-ext',
1896
+ 'dotenv',
1897
+ 'node-fetch',
1898
+ 'minimist',
1899
+ 'ora',
1900
+ 'chalk',
1901
+ 'gulp',
1902
+ 'grunt',
1903
+ 'ncp',
1904
+ 'del',
1905
+ 'cpy',
1906
+ 'tsx',
1907
+ 'ts-node',
1908
+ 'nodemon',
1909
+ 'concurrently'
1752
1910
  ]);
1753
- function isReservedBuiltInPath(extensionPath) {
1754
- const base = __rspack_external_node_path_c5b9b54f.basename(__rspack_external_node_path_c5b9b54f.normalize(extensionPath));
1755
- if (RESERVED_BUILT_IN_NAMES.has(base)) return true;
1756
- const segments = __rspack_external_node_path_c5b9b54f.normalize(extensionPath).split(__rspack_external_node_path_c5b9b54f.sep);
1757
- return segments.some((segment)=>RESERVED_BUILT_IN_NAMES.has(segment));
1911
+ function importsNodeOnly(specifier) {
1912
+ if (specifier.startsWith('node:')) return true;
1913
+ const bare = specifier.startsWith('@') ? specifier.split('/').slice(0, 2).join('/') : specifier.split('/')[0];
1914
+ return NODE_BUILTINS.has(specifier) || NODE_BUILTINS.has(bare) || NODE_BUILD_TOOLS.has(bare);
1758
1915
  }
1759
- function dedupeByResolvedPath(paths) {
1760
- const seen = new Set();
1761
- const result = [];
1762
- for (const entry of paths){
1763
- const resolved = __rspack_external_node_path_c5b9b54f.resolve(entry);
1764
- if (!seen.has(resolved)) {
1765
- seen.add(resolved);
1766
- result.push(entry);
1767
- }
1916
+ function isNodeToolingScript(absPath) {
1917
+ let source;
1918
+ try {
1919
+ source = node_fs.readFileSync(absPath, 'utf8');
1920
+ } catch {
1921
+ return false;
1768
1922
  }
1769
- return result;
1923
+ if (/^#!.*\bnode\b/.test(source)) return true;
1924
+ const specifierRe = /(?:require\s*\(\s*|(?:import|export)\b[^'"()]*?\bfrom\s*|import\s*)['"]([^'"]+)['"]/g;
1925
+ let match;
1926
+ while(null !== (match = specifierRe.exec(source)))if (importsNodeOnly(match[1])) return true;
1927
+ return false;
1770
1928
  }
1771
- function computeExtensionsToLoad(baseDir, mode, browser, userExtensionOutputPath, extraExtensionDirs = [], userManifestPath) {
1772
- const list = [];
1773
- try {
1774
- const devtoolsForBrowser = resolveBuiltInExtensionForBrowser({
1775
- baseDir,
1776
- packageName: 'extension-js-devtools',
1777
- browser
1778
- });
1779
- const themeForBrowser = resolveBuiltInExtensionForBrowser({
1780
- baseDir,
1781
- packageName: 'extension-js-theme',
1782
- browser
1929
+ function filterNodeToolingScripts(list) {
1930
+ const next = {};
1931
+ for (const [key, value] of Object.entries(list || {})){
1932
+ const paths = Array.isArray(value) ? value : value ? [
1933
+ value
1934
+ ] : [];
1935
+ const kept = paths.filter((entry)=>{
1936
+ const abs = String(entry);
1937
+ if (!node_path.isAbsolute(abs)) return true;
1938
+ return !isNodeToolingScript(abs);
1783
1939
  });
1784
- const userHasNewTabOverride = hasNewTabOverride(userExtensionOutputPath) || ('string' == typeof userManifestPath ? hasNewTabOverride(__rspack_external_node_path_c5b9b54f.dirname(userManifestPath)) : false);
1785
- const devtoolsHasNewTabOverride = devtoolsForBrowser ? hasNewTabOverride(devtoolsForBrowser) : false;
1786
- const shouldSkipDevtoolsForNtpConflict = userHasNewTabOverride && devtoolsHasNewTabOverride;
1787
- if (devtoolsForBrowser && !shouldSkipDevtoolsForNtpConflict) list.push(devtoolsForBrowser);
1788
- if (themeForBrowser) list.push(themeForBrowser);
1789
- } catch {}
1790
- for (const p of extraExtensionDirs)if (!isReservedBuiltInPath(p)) list.push(p);
1791
- list.push(userExtensionOutputPath);
1792
- return dedupeByResolvedPath(list);
1793
- }
1794
- function idFromHash(hash) {
1795
- const hex = hash.subarray(0, 16).toString('hex');
1796
- let id = '';
1797
- for (const char of hex)id += String.fromCharCode('a'.charCodeAt(0) + parseInt(char, 16));
1798
- return id;
1799
- }
1800
- function chromiumExtensionIdFromKey(manifestKey) {
1801
- const decoded = Buffer.from(manifestKey, 'base64');
1802
- return idFromHash(createHash('sha256').update(decoded).digest());
1940
+ if (0 !== kept.length) next[key] = Array.isArray(value) ? kept : kept[0];
1941
+ }
1942
+ return next;
1803
1943
  }
1804
- function chromiumExtensionIdFromPath(extensionDir) {
1805
- const absolute = __rspack_external_node_path_c5b9b54f.resolve(extensionDir);
1806
- const bytes = 'win32' === process.platform ? Buffer.from(absolute.replace(/[A-Z]/g, (char)=>char.toLowerCase()), 'utf16le') : Buffer.from(absolute, 'utf8');
1807
- return idFromHash(createHash('sha256').update(bytes).digest());
1944
+ const REFERENCE_SOURCE_EXTS = new Set([
1945
+ '.html',
1946
+ '.htm',
1947
+ '.js',
1948
+ '.jsx',
1949
+ '.mjs',
1950
+ '.cjs',
1951
+ '.ts',
1952
+ '.tsx',
1953
+ '.mts',
1954
+ '.cts',
1955
+ '.vue',
1956
+ '.svelte'
1957
+ ]);
1958
+ const REFERENCE_SKIP_DIRS = new Set([
1959
+ 'node_modules',
1960
+ 'dist',
1961
+ 'build',
1962
+ 'out',
1963
+ '.output',
1964
+ 'coverage',
1965
+ '.next',
1966
+ '.cache',
1967
+ '.git',
1968
+ '.turbo',
1969
+ "scripts"
1970
+ ]);
1971
+ const REFERENCE_MAX_FILES = 4000;
1972
+ const REFERENCE_MAX_BYTES = 12582912;
1973
+ function collectReferenceCorpus(projectRoot) {
1974
+ const parts = [];
1975
+ let files = 0;
1976
+ let bytes = 0;
1977
+ const walk = (dir)=>{
1978
+ if (files >= REFERENCE_MAX_FILES || bytes >= REFERENCE_MAX_BYTES) return;
1979
+ let entries;
1980
+ try {
1981
+ entries = node_fs.readdirSync(dir, {
1982
+ withFileTypes: true
1983
+ });
1984
+ } catch {
1985
+ return;
1986
+ }
1987
+ for (const entry of entries){
1988
+ if (files >= REFERENCE_MAX_FILES || bytes >= REFERENCE_MAX_BYTES) return;
1989
+ const full = node_path.join(dir, entry.name);
1990
+ if (entry.isDirectory()) {
1991
+ if (REFERENCE_SKIP_DIRS.has(entry.name) || entry.name.startsWith('.')) continue;
1992
+ walk(full);
1993
+ continue;
1994
+ }
1995
+ if (!entry.isFile()) continue;
1996
+ const ext = node_path.extname(entry.name).toLowerCase();
1997
+ const isManifest = /^manifest\b.*\.json$/i.test(entry.name);
1998
+ if (isManifest || REFERENCE_SOURCE_EXTS.has(ext)) try {
1999
+ const text = node_fs.readFileSync(full, 'utf8');
2000
+ parts.push(text);
2001
+ files += 1;
2002
+ bytes += text.length;
2003
+ } catch {}
2004
+ }
2005
+ };
2006
+ walk(projectRoot);
2007
+ return 0 === files ? '' : parts.join('\n');
1808
2008
  }
1809
- function readManifest(extensionDir) {
1810
- try {
1811
- const raw = __rspack_external_node_fs_5ea92f0c.readFileSync(__rspack_external_node_path_c5b9b54f.join(extensionDir, 'manifest.json'), 'utf-8');
1812
- const parsed = JSON.parse(stripBom(raw));
1813
- return parsed && 'object' == typeof parsed ? parsed : null;
1814
- } catch {
1815
- return null;
2009
+ function filterUnreferencedScripts(list, projectRoot) {
2010
+ const entries = Object.entries(list || {});
2011
+ if (0 === entries.length) return list || {};
2012
+ const corpus = collectReferenceCorpus(projectRoot);
2013
+ if ('' === corpus) return list || {};
2014
+ const isReferenced = (entry)=>{
2015
+ const abs = String(entry);
2016
+ if (!node_path.isAbsolute(abs)) return true;
2017
+ const rel = node_path.relative(projectRoot, abs).split(node_path.sep).join('/');
2018
+ return corpus.includes(rel);
2019
+ };
2020
+ const next = {};
2021
+ for (const [key, value] of entries){
2022
+ const paths = Array.isArray(value) ? value : value ? [
2023
+ value
2024
+ ] : [];
2025
+ const kept = paths.filter(isReferenced);
2026
+ if (0 !== kept.length) next[key] = Array.isArray(value) ? kept : kept[0];
1816
2027
  }
2028
+ return next;
1817
2029
  }
1818
- function chromiumExtensionId(extensionDir) {
1819
- const manifest = readManifest(extensionDir);
1820
- const key = manifest?.key;
1821
- if ('string' == typeof key && key.trim().length > 0) try {
1822
- return chromiumExtensionIdFromKey(key);
1823
- } catch {}
1824
- return chromiumExtensionIdFromPath(extensionDir);
2030
+ function isUnderPublicDir(entry, projectRoot, publicDir) {
2031
+ if (!entry) return false;
2032
+ const normalizedEntry = String(entry);
2033
+ const candidate = node_path.isAbsolute(normalizedEntry) ? normalizedEntry : node_path.join(projectRoot, normalizedEntry);
2034
+ const rel = node_path.relative(publicDir, candidate);
2035
+ return Boolean(rel && !rel.startsWith('..') && !node_path.isAbsolute(rel));
1825
2036
  }
1826
- function geckoExtensionId(extensionDir) {
1827
- const manifest = readManifest(extensionDir);
1828
- const settings = manifest?.browser_specific_settings ?? manifest?.applications;
1829
- const id = settings?.gecko?.id;
1830
- return 'string' == typeof id && id.trim().length > 0 ? id : void 0;
2037
+ function filterPublicEntrypoints(list, projectRoot, publicDir) {
2038
+ const next = {};
2039
+ for (const [key, value] of Object.entries(list || {})){
2040
+ if (Array.isArray(value)) {
2041
+ const filtered = value.filter((entry)=>!isUnderPublicDir(String(entry), projectRoot, publicDir));
2042
+ if (filtered.length > 0) next[key] = filtered;
2043
+ continue;
2044
+ }
2045
+ if ('string' == typeof value) {
2046
+ if (!isUnderPublicDir(value, projectRoot, publicDir)) next[key] = value;
2047
+ }
2048
+ }
2049
+ return next;
1831
2050
  }
1832
- function managedExtensionRecords(browser, extensionDirs) {
1833
- const gecko = isGeckoBasedBrowser(browser);
1834
- return extensionDirs.map((dir)=>{
1835
- const absolute = __rspack_external_node_path_c5b9b54f.resolve(dir);
1836
- const id = gecko ? geckoExtensionId(absolute) : chromiumExtensionId(absolute);
1837
- return id ? {
1838
- path: absolute,
1839
- id
1840
- } : {
1841
- path: absolute
1842
- };
2051
+ function getSpecialFoldersDataForCompiler(compiler) {
2052
+ const projectRoot = compiler.options.context || '';
2053
+ const publicDir = node_path.join(projectRoot, 'public');
2054
+ const data = getSpecialFoldersData({
2055
+ manifestPath: node_path.join(projectRoot, 'package.json')
1843
2056
  });
2057
+ return finalizeSpecialFoldersData(data, projectRoot, publicDir);
1844
2058
  }
1845
- function nowISO() {
1846
- return new Date().toISOString();
1847
- }
1848
- const MAX_CONTRACT_ERRORS = 10;
1849
- const ANSI_PATTERN = /\u001b\[[0-9;]*m/g;
1850
- function formatStatsErrors(errors) {
1851
- if (!Array.isArray(errors)) return [];
1852
- return errors.slice(0, MAX_CONTRACT_ERRORS).map((error)=>{
1853
- const message = error && 'object' == typeof error ? String(error.message ?? '') : String(error ?? '');
1854
- return message.replace(ANSI_PATTERN, '').trim();
1855
- }).filter(Boolean);
2059
+ function getSpecialFoldersDataForProjectRoot(projectRoot) {
2060
+ const publicDir = node_path.join(projectRoot, 'public');
2061
+ const data = getSpecialFoldersData({
2062
+ manifestPath: node_path.join(projectRoot, 'package.json')
2063
+ });
2064
+ return finalizeSpecialFoldersData(data, projectRoot, publicDir);
1856
2065
  }
1857
- function createRunId() {
1858
- return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
2066
+ function finalizeSpecialFoldersData(data, projectRoot, publicDir) {
2067
+ return {
2068
+ ...data,
2069
+ pages: filterPublicEntrypoints(data.pages, projectRoot, publicDir),
2070
+ scripts: filterPublicEntrypoints(filterUnreferencedScripts(filterNodeToolingScripts(data.scripts), projectRoot), projectRoot, publicDir),
2071
+ extensions: {
2072
+ dir: './extensions'
2073
+ }
2074
+ };
1859
2075
  }
1860
- const runIdByMetadataDir = new Map();
1861
- function getRunIdForSession(metadataDir) {
1862
- const existing = runIdByMetadataDir.get(metadataDir);
1863
- if (existing) return existing;
1864
- const runId = createRunId();
1865
- runIdByMetadataDir.set(metadataDir, runId);
1866
- return runId;
2076
+ const cjsRequire = createRequire(import.meta.url);
2077
+ function messages_getLoggingPrefix(type) {
2078
+ return messaging_prefix(type);
1867
2079
  }
1868
- function getSessionRunId(packageJsonDir, browser) {
1869
- return getRunIdForSession(getPlaywrightMetadataDir(packageJsonDir, browser));
2080
+ function messages_ready(mode, browser) {
2081
+ const noun = artifactNoun(browser);
2082
+ const state = pintor.green(`ready for ${mode}`);
2083
+ const watching = 'development' === mode ? ' Watching for file changes.' : '';
2084
+ return `${messages_getLoggingPrefix('success')} ${noun} ${state}.${watching}`;
1870
2085
  }
1871
- function deriveDistExtensionId(browser, distPath) {
2086
+ function readJsonRecord(filePath) {
1872
2087
  try {
1873
- if (!__rspack_external_node_fs_5ea92f0c.existsSync(__rspack_external_node_path_c5b9b54f.join(distPath, 'manifest.json'))) return;
1874
- const id = isGeckoBasedBrowser(browser) ? geckoExtensionId(distPath) : chromiumExtensionId(distPath);
1875
- return id || void 0;
2088
+ return JSON.parse(__rspack_external_node_fs_5ea92f0c.readFileSync(filePath, 'utf8'));
1876
2089
  } catch {
1877
- return;
2090
+ return null;
1878
2091
  }
1879
2092
  }
1880
- function readManifestProvenance(manifestPath) {
1881
- try {
1882
- const manifest = parseJsonSafe(__rspack_external_node_fs_5ea92f0c.readFileSync(manifestPath, 'utf-8'));
1883
- return {
1884
- extensionName: 'string' == typeof manifest?.name ? manifest.name : void 0,
1885
- extensionVersion: 'string' == typeof manifest?.version ? manifest.version : void 0
1886
- };
1887
- } catch {
1888
- return {};
1889
- }
2093
+ function capitalizeToken(value) {
2094
+ return value.split('-').filter(Boolean).map((token)=>token.charAt(0).toUpperCase() + token.slice(1)).join('-');
1890
2095
  }
1891
- function plugin_playwright_ensureDirSync(dirPath) {
1892
- try {
1893
- __rspack_external_node_fs_5ea92f0c.mkdirSync(dirPath, {
1894
- recursive: true
1895
- });
1896
- } catch {}
2096
+ function getExtensionVersion() {
2097
+ return process.env.EXTENSION_DEVELOP_VERSION || process.env.EXTENSION_CLI_VERSION || (()=>{
2098
+ try {
2099
+ return cjsRequire('../package.json').version;
2100
+ } catch {
2101
+ return 'unknown';
2102
+ }
2103
+ })();
1897
2104
  }
1898
- function writeJsonAtomic(filePath, value) {
1899
- try {
1900
- const tmpPath = `${filePath}.tmp-${process.pid}`;
1901
- __rspack_external_node_fs_5ea92f0c.writeFileSync(tmpPath, `${JSON.stringify(value, null, 2)}\n`, 'utf-8');
1902
- __rspack_external_node_fs_5ea92f0c.renameSync(tmpPath, filePath);
1903
- } catch {}
2105
+ function collapseHomeDirInCardValue(value) {
2106
+ const raw = String(value || '');
2107
+ const home = __rspack_external_node_os_74b4b876.homedir();
2108
+ if (!home || !raw.startsWith(home)) return raw;
2109
+ const rest = raw.slice(home.length);
2110
+ if ('' === rest) return '~';
2111
+ if (rest.startsWith(__rspack_external_node_path_c5b9b54f.sep) || rest.startsWith('/')) return `~${rest}`;
2112
+ return raw;
1904
2113
  }
1905
- function detectLiveDevSessionOwner(readyPath, isAlive = (pid)=>{
1906
- try {
1907
- process.kill(pid, 0);
1908
- return true;
1909
- } catch {
1910
- return false;
1911
- }
1912
- }) {
1913
- try {
1914
- if (!__rspack_external_node_fs_5ea92f0c.existsSync(readyPath)) return null;
1915
- const prev = JSON.parse(__rspack_external_node_fs_5ea92f0c.readFileSync(readyPath, 'utf-8'));
1916
- if (prev?.command !== 'dev') return null;
1917
- if ('number' != typeof prev.pid || prev.pid === process.pid) return null;
1918
- if ('ready' !== prev.status && 'starting' !== prev.status) return null;
1919
- if (!isAlive(prev.pid)) return null;
1920
- return {
1921
- pid: prev.pid,
1922
- runId: 'string' == typeof prev.runId ? prev.runId : '',
1923
- instanceId: 'string' == typeof prev.instanceId ? prev.instanceId : void 0,
1924
- instanceExplicit: true === prev.instanceExplicit
1925
- };
1926
- } catch {
1927
- return null;
1928
- }
2114
+ function browserRunnerDisabled(args) {
2115
+ const manifest = readJsonRecord(args.manifestPath);
2116
+ const ready = readJsonRecord(args.readyPath);
2117
+ const browserLabel = capitalizeToken(String(args.browser || 'unknown'));
2118
+ const runId = String(ready?.runId || '').trim();
2119
+ const pid = Number.isInteger(ready?.pid) ? String(ready?.pid) : '';
2120
+ const runLabel = runId ? `${runId}${pid ? ` · PID ${pid}` : ''}` : pid ? `PID ${pid}` : '';
2121
+ const extensionName = String(manifest?.name || 'Extension');
2122
+ const extensionVersion = String(manifest?.version || '').trim();
2123
+ const extensionLabel = extensionVersion ? `${extensionName} ${extensionVersion}` : extensionName;
2124
+ const extensionJsVersion = getExtensionVersion();
2125
+ const updateSuffix = process.env.EXTENSION_CLI_UPDATE_SUFFIX || '';
2126
+ if (updateSuffix) delete process.env.EXTENSION_CLI_UPDATE_SUFFIX;
2127
+ return card({
2128
+ version: extensionJsVersion,
2129
+ suffix: updateSuffix,
2130
+ rows: [
2131
+ {
2132
+ label: 'Browser',
2133
+ value: browserRowValue(String(args.browser || 'unknown'), `${browserLabel} (no-browser mode)`)
2134
+ },
2135
+ {
2136
+ label: 'Extension',
2137
+ value: extensionLabel
2138
+ },
2139
+ {
2140
+ label: 'Output',
2141
+ value: collapseHomeDirInCardValue(String(args.distPath || '').trim())
2142
+ },
2143
+ {
2144
+ label: 'Run ID',
2145
+ value: runLabel
2146
+ }
2147
+ ]
2148
+ });
1929
2149
  }
1930
- function shouldWarnDevOverDev(owner, my) {
1931
- if (owner.instanceExplicit && my.instanceExplicit && owner.instanceId && my.instanceId && owner.instanceId !== my.instanceId) return false;
1932
- return true;
2150
+ function shouldWarnPortConflict(requested, actual) {
2151
+ const requestedPort = Number(requested);
2152
+ return Number.isFinite(requestedPort) && 0 !== requestedPort && requestedPort !== actual;
1933
2153
  }
1934
- function getPlaywrightMetadataDir(packageJsonDir, browser) {
1935
- return asAbsolute(browserArtifactsDir(packageJsonDir, browser));
2154
+ function portInUse(requestedPort, newPort) {
2155
+ return `${messages_getLoggingPrefix('warn')} Port ${requestedPort} is in use.\nThe dev server listens on port ${newPort} instead.`;
1936
2156
  }
1937
- function createPlaywrightMetadataWriter(options) {
1938
- const metadataDir = getPlaywrightMetadataDir(options.packageJsonDir, options.browser);
1939
- const readyPath = asAbsolute(readyContractPath(options.packageJsonDir, options.browser));
1940
- const eventsPath = asAbsolute(session_paths_eventsPath(options.packageJsonDir, options.browser));
1941
- const toPort = (value)=>{
1942
- if ('number' == typeof value && Number.isFinite(value)) return value;
1943
- if ('string' == typeof value) {
1944
- const parsed = parseInt(value, 10);
1945
- return Number.isFinite(parsed) ? parsed : null;
1946
- }
1947
- return null;
1948
- };
1949
- const liveOwner = detectLiveDevSessionOwner(readyPath);
1950
- const foreignLiveDevSession = 'dev' !== options.command && liveOwner ? liveOwner : null;
1951
- if (foreignLiveDevSession) console.warn(`[extension] a live dev session (pid ${foreignLiveDevSession.pid}) owns ${readyPath}; this ${options.command} run will not rewrite the session's ready.json/events.ndjson. The output dir is shared, so the dev browser may pick up freshly ${options.command}-built files. Stop the dev session first for a clean ${options.command} receipt.`);
1952
- if ('dev' === options.command && liveOwner && shouldWarnDevOverDev(liveOwner, {
1953
- instanceId: options.instanceId,
1954
- instanceExplicit: options.instanceExplicit
1955
- })) humanWarn(anotherDevSessionActive(options.browser, liveOwner.pid, liveOwner.runId));
1956
- const toManagedRecords = (dirs)=>Array.isArray(dirs) && dirs.length > 0 ? managedExtensionRecords(options.browser, dirs) : void 0;
1957
- let managedExtensions = toManagedRecords(options.managedExtensionDirs);
1958
- const base = {
1959
- schemaVersion: 2,
1960
- schema: 1,
1961
- command: options.command,
1962
- browser: options.browser,
1963
- runId: getRunIdForSession(metadataDir),
1964
- startedAt: nowISO(),
1965
- distPath: options.distPath,
1966
- manifestPath: options.manifestPath,
1967
- port: toPort(options.port),
1968
- host: options.host,
1969
- instanceId: options.instanceId,
1970
- ...options.instanceExplicit ? {
1971
- instanceExplicit: true
1972
- } : {},
1973
- controlPort: toPort(options.controlPort),
1974
- controlPath: options.controlPath,
1975
- logsPath: options.logsPath,
1976
- toolchainVersion: package_namespaceObject.rE,
1977
- ...readManifestProvenance(options.manifestPath)
1978
- };
1979
- function writeReady(status, extra) {
1980
- if (foreignLiveDevSession) return;
1981
- plugin_playwright_ensureDirSync(metadataDir);
1982
- const payload = {
1983
- ...base,
1984
- status,
1985
- pid: process.pid,
1986
- ts: nowISO(),
1987
- compiledAt: extra?.compiledAt ?? null,
1988
- errors: Array.isArray(extra?.errors) ? extra.errors : []
1989
- };
1990
- if (extra?.code) payload.code = extra.code;
1991
- if (extra?.message) payload.message = extra.message;
1992
- if (managedExtensions) payload.managedExtensions = managedExtensions;
1993
- const derivedExtensionId = deriveDistExtensionId(options.browser, options.distPath);
1994
- if (derivedExtensionId) payload.extensionId = derivedExtensionId;
1995
- try {
1996
- if (__rspack_external_node_fs_5ea92f0c.existsSync(readyPath)) {
1997
- const prev = JSON.parse(__rspack_external_node_fs_5ea92f0c.readFileSync(readyPath, 'utf-8'));
1998
- if ('number' == typeof prev.cdpPort) payload.cdpPort = prev.cdpPort;
1999
- if ('number' == typeof prev.rdpPort) payload.rdpPort = prev.rdpPort;
2000
- if (!payload.managedExtensions && Array.isArray(prev.managedExtensions)) payload.managedExtensions = prev.managedExtensions;
2001
- if ('string' == typeof prev.profilePath) payload.profilePath = prev.profilePath;
2002
- if ('number' == typeof prev.browserPid) payload.browserPid = prev.browserPid;
2003
- if ('string' == typeof prev.extensionId && prev.extensionId) payload.extensionId = prev.extensionId;
2004
- if ('string' == typeof prev.browserExitedAt) {
2005
- payload.browserExitedAt = prev.browserExitedAt;
2006
- payload.browserExitCode = prev.browserExitCode ?? null;
2007
- }
2008
- if ('string' == typeof prev.executorAttachedAt) {
2009
- payload.executorAttachedAt = prev.executorAttachedAt;
2010
- payload.runtime = 'attached';
2011
- }
2012
- if ('starting' !== status && 'string' == typeof prev.extensionLoadRefusedAt) {
2013
- const target = payload;
2014
- target.extensionLoadRefusedAt = prev.extensionLoadRefusedAt;
2015
- if ('string' == typeof prev.extensionLoadRefusedReason) target.extensionLoadRefusedReason = prev.extensionLoadRefusedReason;
2016
- if ('ready' === status) {
2017
- payload.status = 'error';
2018
- payload.code = 'extension_load_refused';
2019
- payload.message = String(prev.message || 'the browser refused to load the extension');
2020
- }
2021
- }
2022
- }
2023
- } catch {}
2024
- writeJsonAtomic(readyPath, payload);
2025
- }
2026
- function appendEvent(event) {
2027
- if (foreignLiveDevSession) return;
2028
- plugin_playwright_ensureDirSync(metadataDir);
2029
- try {
2030
- __rspack_external_node_fs_5ea92f0c.appendFileSync(eventsPath, `${JSON.stringify({
2031
- ...event,
2032
- runId: event.runId ?? base.runId
2033
- })}\n`, 'utf-8');
2034
- } catch {}
2035
- }
2157
+ function extensionJsRunnerError(error) {
2158
+ return `${messages_getLoggingPrefix('error')} Extension.js couldn't start the runner.\n${pintor.red(String(error))}`;
2159
+ }
2160
+ function autoExitModeEnabled(ms) {
2161
+ return `${messages_getLoggingPrefix('info')} Auto-exit is enabled.\nThe process exits after ${ms} ms of idle time.`;
2162
+ }
2163
+ function autoExitTriggered(ms) {
2164
+ return `${messages_getLoggingPrefix('info')} Auto-exit triggered after ${ms} ms.\nThe process cleans up and exits now.`;
2165
+ }
2166
+ function autoExitForceKill(ms) {
2167
+ return `${messages_getLoggingPrefix('warn')} The process didn't exit within ${ms} ms, so it exits forcibly now.`;
2168
+ }
2169
+ function devServerStartTimeout(ms) {
2170
+ return [
2171
+ `${messages_getLoggingPrefix('warn')} The dev server didn't start within ${ms} ms.`,
2172
+ "The bundler may have hit an error before emitting the first build.",
2173
+ `If nothing else prints, set ${pintor.blue('EXTENSION_VERBOSE=1')} to see more logs.`
2174
+ ].join('\n');
2175
+ }
2176
+ function bundlerFatalError(error) {
2177
+ const text = error instanceof Error ? error.stack || error.message : String(error);
2178
+ return `${messages_getLoggingPrefix('error')} The build failed to start.\n${pintor.red(text)}`;
2179
+ }
2180
+ function bundlerRecompiling() {
2181
+ return `${messages_getLoggingPrefix('info')} Recompiling due to file changes…`;
2182
+ }
2183
+ function noEntrypointsDetected(port) {
2184
+ return [
2185
+ `${messages_getLoggingPrefix('warn')} The first compilation produced no entrypoints or assets.`,
2186
+ `The dev server is running on 127.0.0.1:${port}, but nothing is being built.`,
2187
+ "Possible causes:",
2188
+ "- Empty or missing entry configuration.",
2189
+ "- Extension plugins are disabled, so entries aren't derived from the manifest.",
2190
+ "- All sources are ignored or excluded.",
2191
+ `Set ${pintor.blue('EXTENSION_VERBOSE=1')} to see verbose logs, or review your extension config.`
2192
+ ].join('\n');
2193
+ }
2194
+ function spacerLine() {
2195
+ return ' ';
2196
+ }
2197
+ function getDarkModeDefaults(browser) {
2198
+ if (isChromiumBasedBrowser(String(browser))) return {
2199
+ browserFlags: [
2200
+ '--force-dark-mode',
2201
+ '--enable-features=WebUIDarkMode'
2202
+ ],
2203
+ preferences: {}
2204
+ };
2205
+ if (isGeckoBasedBrowser(String(browser))) return {
2206
+ browserFlags: [],
2207
+ preferences: {
2208
+ 'ui.systemUsesDarkTheme': 1,
2209
+ 'layout.css.prefers-color-scheme.content-override': 2,
2210
+ 'devtools.theme': 'dark'
2211
+ }
2212
+ };
2036
2213
  return {
2037
- metadataDir,
2038
- readyPath,
2039
- eventsPath,
2040
- setManagedExtensionDirs (dirs) {
2041
- managedExtensions = toManagedRecords(dirs);
2042
- },
2043
- writeStarting () {
2044
- if (foreignLiveDevSession) return;
2045
- plugin_playwright_ensureDirSync(metadataDir);
2046
- try {
2047
- __rspack_external_node_fs_5ea92f0c.writeFileSync(eventsPath, '', 'utf-8');
2048
- } catch {}
2049
- writeReady('starting');
2050
- },
2051
- writeReady (compiledAt) {
2052
- writeReady('ready', {
2053
- compiledAt: compiledAt || nowISO()
2054
- });
2055
- },
2056
- writeError (code, message, errors) {
2057
- writeReady('error', {
2058
- code,
2059
- message,
2060
- errors: Array.isArray(errors) ? errors : [],
2061
- compiledAt: null
2062
- });
2063
- },
2064
- writeShutdown () {
2065
- if (foreignLiveDevSession) return;
2066
- try {
2067
- if (!__rspack_external_node_fs_5ea92f0c.existsSync(readyPath)) return;
2068
- const prev = JSON.parse(__rspack_external_node_fs_5ea92f0c.readFileSync(readyPath, 'utf-8'));
2069
- prev.status = 'stopped';
2070
- prev.code = 'shutdown';
2071
- prev.message = 'the dev session ended (watch closed)';
2072
- prev.ts = nowISO();
2073
- writeJsonAtomic(readyPath, prev);
2074
- } catch {}
2075
- },
2076
- stampExecutorAttached () {
2077
- try {
2078
- if (!__rspack_external_node_fs_5ea92f0c.existsSync(readyPath)) return;
2079
- const prev = JSON.parse(__rspack_external_node_fs_5ea92f0c.readFileSync(readyPath, 'utf-8'));
2080
- if ('string' == typeof prev.executorAttachedAt) return;
2081
- prev.executorAttachedAt = nowISO();
2082
- prev.runtime = 'attached';
2083
- if ('string' == typeof prev.extensionLoadRefusedAt) {
2084
- delete prev.extensionLoadRefusedAt;
2085
- delete prev.extensionLoadRefusedReason;
2086
- if ('extension_load_refused' === prev.code) {
2087
- prev.status = 'ready';
2088
- delete prev.code;
2089
- delete prev.message;
2090
- }
2091
- }
2092
- prev.ts = nowISO();
2093
- writeJsonAtomic(readyPath, prev);
2094
- } catch {}
2095
- },
2096
- appendEvent
2214
+ browserFlags: [],
2215
+ preferences: {}
2097
2216
  };
2098
2217
  }
2099
- class PlaywrightPlugin {
2100
- static name = 'plugin-playwright';
2101
- writer;
2102
- command;
2103
- browser;
2104
- constructor(options){
2105
- this.browser = String(options.browser || 'chromium');
2106
- this.command = options.command || ('development' === options.mode ? 'dev' : 'start');
2107
- this.writer = createPlaywrightMetadataWriter({
2108
- packageJsonDir: options.packageJsonDir,
2109
- browser: this.browser,
2110
- command: this.command,
2111
- distPath: options.outputPath,
2112
- manifestPath: options.manifestPath,
2113
- port: options.port,
2114
- host: options.host,
2115
- instanceId: options.instanceId,
2116
- instanceExplicit: options.instanceExplicit,
2117
- controlPort: options.controlPort,
2118
- controlPath: options.controlPath,
2119
- logsPath: options.logsPath,
2120
- managedExtensionDirs: options.managedExtensionDirs
2121
- });
2218
+ function withDarkMode(config) {
2219
+ const defaults = getDarkModeDefaults(config.browser);
2220
+ const existingFlags = Array.isArray(config.browserFlags) ? [
2221
+ ...config.browserFlags
2222
+ ] : [];
2223
+ const nextFlags = [
2224
+ ...existingFlags
2225
+ ];
2226
+ for (const flag of defaults.browserFlags || [])if (!nextFlags.some((f)=>String(f).trim() === flag)) nextFlags.push(flag);
2227
+ const nextPreferences = {
2228
+ ...config.preferences || {},
2229
+ ...Object.fromEntries(Object.entries(defaults.preferences || {}).filter(([k])=>!(k in (config.preferences || {}))))
2230
+ };
2231
+ return {
2232
+ ...config,
2233
+ browserFlags: nextFlags,
2234
+ preferences: nextPreferences
2235
+ };
2236
+ }
2237
+ function hasNewTabOverride(extensionDir) {
2238
+ const manifestPath = __rspack_external_node_path_c5b9b54f.join(extensionDir, 'manifest.json');
2239
+ if (!__rspack_external_node_fs_5ea92f0c.existsSync(manifestPath)) return false;
2240
+ try {
2241
+ const raw = __rspack_external_node_fs_5ea92f0c.readFileSync(manifestPath, 'utf-8');
2242
+ const manifest = JSON.parse(stripBom(raw));
2243
+ const newtab = manifest?.chrome_url_overrides?.newtab;
2244
+ return 'string' == typeof newtab && newtab.trim().length > 0;
2245
+ } catch {
2246
+ return false;
2122
2247
  }
2123
- apply(compiler) {
2124
- this.writer.writeStarting();
2125
- compiler.hooks.compile.tap(PlaywrightPlugin.name, ()=>{
2126
- this.writer.appendEvent({
2127
- type: 'compile_start',
2128
- ts: nowISO(),
2129
- command: this.command,
2130
- browser: this.browser
2131
- });
2132
- });
2133
- compiler.hooks.done.tap(PlaywrightPlugin.name, (stats)=>{
2134
- const durationMs = Number((stats?.compilation?.endTime || 0) - (stats?.compilation?.startTime || 0));
2135
- const hasErrors = Boolean(stats?.hasErrors?.());
2136
- const errorsJson = stats?.toJson?.({
2137
- all: false,
2138
- errors: true
2139
- });
2140
- const errorsCount = Array.isArray(errorsJson?.errors) ? errorsJson.errors.length : 0;
2141
- if (hasErrors) {
2142
- const errorMessages = formatStatsErrors(errorsJson?.errors);
2143
- const contractErrors = errorMessages.length ? errorMessages : [
2144
- `errors: ${String(errorsCount || 1)}`
2145
- ];
2146
- this.writer.appendEvent({
2147
- type: 'compile_error',
2148
- ts: nowISO(),
2149
- command: this.command,
2150
- browser: this.browser,
2151
- durationMs: Number.isFinite(durationMs) ? durationMs : void 0,
2152
- errorCount: Number.isFinite(errorsCount) ? errorsCount : 1,
2153
- errors: contractErrors
2154
- });
2155
- this.writer.writeError('compile_error', 'Compilation failed', contractErrors);
2156
- return;
2157
- }
2158
- this.writer.appendEvent({
2159
- type: 'compile_success',
2160
- ts: nowISO(),
2161
- command: this.command,
2162
- browser: this.browser,
2163
- durationMs: Number.isFinite(durationMs) ? durationMs : void 0,
2164
- errorCount: 0
2165
- });
2166
- this.writer.writeReady(nowISO());
2167
- });
2168
- compiler.hooks.failed.tap(PlaywrightPlugin.name, (error)=>{
2169
- this.writer.appendEvent({
2170
- type: 'compile_error',
2171
- ts: nowISO(),
2172
- command: this.command,
2173
- browser: this.browser,
2174
- errorCount: 1
2175
- });
2176
- this.writer.writeError('compile_failed', error instanceof Error ? error.message : String(error));
2248
+ }
2249
+ function resolveBuiltInExtensionForBrowser(input) {
2250
+ const engine = devtoolsEngineFor(input.browser);
2251
+ const packageRelativeCandidates = [
2252
+ __rspack_external_node_path_c5b9b54f.join('dist', input.packageName, engine)
2253
+ ];
2254
+ const normalizedBaseDir = __rspack_external_node_path_c5b9b54f.normalize(input.baseDir);
2255
+ const parentName = __rspack_external_node_path_c5b9b54f.basename(__rspack_external_node_path_c5b9b54f.dirname(normalizedBaseDir));
2256
+ const baseName = __rspack_external_node_path_c5b9b54f.basename(normalizedBaseDir);
2257
+ if ('programs' === parentName && 'develop' === baseName) packageRelativeCandidates.push(__rspack_external_node_path_c5b9b54f.join('..', '..', 'extensions', input.packageName, 'dist', engine));
2258
+ for (const rel of packageRelativeCandidates){
2259
+ const candidate = __rspack_external_node_path_c5b9b54f.resolve(input.baseDir, rel);
2260
+ if (__rspack_external_node_fs_5ea92f0c.existsSync(candidate)) return candidate;
2261
+ }
2262
+ }
2263
+ const RESERVED_BUILT_IN_NAMES = new Set([
2264
+ 'extension-js-devtools',
2265
+ 'extension-js-theme'
2266
+ ]);
2267
+ function isReservedBuiltInPath(extensionPath) {
2268
+ const base = __rspack_external_node_path_c5b9b54f.basename(__rspack_external_node_path_c5b9b54f.normalize(extensionPath));
2269
+ if (RESERVED_BUILT_IN_NAMES.has(base)) return true;
2270
+ const segments = __rspack_external_node_path_c5b9b54f.normalize(extensionPath).split(__rspack_external_node_path_c5b9b54f.sep);
2271
+ return segments.some((segment)=>RESERVED_BUILT_IN_NAMES.has(segment));
2272
+ }
2273
+ function dedupeByResolvedPath(paths) {
2274
+ const seen = new Set();
2275
+ const result = [];
2276
+ for (const entry of paths){
2277
+ const resolved = __rspack_external_node_path_c5b9b54f.resolve(entry);
2278
+ if (!seen.has(resolved)) {
2279
+ seen.add(resolved);
2280
+ result.push(entry);
2281
+ }
2282
+ }
2283
+ return result;
2284
+ }
2285
+ function computeExtensionsToLoad(baseDir, mode, browser, userExtensionOutputPath, extraExtensionDirs = [], userManifestPath) {
2286
+ const list = [];
2287
+ try {
2288
+ const devtoolsForBrowser = resolveBuiltInExtensionForBrowser({
2289
+ baseDir,
2290
+ packageName: 'extension-js-devtools',
2291
+ browser
2177
2292
  });
2178
- compiler.hooks.watchClose.tap(PlaywrightPlugin.name, ()=>{
2179
- this.writer.appendEvent({
2180
- type: 'shutdown',
2181
- ts: nowISO(),
2182
- command: this.command,
2183
- browser: this.browser
2184
- });
2185
- if ('dev' === this.command) this.writer.writeShutdown();
2293
+ const themeForBrowser = resolveBuiltInExtensionForBrowser({
2294
+ baseDir,
2295
+ packageName: 'extension-js-theme',
2296
+ browser
2186
2297
  });
2298
+ const userHasNewTabOverride = hasNewTabOverride(userExtensionOutputPath) || ('string' == typeof userManifestPath ? hasNewTabOverride(__rspack_external_node_path_c5b9b54f.dirname(userManifestPath)) : false);
2299
+ const devtoolsHasNewTabOverride = devtoolsForBrowser ? hasNewTabOverride(devtoolsForBrowser) : false;
2300
+ const shouldSkipDevtoolsForNtpConflict = userHasNewTabOverride && devtoolsHasNewTabOverride;
2301
+ if (devtoolsForBrowser && !shouldSkipDevtoolsForNtpConflict) list.push(devtoolsForBrowser);
2302
+ if (themeForBrowser) list.push(themeForBrowser);
2303
+ } catch {}
2304
+ const resolvedUserOutputPath = __rspack_external_node_path_c5b9b54f.resolve(userExtensionOutputPath);
2305
+ for (const p of extraExtensionDirs)if (!isReservedBuiltInPath(p)) {
2306
+ if (__rspack_external_node_path_c5b9b54f.resolve(p) !== resolvedUserOutputPath) list.push(p);
2187
2307
  }
2308
+ list.push(userExtensionOutputPath);
2309
+ return dedupeByResolvedPath(list);
2188
2310
  }
2189
2311
  function resolveCompanionExtensionDirs(opts) {
2190
2312
  const { projectRoot, config } = opts;
@@ -2300,9 +2422,7 @@ async function extensionPreview(pathOrRemoteUrl, previewOptions, browserLauncher
2300
2422
  const mergedGeckoBinary = safePreviewOptions.geckoBinary || safePreviewOptions.firefoxBinary || safeCommandConfig.geckoBinary || safeCommandConfig.firefoxBinary || safeBrowserConfig.geckoBinary || safeBrowserConfig.firefoxBinary;
2301
2423
  const mergedChromiumBinary = safePreviewOptions.chromiumBinary || safeCommandConfig.chromiumBinary || safeBrowserConfig.chromiumBinary;
2302
2424
  const merged = {
2303
- ...safeBrowserConfig,
2304
- ...safeCommandConfig,
2305
- ...safePreviewOptions,
2425
+ ...mergeOptionLayers(SERVE_COMMAND_DEFAULTS, safeBrowserConfig, safeCommandConfig, safePreviewOptions),
2306
2426
  extensions: resolvedExtensionsConfig,
2307
2427
  chromiumBinary: mergedChromiumBinary,
2308
2428
  geckoBinary: mergedGeckoBinary
@@ -2335,10 +2455,17 @@ async function extensionPreview(pathOrRemoteUrl, previewOptions, browserLauncher
2335
2455
  geckoBinary: merged.geckoBinary,
2336
2456
  instanceId: merged.instanceId,
2337
2457
  port: merged.port,
2338
- dryRun: merged.dryRun
2458
+ dryRun: merged.dryRun,
2459
+ logLevel: merged.logLevel,
2460
+ logContexts: merged.logContexts,
2461
+ logFormat: merged.logFormat,
2462
+ logTimestamps: merged.logTimestamps,
2463
+ logColor: merged.logColor,
2464
+ logUrl: merged.logUrl,
2465
+ logTab: merged.logTab
2339
2466
  };
2340
2467
  if (!browserLauncher) throw new Error("extensionPreview requires a browserLauncher callback. The browser launch code has moved to programs/extension/browsers/.");
2341
2468
  await browserLauncher(resolvedOpts);
2342
2469
  metadata.writeReady();
2343
2470
  }
2344
- export { CHROMIUM_FAMILY_ALIASES, GECKO_FAMILY_ALIASES, PlaywrightPlugin, asAbsolute, assertNoManagedDependencyConflicts, authorInstallNotice, autoExitForceKill, autoExitModeEnabled, autoExitTriggered, browserLaunchFailed, browserRunnerDisabled, buildAssetsTree, buildCommandFailed, buildComplete, buildFailed, buildShareHint, buildWarningsDetails, bundlerFatalError, bundlerRecompiling, computeExtensionsToLoad, createPlaywrightMetadataWriter, debugBrowser, debugContextPath, debugDirs, debugExtensionsToLoad, debugOutputPath, devCommandFailed, devServerStartTimeout, downloadingText, extensionJsRunnerError, extensionLoadRecovered, extensionLoadStillRefused, extensionPreview, failedToDownloadOrExtractZIPFileError, getDirs, getDistPath, getProjectStructure, getSessionRunId, getSpecialFoldersDataForCompiler, getSpecialFoldersDataForProjectRoot, invalidRemoteZip, isChromiumBasedBrowser, isGeckoBasedBrowser, loadBrowserConfig, loadCommandConfig, loadCustomConfig, localZipNotFound, manifestInvalidJson, messages_ready, needsInstall, noCompanionExtensionsResolved, noEntrypointsDetected, normalizeBrowser, notAZipArchive, package_namespaceObject, packagingDistributionFiles, packagingSourceFiles, portInUse, projectInstallFallbackToNpm, projectInstallScriptsDisabled, resolveCompanionExtensionDirs, resolveCompanionExtensionsConfig, sanitize, shouldWarnPortConflict, spacerLine, toPosixPath, treeWithDistFilesBrowser, treeWithSourceAndDistFiles, treeWithSourceFiles, unpackagedSuccessfully, unpackagingExtension, writingTypeDefinitions, writingTypeDefinitionsError, zipArtifactReady };
2471
+ export { BUILD_COMMAND_DEFAULTS, CHROMIUM_FAMILY_ALIASES, DEV_COMMAND_DEFAULTS, GECKO_FAMILY_ALIASES, PlaywrightPlugin, START_BUILD_DEFAULTS, asAbsolute, assertNoManagedDependencyConflicts, authorInstallNotice, autoExitForceKill, autoExitModeEnabled, autoExitTriggered, browserLaunchFailed, browserRunnerDisabled, buildAssetsTree, buildCommandFailed, buildComplete, buildFailed, buildShareHint, buildWarningsDetails, bundlerFatalError, bundlerRecompiling, computeExtensionsToLoad, createPlaywrightMetadataWriter, debugBrowser, debugContextPath, debugDirs, debugExtensionsToLoad, debugOutputPath, devCommandFailed, devServerStartTimeout, downloadingText, extensionJsRunnerError, extensionLoadRecovered, extensionLoadStillRefused, extensionPreview, failedToDownloadOrExtractZIPFileError, getDirs, getDistPath, getProjectStructure, getSessionRunId, getSpecialFoldersDataForCompiler, getSpecialFoldersDataForProjectRoot, invalidRemoteZip, isChromiumBasedBrowser, isGeckoBasedBrowser, isWebkitBasedBrowser, loadBrowserConfig, loadCommandConfig, loadCustomConfig, localZipNotFound, manifestInvalidJson, mergeOptionLayers, messages_ready, needsInstall, noCompanionExtensionsResolved, noEntrypointsDetected, normalizeBrowser, notAZipArchive, package_namespaceObject, packagingDistributionFiles, packagingSourceFiles, portInUse, projectInstallFallbackToNpm, projectInstallScriptsDisabled, resolveCompanionExtensionDirs, resolveCompanionExtensionsConfig, sanitize, shouldWarnPortConflict, spacerLine, stampReadyDistExtensionId, stampReadyKnownExtensionId, toPosixPath, treeWithDistFilesBrowser, treeWithSourceAndDistFiles, treeWithSourceFiles, unpackagedSuccessfully, unpackagingExtension, writingTypeDefinitions, writingTypeDefinitionsError, zipArtifactReady };