@hominis/fireforge 0.9.0
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/CHANGELOG.md +7 -0
- package/LICENSE.md +294 -0
- package/README.md +435 -0
- package/dist/bin/fireforge.d.ts +10 -0
- package/dist/bin/fireforge.js +29 -0
- package/dist/src/cli.d.ts +33 -0
- package/dist/src/cli.js +180 -0
- package/dist/src/commands/bootstrap.d.ts +9 -0
- package/dist/src/commands/bootstrap.js +73 -0
- package/dist/src/commands/build.d.ts +11 -0
- package/dist/src/commands/build.js +102 -0
- package/dist/src/commands/config.d.ts +13 -0
- package/dist/src/commands/config.js +135 -0
- package/dist/src/commands/discard.d.ts +12 -0
- package/dist/src/commands/discard.js +84 -0
- package/dist/src/commands/doctor.d.ts +18 -0
- package/dist/src/commands/doctor.js +356 -0
- package/dist/src/commands/download.d.ts +11 -0
- package/dist/src/commands/download.js +127 -0
- package/dist/src/commands/export-all.d.ts +11 -0
- package/dist/src/commands/export-all.js +122 -0
- package/dist/src/commands/export-shared.d.ts +48 -0
- package/dist/src/commands/export-shared.js +208 -0
- package/dist/src/commands/export.d.ts +13 -0
- package/dist/src/commands/export.js +178 -0
- package/dist/src/commands/furnace/apply.d.ts +7 -0
- package/dist/src/commands/furnace/apply.js +80 -0
- package/dist/src/commands/furnace/create.d.ts +8 -0
- package/dist/src/commands/furnace/create.js +377 -0
- package/dist/src/commands/furnace/deploy.d.ts +8 -0
- package/dist/src/commands/furnace/deploy.js +338 -0
- package/dist/src/commands/furnace/diff.d.ts +7 -0
- package/dist/src/commands/furnace/diff.js +119 -0
- package/dist/src/commands/furnace/index.d.ts +16 -0
- package/dist/src/commands/furnace/index.js +121 -0
- package/dist/src/commands/furnace/list.d.ts +5 -0
- package/dist/src/commands/furnace/list.js +65 -0
- package/dist/src/commands/furnace/override.d.ts +8 -0
- package/dist/src/commands/furnace/override.js +188 -0
- package/dist/src/commands/furnace/preview.d.ts +7 -0
- package/dist/src/commands/furnace/preview.js +96 -0
- package/dist/src/commands/furnace/remove.d.ts +8 -0
- package/dist/src/commands/furnace/remove.js +159 -0
- package/dist/src/commands/furnace/scan.d.ts +5 -0
- package/dist/src/commands/furnace/scan.js +112 -0
- package/dist/src/commands/furnace/status.d.ts +7 -0
- package/dist/src/commands/furnace/status.js +137 -0
- package/dist/src/commands/furnace/validate.d.ts +6 -0
- package/dist/src/commands/furnace/validate.js +91 -0
- package/dist/src/commands/furnace/validation-output.d.ts +7 -0
- package/dist/src/commands/furnace/validation-output.js +22 -0
- package/dist/src/commands/import.d.ts +11 -0
- package/dist/src/commands/import.js +241 -0
- package/dist/src/commands/lint.d.ts +10 -0
- package/dist/src/commands/lint.js +118 -0
- package/dist/src/commands/package.d.ts +11 -0
- package/dist/src/commands/package.js +80 -0
- package/dist/src/commands/re-export.d.ts +12 -0
- package/dist/src/commands/re-export.js +242 -0
- package/dist/src/commands/rebase/abort.d.ts +7 -0
- package/dist/src/commands/rebase/abort.js +49 -0
- package/dist/src/commands/rebase/confirm.d.ts +18 -0
- package/dist/src/commands/rebase/confirm.js +33 -0
- package/dist/src/commands/rebase/continue.d.ts +7 -0
- package/dist/src/commands/rebase/continue.js +81 -0
- package/dist/src/commands/rebase/index.d.ts +22 -0
- package/dist/src/commands/rebase/index.js +127 -0
- package/dist/src/commands/rebase/patch-loop.d.ts +9 -0
- package/dist/src/commands/rebase/patch-loop.js +135 -0
- package/dist/src/commands/rebase/summary.d.ts +12 -0
- package/dist/src/commands/rebase/summary.js +43 -0
- package/dist/src/commands/rebase.d.ts +4 -0
- package/dist/src/commands/rebase.js +6 -0
- package/dist/src/commands/register.d.ts +13 -0
- package/dist/src/commands/register.js +67 -0
- package/dist/src/commands/reset.d.ts +11 -0
- package/dist/src/commands/reset.js +83 -0
- package/dist/src/commands/resolve.d.ts +9 -0
- package/dist/src/commands/resolve.js +124 -0
- package/dist/src/commands/run.d.ts +9 -0
- package/dist/src/commands/run.js +91 -0
- package/dist/src/commands/setup-support.d.ts +23 -0
- package/dist/src/commands/setup-support.js +310 -0
- package/dist/src/commands/setup.d.ts +11 -0
- package/dist/src/commands/setup.js +94 -0
- package/dist/src/commands/status.d.ts +11 -0
- package/dist/src/commands/status.js +268 -0
- package/dist/src/commands/test.d.ts +12 -0
- package/dist/src/commands/test.js +182 -0
- package/dist/src/commands/token-coverage.d.ts +5 -0
- package/dist/src/commands/token-coverage.js +57 -0
- package/dist/src/commands/token.d.ts +14 -0
- package/dist/src/commands/token.js +121 -0
- package/dist/src/commands/watch.d.ts +9 -0
- package/dist/src/commands/watch.js +112 -0
- package/dist/src/commands/wire.d.ts +13 -0
- package/dist/src/commands/wire.js +149 -0
- package/dist/src/core/ast-utils.d.ts +47 -0
- package/dist/src/core/ast-utils.js +57 -0
- package/dist/src/core/brand-validation.d.ts +7 -0
- package/dist/src/core/brand-validation.js +15 -0
- package/dist/src/core/branding.d.ts +49 -0
- package/dist/src/core/branding.js +229 -0
- package/dist/src/core/browser-wire.d.ts +40 -0
- package/dist/src/core/browser-wire.js +66 -0
- package/dist/src/core/build-prepare.d.ts +25 -0
- package/dist/src/core/build-prepare.js +93 -0
- package/dist/src/core/config-mutate.d.ts +15 -0
- package/dist/src/core/config-mutate.js +51 -0
- package/dist/src/core/config-paths.d.ts +28 -0
- package/dist/src/core/config-paths.js +65 -0
- package/dist/src/core/config-state.d.ts +28 -0
- package/dist/src/core/config-state.js +152 -0
- package/dist/src/core/config-validate.d.ts +11 -0
- package/dist/src/core/config-validate.js +141 -0
- package/dist/src/core/config.d.ts +39 -0
- package/dist/src/core/config.js +70 -0
- package/dist/src/core/file-lock.d.ts +11 -0
- package/dist/src/core/file-lock.js +80 -0
- package/dist/src/core/firefox-archive.d.ts +40 -0
- package/dist/src/core/firefox-archive.js +63 -0
- package/dist/src/core/firefox-cache.d.ts +23 -0
- package/dist/src/core/firefox-cache.js +134 -0
- package/dist/src/core/firefox-download.d.ts +21 -0
- package/dist/src/core/firefox-download.js +129 -0
- package/dist/src/core/firefox-extract.d.ts +21 -0
- package/dist/src/core/firefox-extract.js +53 -0
- package/dist/src/core/firefox.d.ts +34 -0
- package/dist/src/core/firefox.js +78 -0
- package/dist/src/core/furnace-apply-helpers.d.ts +21 -0
- package/dist/src/core/furnace-apply-helpers.js +244 -0
- package/dist/src/core/furnace-apply.d.ts +16 -0
- package/dist/src/core/furnace-apply.js +147 -0
- package/dist/src/core/furnace-config.d.ts +94 -0
- package/dist/src/core/furnace-config.js +372 -0
- package/dist/src/core/furnace-constants.d.ts +4 -0
- package/dist/src/core/furnace-constants.js +6 -0
- package/dist/src/core/furnace-registration-ast.d.ts +24 -0
- package/dist/src/core/furnace-registration-ast.js +218 -0
- package/dist/src/core/furnace-registration-remove.d.ts +14 -0
- package/dist/src/core/furnace-registration-remove.js +89 -0
- package/dist/src/core/furnace-registration-validate.d.ts +20 -0
- package/dist/src/core/furnace-registration-validate.js +40 -0
- package/dist/src/core/furnace-registration.d.ts +29 -0
- package/dist/src/core/furnace-registration.js +96 -0
- package/dist/src/core/furnace-rollback.d.ts +20 -0
- package/dist/src/core/furnace-rollback.js +66 -0
- package/dist/src/core/furnace-scanner.d.ts +40 -0
- package/dist/src/core/furnace-scanner.js +143 -0
- package/dist/src/core/furnace-stories.d.ts +37 -0
- package/dist/src/core/furnace-stories.js +185 -0
- package/dist/src/core/furnace-validate-accessibility.d.ts +6 -0
- package/dist/src/core/furnace-validate-accessibility.js +32 -0
- package/dist/src/core/furnace-validate-checks.d.ts +4 -0
- package/dist/src/core/furnace-validate-checks.js +7 -0
- package/dist/src/core/furnace-validate-compatibility.d.ts +6 -0
- package/dist/src/core/furnace-validate-compatibility.js +57 -0
- package/dist/src/core/furnace-validate-helpers.d.ts +28 -0
- package/dist/src/core/furnace-validate-helpers.js +129 -0
- package/dist/src/core/furnace-validate-registration.d.ts +37 -0
- package/dist/src/core/furnace-validate-registration.js +220 -0
- package/dist/src/core/furnace-validate-structure.d.ts +6 -0
- package/dist/src/core/furnace-validate-structure.js +66 -0
- package/dist/src/core/furnace-validate.d.ts +16 -0
- package/dist/src/core/furnace-validate.js +103 -0
- package/dist/src/core/git-base.d.ts +47 -0
- package/dist/src/core/git-base.js +50 -0
- package/dist/src/core/git-diff.d.ts +63 -0
- package/dist/src/core/git-diff.js +246 -0
- package/dist/src/core/git-file-ops.d.ts +65 -0
- package/dist/src/core/git-file-ops.js +141 -0
- package/dist/src/core/git-status.d.ts +65 -0
- package/dist/src/core/git-status.js +163 -0
- package/dist/src/core/git.d.ts +113 -0
- package/dist/src/core/git.js +363 -0
- package/dist/src/core/license-headers.d.ts +36 -0
- package/dist/src/core/license-headers.js +83 -0
- package/dist/src/core/mach-build-artifacts.d.ts +29 -0
- package/dist/src/core/mach-build-artifacts.js +117 -0
- package/dist/src/core/mach-mozconfig.d.ts +17 -0
- package/dist/src/core/mach-mozconfig.js +50 -0
- package/dist/src/core/mach-python.d.ts +16 -0
- package/dist/src/core/mach-python.js +126 -0
- package/dist/src/core/mach.d.ts +106 -0
- package/dist/src/core/mach.js +166 -0
- package/dist/src/core/manifest-helpers.d.ts +25 -0
- package/dist/src/core/manifest-helpers.js +96 -0
- package/dist/src/core/manifest-register.d.ts +30 -0
- package/dist/src/core/manifest-register.js +65 -0
- package/dist/src/core/manifest-rules.d.ts +39 -0
- package/dist/src/core/manifest-rules.js +151 -0
- package/dist/src/core/manifest-tokenizers.d.ts +34 -0
- package/dist/src/core/manifest-tokenizers.js +84 -0
- package/dist/src/core/parser-fallback.d.ts +36 -0
- package/dist/src/core/parser-fallback.js +43 -0
- package/dist/src/core/patch-apply-fuzz.d.ts +29 -0
- package/dist/src/core/patch-apply-fuzz.js +70 -0
- package/dist/src/core/patch-apply.d.ts +46 -0
- package/dist/src/core/patch-apply.js +235 -0
- package/dist/src/core/patch-export.d.ts +99 -0
- package/dist/src/core/patch-export.js +314 -0
- package/dist/src/core/patch-files.d.ts +11 -0
- package/dist/src/core/patch-files.js +51 -0
- package/dist/src/core/patch-lint.d.ts +72 -0
- package/dist/src/core/patch-lint.js +403 -0
- package/dist/src/core/patch-lock.d.ts +8 -0
- package/dist/src/core/patch-lock.js +29 -0
- package/dist/src/core/patch-manifest-consistency.d.ts +24 -0
- package/dist/src/core/patch-manifest-consistency.js +135 -0
- package/dist/src/core/patch-manifest-io.d.ts +36 -0
- package/dist/src/core/patch-manifest-io.js +77 -0
- package/dist/src/core/patch-manifest-query.d.ts +48 -0
- package/dist/src/core/patch-manifest-query.js +124 -0
- package/dist/src/core/patch-manifest-validate.d.ts +22 -0
- package/dist/src/core/patch-manifest-validate.js +72 -0
- package/dist/src/core/patch-manifest.d.ts +11 -0
- package/dist/src/core/patch-manifest.js +12 -0
- package/dist/src/core/patch-parse.d.ts +43 -0
- package/dist/src/core/patch-parse.js +143 -0
- package/dist/src/core/patch-transform.d.ts +21 -0
- package/dist/src/core/patch-transform.js +138 -0
- package/dist/src/core/rebase-session.d.ts +47 -0
- package/dist/src/core/rebase-session.js +65 -0
- package/dist/src/core/register-browser-content.d.ts +11 -0
- package/dist/src/core/register-browser-content.js +116 -0
- package/dist/src/core/register-module.d.ts +11 -0
- package/dist/src/core/register-module.js +76 -0
- package/dist/src/core/register-shared-css.d.ts +11 -0
- package/dist/src/core/register-shared-css.js +117 -0
- package/dist/src/core/register-test-manifest.d.ts +18 -0
- package/dist/src/core/register-test-manifest.js +99 -0
- package/dist/src/core/state-file.d.ts +4 -0
- package/dist/src/core/state-file.js +25 -0
- package/dist/src/core/token-coverage.d.ts +12 -0
- package/dist/src/core/token-coverage.js +74 -0
- package/dist/src/core/token-manager.d.ts +55 -0
- package/dist/src/core/token-manager.js +387 -0
- package/dist/src/core/wire-destroy.d.ts +21 -0
- package/dist/src/core/wire-destroy.js +103 -0
- package/dist/src/core/wire-dom-fragment.d.ts +23 -0
- package/dist/src/core/wire-dom-fragment.js +129 -0
- package/dist/src/core/wire-init.d.ts +23 -0
- package/dist/src/core/wire-init.js +201 -0
- package/dist/src/core/wire-subscript.d.ts +20 -0
- package/dist/src/core/wire-subscript.js +134 -0
- package/dist/src/core/wire-targets.d.ts +7 -0
- package/dist/src/core/wire-targets.js +9 -0
- package/dist/src/core/wire-utils.d.ts +88 -0
- package/dist/src/core/wire-utils.js +279 -0
- package/dist/src/errors/base.d.ts +60 -0
- package/dist/src/errors/base.js +87 -0
- package/dist/src/errors/build.d.ts +52 -0
- package/dist/src/errors/build.js +114 -0
- package/dist/src/errors/codes.d.ts +29 -0
- package/dist/src/errors/codes.js +30 -0
- package/dist/src/errors/config.d.ts +31 -0
- package/dist/src/errors/config.js +61 -0
- package/dist/src/errors/download.d.ts +42 -0
- package/dist/src/errors/download.js +95 -0
- package/dist/src/errors/furnace.d.ts +10 -0
- package/dist/src/errors/furnace.js +22 -0
- package/dist/src/errors/git.d.ts +41 -0
- package/dist/src/errors/git.js +99 -0
- package/dist/src/errors/patch.d.ts +10 -0
- package/dist/src/errors/patch.js +26 -0
- package/dist/src/errors/rebase.d.ts +20 -0
- package/dist/src/errors/rebase.js +30 -0
- package/dist/src/index.d.ts +21 -0
- package/dist/src/index.js +21 -0
- package/dist/src/types/cli.d.ts +14 -0
- package/dist/src/types/cli.js +2 -0
- package/dist/src/types/commands/index.d.ts +6 -0
- package/dist/src/types/commands/index.js +6 -0
- package/dist/src/types/commands/options.d.ts +239 -0
- package/dist/src/types/commands/options.js +6 -0
- package/dist/src/types/commands/patches.d.ts +89 -0
- package/dist/src/types/commands/patches.js +6 -0
- package/dist/src/types/commands/project.d.ts +71 -0
- package/dist/src/types/commands/project.js +6 -0
- package/dist/src/types/config.d.ts +101 -0
- package/dist/src/types/config.js +2 -0
- package/dist/src/types/furnace.d.ts +158 -0
- package/dist/src/types/furnace.js +2 -0
- package/dist/src/types/index.d.ts +6 -0
- package/dist/src/types/index.js +6 -0
- package/dist/src/utils/errors.d.ts +2 -0
- package/dist/src/utils/errors.js +15 -0
- package/dist/src/utils/fs.d.ts +72 -0
- package/dist/src/utils/fs.js +179 -0
- package/dist/src/utils/logger.d.ts +58 -0
- package/dist/src/utils/logger.js +120 -0
- package/dist/src/utils/options.d.ts +8 -0
- package/dist/src/utils/options.js +16 -0
- package/dist/src/utils/package-root.d.ts +10 -0
- package/dist/src/utils/package-root.js +53 -0
- package/dist/src/utils/parse.d.ts +110 -0
- package/dist/src/utils/parse.js +200 -0
- package/dist/src/utils/paths.d.ts +10 -0
- package/dist/src/utils/paths.js +43 -0
- package/dist/src/utils/platform.d.ts +38 -0
- package/dist/src/utils/platform.js +56 -0
- package/dist/src/utils/process.d.ts +80 -0
- package/dist/src/utils/process.js +188 -0
- package/dist/src/utils/regex.d.ts +24 -0
- package/dist/src/utils/regex.js +40 -0
- package/dist/src/utils/validation.d.ts +133 -0
- package/dist/src/utils/validation.js +250 -0
- package/package.json +106 -0
- package/templates/configs/common.mozconfig +24 -0
- package/templates/configs/darwin.mozconfig +10 -0
- package/templates/configs/linux.mozconfig +12 -0
- package/templates/configs/win32.mozconfig +14 -0
- package/templates/licenses/0BSD.md +14 -0
- package/templates/licenses/EUPL-1.2.md +294 -0
- package/templates/licenses/GPL-2.0-or-later.md +339 -0
- package/templates/licenses/MPL-2.0.md +383 -0
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
import { unlink } from 'node:fs/promises';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { toError } from '../utils/errors.js';
|
|
5
|
+
import { pathExists, readText, removeFile, writeText } from '../utils/fs.js';
|
|
6
|
+
import { warn } from '../utils/logger.js';
|
|
7
|
+
import { PATCH_CATEGORIES } from '../utils/validation.js';
|
|
8
|
+
import { discoverPatches, isNewFilePatch, withPatchDirectoryLock } from './patch-apply.js';
|
|
9
|
+
import { addPatchToManifest, loadPatchesManifest, PATCHES_MANIFEST, savePatchesManifest, } from './patch-manifest.js';
|
|
10
|
+
/**
|
|
11
|
+
* Gets the next patch number for a new patch.
|
|
12
|
+
* @param patchesDir - Path to the patches directory
|
|
13
|
+
* @returns Next patch number (e.g., "005" for 4 existing patches)
|
|
14
|
+
*/
|
|
15
|
+
export async function getNextPatchNumber(patchesDir) {
|
|
16
|
+
const patches = await discoverPatches(patchesDir);
|
|
17
|
+
if (patches.length === 0) {
|
|
18
|
+
return '001';
|
|
19
|
+
}
|
|
20
|
+
const finitePatches = patches.filter((p) => Number.isFinite(p.order));
|
|
21
|
+
if (finitePatches.length === 0)
|
|
22
|
+
return '001';
|
|
23
|
+
const maxOrder = finitePatches.reduce((max, p) => Math.max(max, p.order), 0);
|
|
24
|
+
const nextNumber = maxOrder + 1;
|
|
25
|
+
return String(nextNumber).padStart(Math.max(3, String(nextNumber).length), '0');
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Sanitizes a string for use in a filename.
|
|
29
|
+
*/
|
|
30
|
+
function sanitizeName(name) {
|
|
31
|
+
return name
|
|
32
|
+
.toLowerCase()
|
|
33
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
34
|
+
.replace(/^-+|-+$/g, '')
|
|
35
|
+
.slice(0, 50);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Generates the next patch filename with category.
|
|
39
|
+
* @param patchesDir - Path to the patches directory
|
|
40
|
+
* @param category - Patch category
|
|
41
|
+
* @param name - Human-readable name
|
|
42
|
+
* @returns Filename like "001-ui-sidebar.patch"
|
|
43
|
+
*/
|
|
44
|
+
export async function getNextPatchFilename(patchesDir, category, name) {
|
|
45
|
+
const patchNumber = await getNextPatchNumber(patchesDir);
|
|
46
|
+
const sanitizedName = sanitizeName(name);
|
|
47
|
+
return `${patchNumber}-${category}-${sanitizedName}.patch`;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Commits a freshly generated patch file and manifest update under an exclusive
|
|
51
|
+
* patch directory lock so concurrent exports cannot allocate the same number.
|
|
52
|
+
*/
|
|
53
|
+
export async function commitExportedPatch(input) {
|
|
54
|
+
return withPatchDirectoryLock(input.patchesDir, async () => {
|
|
55
|
+
const patchFilename = await getNextPatchFilename(input.patchesDir, input.category, input.name);
|
|
56
|
+
const patchPath = join(input.patchesDir, patchFilename);
|
|
57
|
+
const metadata = {
|
|
58
|
+
filename: patchFilename,
|
|
59
|
+
order: parseInt(patchFilename.split('-')[0] ?? '0', 10),
|
|
60
|
+
category: input.category,
|
|
61
|
+
name: input.name,
|
|
62
|
+
description: input.description,
|
|
63
|
+
createdAt: new Date().toISOString(),
|
|
64
|
+
sourceEsrVersion: input.sourceEsrVersion,
|
|
65
|
+
filesAffected: input.filesAffected,
|
|
66
|
+
};
|
|
67
|
+
const superseded = await findAllPatchesForFiles(input.patchesDir, input.filesAffected, patchFilename);
|
|
68
|
+
const supersededFilenames = superseded.map((patch) => patch.filename);
|
|
69
|
+
const originalManifest = await loadPatchesManifest(input.patchesDir);
|
|
70
|
+
const originalPatchContent = (await pathExists(patchPath)) ? await readText(patchPath) : null;
|
|
71
|
+
const removedPatchContents = new Map();
|
|
72
|
+
for (const oldPatch of superseded) {
|
|
73
|
+
if (await pathExists(oldPatch.path)) {
|
|
74
|
+
removedPatchContents.set(oldPatch.path, await readText(oldPatch.path));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
await writeText(patchPath, input.diff);
|
|
79
|
+
await addPatchToManifest(input.patchesDir, metadata, supersededFilenames);
|
|
80
|
+
for (const oldPatch of superseded) {
|
|
81
|
+
await removeFile(oldPatch.path);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
// Best-effort rollback: wrap each operation so a secondary failure
|
|
86
|
+
// never masks the original failure.
|
|
87
|
+
try {
|
|
88
|
+
if (originalPatchContent === null) {
|
|
89
|
+
await removeFile(patchPath);
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
await writeText(patchPath, originalPatchContent);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
warn(`Rollback warning: could not restore patch file: ${toError(error).message}`);
|
|
97
|
+
}
|
|
98
|
+
for (const [oldPatchPath, oldPatchContent] of removedPatchContents) {
|
|
99
|
+
try {
|
|
100
|
+
await writeText(oldPatchPath, oldPatchContent);
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
warn(`Rollback warning: could not restore ${oldPatchPath}: ${toError(error).message}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
try {
|
|
107
|
+
if (originalManifest) {
|
|
108
|
+
await savePatchesManifest(input.patchesDir, originalManifest);
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
await removeFile(join(input.patchesDir, PATCHES_MANIFEST));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
warn(`Rollback warning: could not restore manifest: ${toError(error).message}`);
|
|
116
|
+
}
|
|
117
|
+
throw error;
|
|
118
|
+
}
|
|
119
|
+
return {
|
|
120
|
+
patchFilename,
|
|
121
|
+
metadata,
|
|
122
|
+
superseded,
|
|
123
|
+
};
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Parses a patch filename to extract order, category, and name.
|
|
128
|
+
* Supports both new format (001-category-name.patch) and legacy (001-name.patch).
|
|
129
|
+
*/
|
|
130
|
+
export function parseFilename(filename) {
|
|
131
|
+
// New format: 001-ui-sidebar.patch
|
|
132
|
+
const newMatch = /^(\d+)-([a-z]+)-(.+)\.patch$/.exec(filename);
|
|
133
|
+
if (newMatch?.[1] && newMatch[2] && newMatch[3]) {
|
|
134
|
+
const orderStr = newMatch[1];
|
|
135
|
+
const category = newMatch[2];
|
|
136
|
+
const name = newMatch[3];
|
|
137
|
+
if (PATCH_CATEGORIES.includes(category)) {
|
|
138
|
+
return {
|
|
139
|
+
order: parseInt(orderStr, 10),
|
|
140
|
+
category: category,
|
|
141
|
+
name,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
// Legacy format: 001-name.patch
|
|
146
|
+
const legacyMatch = /^(\d+)-(.+)\.patch$/.exec(filename);
|
|
147
|
+
if (legacyMatch?.[1] && legacyMatch[2]) {
|
|
148
|
+
return {
|
|
149
|
+
order: parseInt(legacyMatch[1], 10),
|
|
150
|
+
category: null,
|
|
151
|
+
name: legacyMatch[2],
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
return { order: Infinity, category: null, name: filename };
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Finds an existing patch that contains the specified file.
|
|
158
|
+
* Returns the most recent (highest order) patch if multiple exist.
|
|
159
|
+
* @param patchesDir - Path to the patches directory
|
|
160
|
+
* @param filePath - File path to search for
|
|
161
|
+
* @returns The patch info and metadata, or null if not found
|
|
162
|
+
*/
|
|
163
|
+
export async function findExistingPatchForFile(patchesDir, filePath) {
|
|
164
|
+
const { findPatchesAffectingFile } = await import('./patch-manifest.js');
|
|
165
|
+
const affectingPatches = await findPatchesAffectingFile(patchesDir, filePath);
|
|
166
|
+
if (affectingPatches.length === 0) {
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
// Return the most recent (highest order) patch
|
|
170
|
+
return affectingPatches[affectingPatches.length - 1] ?? null;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Updates the content of a patch file.
|
|
174
|
+
* @param patchPath - Path to the patch file
|
|
175
|
+
* @param newContent - New patch content
|
|
176
|
+
*/
|
|
177
|
+
export async function updatePatch(patchPath, newContent) {
|
|
178
|
+
await writeText(patchPath, newContent);
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Updates metadata for a patch in the manifest.
|
|
182
|
+
* @param patchesDir - Path to the patches directory
|
|
183
|
+
* @param filename - Patch filename
|
|
184
|
+
* @param updates - Partial metadata updates
|
|
185
|
+
*/
|
|
186
|
+
export async function updatePatchMetadata(patchesDir, filename, updates) {
|
|
187
|
+
await withPatchDirectoryLock(patchesDir, async () => {
|
|
188
|
+
const manifest = await loadPatchesManifest(patchesDir);
|
|
189
|
+
if (!manifest)
|
|
190
|
+
return;
|
|
191
|
+
const patchIndex = manifest.patches.findIndex((p) => p.filename === filename);
|
|
192
|
+
if (patchIndex === -1)
|
|
193
|
+
return;
|
|
194
|
+
const existingPatch = manifest.patches[patchIndex];
|
|
195
|
+
if (existingPatch) {
|
|
196
|
+
manifest.patches[patchIndex] = { ...existingPatch, ...updates };
|
|
197
|
+
await savePatchesManifest(patchesDir, manifest);
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Finds patches that are completely superseded by newer patches.
|
|
203
|
+
* A patch is superseded if all its affected files are covered by newer patches.
|
|
204
|
+
* @param patchesDir - Path to the patches directory
|
|
205
|
+
* @param newPatchFiles - Files affected by the new patch
|
|
206
|
+
* @param excludeFilename - Filename to exclude from results (the new patch itself)
|
|
207
|
+
* @returns Superseded patches
|
|
208
|
+
*/
|
|
209
|
+
export async function findSupersededPatches(patchesDir, newPatchFiles, excludeFilename) {
|
|
210
|
+
const manifest = await loadPatchesManifest(patchesDir);
|
|
211
|
+
if (!manifest)
|
|
212
|
+
return [];
|
|
213
|
+
const patches = await discoverPatches(patchesDir);
|
|
214
|
+
const superseded = [];
|
|
215
|
+
for (const metadata of manifest.patches) {
|
|
216
|
+
// Skip the new patch itself
|
|
217
|
+
if (excludeFilename && metadata.filename === excludeFilename)
|
|
218
|
+
continue;
|
|
219
|
+
// Check if this is a "new file" patch (single file, created from scratch)
|
|
220
|
+
// A patch is superseded if it's a single-file new-file patch and
|
|
221
|
+
// the new patch covers the same file
|
|
222
|
+
if (metadata.filesAffected.length === 1) {
|
|
223
|
+
const affectedFile = metadata.filesAffected[0];
|
|
224
|
+
if (affectedFile && newPatchFiles.includes(affectedFile)) {
|
|
225
|
+
const patch = patches.find((p) => p.filename === metadata.filename);
|
|
226
|
+
if (patch && (await isNewFilePatch(patch.path))) {
|
|
227
|
+
superseded.push(patch);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return superseded;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Deletes a patch file and removes it from the manifest.
|
|
236
|
+
* @param patchesDir - Path to the patches directory
|
|
237
|
+
* @param filename - Patch filename to delete
|
|
238
|
+
*/
|
|
239
|
+
export async function deletePatch(patchesDir, filename) {
|
|
240
|
+
await withPatchDirectoryLock(patchesDir, async () => {
|
|
241
|
+
const patchPath = join(patchesDir, filename);
|
|
242
|
+
const manifest = await loadPatchesManifest(patchesDir);
|
|
243
|
+
const updatedManifest = manifest
|
|
244
|
+
? {
|
|
245
|
+
...manifest,
|
|
246
|
+
patches: manifest.patches.filter((patch) => patch.filename !== filename),
|
|
247
|
+
}
|
|
248
|
+
: null;
|
|
249
|
+
// Update manifest first so interrupted deletions leave an explicit repairable
|
|
250
|
+
// extra patch file rather than silently dropping metadata for an absent file.
|
|
251
|
+
if (updatedManifest) {
|
|
252
|
+
await savePatchesManifest(patchesDir, updatedManifest);
|
|
253
|
+
}
|
|
254
|
+
if (!(await pathExists(patchPath))) {
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
try {
|
|
258
|
+
await unlink(patchPath);
|
|
259
|
+
}
|
|
260
|
+
catch (error) {
|
|
261
|
+
if (manifest) {
|
|
262
|
+
try {
|
|
263
|
+
await savePatchesManifest(patchesDir, manifest);
|
|
264
|
+
}
|
|
265
|
+
catch (error) {
|
|
266
|
+
warn(`Failed to restore manifest after patch deletion error for "${filename}": ${toError(error).message}`);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
throw error;
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Checks whether a patch is fully covered by a new export.
|
|
275
|
+
* A patch is fully covered when every file it affects is present in the new export.
|
|
276
|
+
* @param patchFiles - Files affected by the existing patch
|
|
277
|
+
* @param targetFiles - Files affected by the new export
|
|
278
|
+
* @returns True when the existing patch is fully covered
|
|
279
|
+
*/
|
|
280
|
+
export function isPatchFullyCovered(patchFiles, targetFiles) {
|
|
281
|
+
if (patchFiles.length === 0) {
|
|
282
|
+
return false;
|
|
283
|
+
}
|
|
284
|
+
const targetFileSet = new Set(targetFiles);
|
|
285
|
+
return patchFiles.every((file) => targetFileSet.has(file));
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Finds patches whose filesAffected entries are fully covered by the specified files.
|
|
289
|
+
* Used for complete supersession when exporting full-file patches.
|
|
290
|
+
* @param patchesDir - Path to the patches directory
|
|
291
|
+
* @param targetFiles - Files affected by the new export
|
|
292
|
+
* @param excludeFilename - Filename to exclude from results (the new patch itself)
|
|
293
|
+
* @returns Patches that are fully covered by the new export
|
|
294
|
+
*/
|
|
295
|
+
export async function findAllPatchesForFiles(patchesDir, targetFiles, excludeFilename) {
|
|
296
|
+
const manifest = await loadPatchesManifest(patchesDir);
|
|
297
|
+
if (!manifest)
|
|
298
|
+
return [];
|
|
299
|
+
const patches = await discoverPatches(patchesDir);
|
|
300
|
+
const superseded = [];
|
|
301
|
+
for (const metadata of manifest.patches) {
|
|
302
|
+
// Skip the new patch itself
|
|
303
|
+
if (excludeFilename && metadata.filename === excludeFilename)
|
|
304
|
+
continue;
|
|
305
|
+
if (isPatchFullyCovered(metadata.filesAffected, targetFiles)) {
|
|
306
|
+
const patch = patches.find((p) => p.filename === metadata.filename);
|
|
307
|
+
if (patch) {
|
|
308
|
+
superseded.push(patch);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
return superseded;
|
|
313
|
+
}
|
|
314
|
+
//# sourceMappingURL=patch-export.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { PatchInfo } from '../types/commands/index.js';
|
|
2
|
+
/** Discovers patch files in a directory and returns them in apply order. */
|
|
3
|
+
export declare function discoverPatches(patchesDir: string): Promise<PatchInfo[]>;
|
|
4
|
+
/** Counts the patch files currently present in a patch directory. */
|
|
5
|
+
export declare function countPatches(patchesDir: string): Promise<number>;
|
|
6
|
+
/** Checks whether a patch creates a new file rather than modifying an existing one. */
|
|
7
|
+
export declare function isNewFilePatch(patchPath: string): Promise<boolean>;
|
|
8
|
+
/** Returns the first target file path referenced by a patch, if any. */
|
|
9
|
+
export declare function getTargetFileFromPatch(patchPath: string): Promise<string | null>;
|
|
10
|
+
/** Returns all target file paths referenced by a multi-file patch. */
|
|
11
|
+
export declare function getAllTargetFilesFromPatch(patchPath: string): Promise<string[]>;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
import { readdir } from 'node:fs/promises';
|
|
3
|
+
import { extname, join } from 'node:path';
|
|
4
|
+
import { pathExists, readText } from '../utils/fs.js';
|
|
5
|
+
import { extractOrder } from './patch-parse.js';
|
|
6
|
+
/** Discovers patch files in a directory and returns them in apply order. */
|
|
7
|
+
export async function discoverPatches(patchesDir) {
|
|
8
|
+
if (!(await pathExists(patchesDir))) {
|
|
9
|
+
return [];
|
|
10
|
+
}
|
|
11
|
+
const entries = await readdir(patchesDir, { withFileTypes: true });
|
|
12
|
+
const patches = entries
|
|
13
|
+
.filter((entry) => entry.isFile() && extname(entry.name) === '.patch')
|
|
14
|
+
.map((entry) => ({
|
|
15
|
+
path: join(patchesDir, entry.name),
|
|
16
|
+
filename: entry.name,
|
|
17
|
+
order: extractOrder(entry.name),
|
|
18
|
+
}));
|
|
19
|
+
patches.sort((a, b) => a.order - b.order || a.filename.localeCompare(b.filename));
|
|
20
|
+
return patches;
|
|
21
|
+
}
|
|
22
|
+
/** Counts the patch files currently present in a patch directory. */
|
|
23
|
+
export async function countPatches(patchesDir) {
|
|
24
|
+
const patches = await discoverPatches(patchesDir);
|
|
25
|
+
return patches.length;
|
|
26
|
+
}
|
|
27
|
+
/** Checks whether a patch creates a new file rather than modifying an existing one. */
|
|
28
|
+
export async function isNewFilePatch(patchPath) {
|
|
29
|
+
const content = await readText(patchPath);
|
|
30
|
+
return content.includes('new file mode') && content.includes('--- /dev/null');
|
|
31
|
+
}
|
|
32
|
+
/** Returns the first target file path referenced by a patch, if any. */
|
|
33
|
+
export async function getTargetFileFromPatch(patchPath) {
|
|
34
|
+
const content = await readText(patchPath);
|
|
35
|
+
const match = /^\+\+\+ b\/(.+)$/m.exec(content);
|
|
36
|
+
return match?.[1] ?? null;
|
|
37
|
+
}
|
|
38
|
+
/** Returns all target file paths referenced by a multi-file patch. */
|
|
39
|
+
export async function getAllTargetFilesFromPatch(patchPath) {
|
|
40
|
+
const content = await readText(patchPath);
|
|
41
|
+
const files = [];
|
|
42
|
+
const regex = /^\+\+\+ b\/(.+)$/gm;
|
|
43
|
+
let match;
|
|
44
|
+
while ((match = regex.exec(content)) !== null) {
|
|
45
|
+
if (match[1]) {
|
|
46
|
+
files.push(match[1]);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return files;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=patch-files.js.map
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { PatchLintIssue } from '../types/commands/index.js';
|
|
2
|
+
import type { FireForgeConfig } from '../types/config.js';
|
|
3
|
+
import { type CommentStyle } from './license-headers.js';
|
|
4
|
+
/**
|
|
5
|
+
* Detects comment style from file extension for license header checks.
|
|
6
|
+
*/
|
|
7
|
+
export declare function commentStyleForFile(file: string): CommentStyle | null;
|
|
8
|
+
/**
|
|
9
|
+
* Extracts new-file paths from a unified diff by scanning for `new file mode` markers.
|
|
10
|
+
*/
|
|
11
|
+
export declare function detectNewFilesInDiff(diffContent: string): Set<string>;
|
|
12
|
+
/**
|
|
13
|
+
* Lints patched CSS files for raw color values and non-tokenized custom properties.
|
|
14
|
+
*
|
|
15
|
+
* @param repoDir - Absolute path to the engine (repository) directory
|
|
16
|
+
* @param affectedFiles - File paths (relative to repoDir) affected by the patch
|
|
17
|
+
* @returns Array of lint issues found
|
|
18
|
+
*/
|
|
19
|
+
export declare function lintPatchedCss(repoDir: string, affectedFiles: string[]): Promise<PatchLintIssue[]>;
|
|
20
|
+
/**
|
|
21
|
+
* Checks new files for required license headers.
|
|
22
|
+
*
|
|
23
|
+
* @param repoDir - Absolute path to the engine directory
|
|
24
|
+
* @param newFiles - New file paths (relative to repoDir)
|
|
25
|
+
* @param config - Project configuration
|
|
26
|
+
* @returns Array of lint issues
|
|
27
|
+
*/
|
|
28
|
+
export declare function lintNewFileHeaders(repoDir: string, newFiles: string[], config: FireForgeConfig): Promise<PatchLintIssue[]>;
|
|
29
|
+
/**
|
|
30
|
+
* Lints patched JS/MJS files for import conventions, file size, JSDoc, and
|
|
31
|
+
* observer topic naming.
|
|
32
|
+
*
|
|
33
|
+
* @param repoDir - Absolute path to the engine directory
|
|
34
|
+
* @param affectedFiles - File paths (relative to repoDir)
|
|
35
|
+
* @param newFiles - Set of files that are newly created in this patch
|
|
36
|
+
* @param config - Project configuration
|
|
37
|
+
* @returns Array of lint issues
|
|
38
|
+
*/
|
|
39
|
+
export declare function lintPatchedJs(repoDir: string, affectedFiles: string[], newFiles: Set<string>, config: FireForgeConfig): Promise<PatchLintIssue[]>;
|
|
40
|
+
/**
|
|
41
|
+
* Checks that modifications to existing (non-new) JS/MJS files include at
|
|
42
|
+
* least one `// BINARYNAME:` comment in the added lines.
|
|
43
|
+
*
|
|
44
|
+
* @param diffContent - Raw unified diff string
|
|
45
|
+
* @param config - Project configuration
|
|
46
|
+
* @returns Array of lint issues
|
|
47
|
+
*/
|
|
48
|
+
export declare function lintModificationComments(diffContent: string, config: FireForgeConfig): PatchLintIssue[];
|
|
49
|
+
/**
|
|
50
|
+
* Checks patch size and emits advisory warnings.
|
|
51
|
+
*/
|
|
52
|
+
export declare function lintPatchSize(filesAffected: string[], lineCount: number): PatchLintIssue[];
|
|
53
|
+
/**
|
|
54
|
+
* Checks that modified (non-new) files with a supported extension still
|
|
55
|
+
* start with a recognized license header.
|
|
56
|
+
*
|
|
57
|
+
* @param repoDir - Engine root directory
|
|
58
|
+
* @param affectedFiles - All files affected by the patch
|
|
59
|
+
* @param newFiles - Set of newly created files (excluded from this check)
|
|
60
|
+
* @returns Warning-level lint issues for files missing any recognized header
|
|
61
|
+
*/
|
|
62
|
+
export declare function lintModifiedFileHeaders(repoDir: string, affectedFiles: string[], newFiles: Set<string>): Promise<PatchLintIssue[]>;
|
|
63
|
+
/**
|
|
64
|
+
* Runs all patch lint checks and returns combined issues.
|
|
65
|
+
*
|
|
66
|
+
* @param repoDir - Absolute path to the engine directory
|
|
67
|
+
* @param affectedFiles - File paths (relative to repoDir) affected by the patch
|
|
68
|
+
* @param diffContent - Raw unified diff string
|
|
69
|
+
* @param config - Project configuration
|
|
70
|
+
* @returns Array of all lint issues found
|
|
71
|
+
*/
|
|
72
|
+
export declare function lintExportedPatch(repoDir: string, affectedFiles: string[], diffContent: string, config: FireForgeConfig): Promise<PatchLintIssue[]>;
|