@hominis/fireforge 0.49.0 → 0.49.1

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.
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "version": "0.49.0",
4
- "commit": "09dd0a82fdad367fb2390b4311f785a975995acd",
5
- "shortCommit": "09dd0a82fdad",
3
+ "version": "0.49.1",
4
+ "commit": "6348d3cae2e93096039d40ce0b52bfce0a0b9308",
5
+ "shortCommit": "6348d3cae2e9",
6
6
  "dirty": false,
7
7
  "dirtyHash": null,
8
- "buildTime": "2026-10-04T08:17:58.923Z"
8
+ "buildTime": "2026-10-04T15:20:41.231Z"
9
9
  }
@@ -30,6 +30,15 @@ function replacePrefix(value, oldRoot, newRoot) {
30
30
  ? newRoot + value.slice(oldRoot.length)
31
31
  : value;
32
32
  }
33
+ /** Rewrites references under oldRoot, including the forward-slash spelling mozbuild writes on Windows. */
34
+ function rewriteRootReferences(content, oldRoot, newRoot) {
35
+ const rewritten = content.replaceAll(oldRoot + sep, newRoot + sep);
36
+ if (sep !== '\\')
37
+ return rewritten;
38
+ return rewritten
39
+ .replaceAll(oldRoot + '/', newRoot + '/')
40
+ .replaceAll(oldRoot.replaceAll('\\', '/') + '/', newRoot.replaceAll('\\', '/') + '/');
41
+ }
33
42
  function isDependencyMetadata(path) {
34
43
  return (/\.(?:pp|d)$/.test(path) ||
35
44
  /[/\\]faster[/\\]/.test(path) ||
@@ -72,6 +81,13 @@ export async function relocateObjdirProducts(engineDir, objDir, oldRoot) {
72
81
  try {
73
82
  await rename(temp, path);
74
83
  }
84
+ catch (error) {
85
+ // Windows refuses to rename over a directory link; replace it in two steps.
86
+ if (process.platform !== 'win32' || getNodeErrorCode(error) !== 'EPERM')
87
+ throw error;
88
+ await rm(path);
89
+ await rename(temp, path);
90
+ }
75
91
  finally {
76
92
  await rm(temp, { force: true });
77
93
  }
@@ -79,7 +95,7 @@ export async function relocateObjdirProducts(engineDir, objDir, oldRoot) {
79
95
  }
80
96
  else if (isDependencyMetadata(path)) {
81
97
  const content = await readFile(path, 'utf8');
82
- const rewritten = content.replaceAll(oldRoot + sep, newRoot + sep);
98
+ const rewritten = rewriteRootReferences(content, oldRoot, newRoot);
83
99
  if (content !== rewritten)
84
100
  await writeText(path, rewritten);
85
101
  }
@@ -142,12 +142,14 @@ export function parseDiffGitHeader(line) {
142
142
  return { sourcePath: candidate, targetPath: candidate };
143
143
  }
144
144
  }
145
- // Rename/copy (differing paths): non-greedy split at the first ` b/`.
146
- const asymmetric = /^(.+?) b\/(.+)$/.exec(rest);
147
- if (asymmetric?.[1] !== undefined && asymmetric[2] !== undefined) {
148
- return { sourcePath: asymmetric[1], targetPath: asymmetric[2] };
149
- }
150
- return null;
145
+ // Rename/copy (differing paths): split at the first ` b/` with a path on
146
+ // each side. indexOf keeps this linear where a lazy regex backtracks.
147
+ if (/[\n\r\u2028\u2029]/.test(rest))
148
+ return null;
149
+ const split = rest.indexOf(' b/', 1);
150
+ if (split === -1 || split + 3 >= rest.length)
151
+ return null;
152
+ return { sourcePath: rest.slice(0, split), targetPath: rest.slice(split + 3) };
151
153
  }
152
154
  /**
153
155
  * Splits raw diff content into lines, tolerating CRLF-saved patch files.
@@ -209,6 +209,33 @@ export function findCategorySection(lines, category, tokensCssPath) {
209
209
  }
210
210
  return { categoryLine: section.categoryLine, sectionEnd: section.sectionEnd };
211
211
  }
212
+ /**
213
+ * Drops complete quoted strings from one CSS line in linear time. An
214
+ * unterminated quote is kept, and once one fails to close every later quote
215
+ * of that kind fails too, so no start position is ever rescanned.
216
+ */
217
+ function stripCssStrings(line) {
218
+ let out = '';
219
+ const unterminated = new Set();
220
+ for (let i = 0; i < line.length; i++) {
221
+ const quote = line.charAt(i);
222
+ if ((quote !== '"' && quote !== "'") || unterminated.has(quote)) {
223
+ out += quote;
224
+ continue;
225
+ }
226
+ let end = i + 1;
227
+ while (end < line.length && line.charAt(end) !== quote)
228
+ end += line.charAt(end) === '\\' ? 2 : 1;
229
+ if (end >= line.length) {
230
+ unterminated.add(quote);
231
+ out += quote;
232
+ }
233
+ else {
234
+ i = end;
235
+ }
236
+ }
237
+ return out;
238
+ }
212
239
  /** 0-based open/close line indices of the base `:root {` block. */
213
240
  export function findBaseRootBounds(lines) {
214
241
  const open = lines.findIndex((line) => /:root\s*\{/.test(line));
@@ -218,7 +245,7 @@ export function findBaseRootBounds(lines) {
218
245
  let depth = 0;
219
246
  for (let i = open; i < lines.length; i++) {
220
247
  // Ignore braces in CSS strings (including data URLs).
221
- const code = (masked[i] ?? '').replace(/(["'])(?:\\.|(?!\1).)*\1/g, '');
248
+ const code = stripCssStrings(masked[i] ?? '');
222
249
  for (const char of code) {
223
250
  if (char === '{')
224
251
  depth++;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hominis/fireforge",
3
- "version": "0.49.0",
3
+ "version": "0.49.1",
4
4
  "description": "FireForge — a build tool for customizing Firefox",
5
5
  "type": "module",
6
6
  "main": "./dist/src/index.js",
@@ -76,9 +76,9 @@
76
76
  "eslint-plugin-simple-import-sort": "^14.0.0",
77
77
  "fast-check": "^4.6.0",
78
78
  "husky": "^9.1.7",
79
- "knip": "6.37.0",
79
+ "knip": "6.39.0",
80
80
  "lint-staged": "^17.0.4",
81
- "prettier": "3.9.8",
81
+ "prettier": "3.9.9",
82
82
  "tsx": "^4.7.0",
83
83
  "typescript": "~6.0.0",
84
84
  "typescript-eslint": "^8.0.0",