@torba/dev 1.0.10 → 1.0.11
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/index.cjs +167 -1
- package/dist/index.d.cts +105 -2
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +105 -2
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +163 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -2
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
2
|
let _torba_core = require("@torba/core");
|
|
3
|
+
let node_os = require("node:os");
|
|
4
|
+
let node_path = require("node:path");
|
|
5
|
+
let node_crypto = require("node:crypto");
|
|
6
|
+
let node_fs_promises = require("node:fs/promises");
|
|
3
7
|
|
|
4
8
|
//#region lib/plugin.ts
|
|
5
9
|
/** Identity helper for authoring a plugin with inferred types. */
|
|
@@ -75,7 +79,169 @@ async function buildManifest(config, ctx) {
|
|
|
75
79
|
}
|
|
76
80
|
|
|
77
81
|
//#endregion
|
|
82
|
+
//#region lib/overrides.ts
|
|
83
|
+
function matchesSelector(selector, artifact) {
|
|
84
|
+
if (typeof selector === "function") return selector(artifact);
|
|
85
|
+
return (Array.isArray(selector) ? selector : [selector]).map(_torba_core.globToRegex).some((re) => re.test(artifact.path));
|
|
86
|
+
}
|
|
87
|
+
/** Apply an ordered list of {@link ArtifactOverride}s to a list of artifacts. */
|
|
88
|
+
function applyOverrides(artifacts, overrides) {
|
|
89
|
+
if (overrides.length === 0) return [...artifacts];
|
|
90
|
+
const out = [];
|
|
91
|
+
for (const artifact of artifacts) {
|
|
92
|
+
let current = artifact;
|
|
93
|
+
for (const override of overrides) {
|
|
94
|
+
if (current === null) break;
|
|
95
|
+
if (!matchesSelector(override.match, current)) continue;
|
|
96
|
+
if (override.exclude) {
|
|
97
|
+
current = null;
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
if (override.rules !== void 0) {
|
|
101
|
+
const extra = (0, _torba_core.parseShortRuleset)(override.rules);
|
|
102
|
+
current = {
|
|
103
|
+
...current,
|
|
104
|
+
rules: [...current.rules, ...extra]
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
if (override.integrity === null) current = {
|
|
108
|
+
...current,
|
|
109
|
+
integrity: void 0,
|
|
110
|
+
discovery: void 0
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
if (current !== null) out.push(current);
|
|
114
|
+
}
|
|
115
|
+
return out;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
//#endregion
|
|
119
|
+
//#region lib/artifact-plugin.ts
|
|
120
|
+
/**
|
|
121
|
+
* Wraps a {@link TorbaPlugin} so that the artifacts it contributes are passed
|
|
122
|
+
* through an ordered list of {@link ArtifactOverride}s before being returned.
|
|
123
|
+
*
|
|
124
|
+
* Use this to give artifact-producing plugins (`forge`, `curseforge`, …) the
|
|
125
|
+
* same filter/patch support that `artifactScanner` already has natively. The
|
|
126
|
+
* inner plugin's `build` runs unchanged; only `Contribution.artifacts` is
|
|
127
|
+
* rewritten. Non-artifact contribution fields (`vars`, `launch`) pass through
|
|
128
|
+
* untouched.
|
|
129
|
+
*
|
|
130
|
+
* @param plugin The inner plugin to wrap.
|
|
131
|
+
* @param overrides Overrides applied to the inner plugin's artifacts. An empty
|
|
132
|
+
* list is a no-op (artifacts pass through unchanged).
|
|
133
|
+
*/
|
|
134
|
+
function defineArtifactPlugin(plugin, overrides) {
|
|
135
|
+
return {
|
|
136
|
+
name: plugin.name,
|
|
137
|
+
async build(ctx) {
|
|
138
|
+
const contribution = await plugin.build(ctx);
|
|
139
|
+
if (contribution.artifacts === void 0) return contribution;
|
|
140
|
+
return {
|
|
141
|
+
...contribution,
|
|
142
|
+
artifacts: applyOverrides(contribution.artifacts, overrides)
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
//#endregion
|
|
149
|
+
//#region lib/paths.ts
|
|
150
|
+
/**
|
|
151
|
+
* Per-user data directory for an application, matching OS conventions:
|
|
152
|
+
*
|
|
153
|
+
* - Windows: `%APPDATA%\<name>` (e.g. `C:\Users\you\AppData\Roaming\<name>`)
|
|
154
|
+
* - macOS: `~/Library/Application Support/<name>`
|
|
155
|
+
* - Linux: `$XDG_DATA_HOME/<name>` or `~/.local/share/<name>`
|
|
156
|
+
*/
|
|
157
|
+
function userDataDir(name) {
|
|
158
|
+
if (process.platform === "win32") return (0, node_path.join)(process.env.APPDATA ?? (0, node_os.homedir)(), name);
|
|
159
|
+
if (process.platform === "darwin") return (0, node_path.join)((0, node_os.homedir)(), "Library", "Application Support", name);
|
|
160
|
+
return (0, node_path.join)(process.env.XDG_DATA_HOME ?? (0, node_path.join)((0, node_os.homedir)(), ".local", "share"), name);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
//#endregion
|
|
164
|
+
//#region lib/scanner.ts
|
|
165
|
+
async function walkDir(dir) {
|
|
166
|
+
async function walk(cur) {
|
|
167
|
+
const entries = await (0, node_fs_promises.readdir)(cur, { withFileTypes: true });
|
|
168
|
+
return (await Promise.all(entries.map(async (e) => {
|
|
169
|
+
const abs = (0, node_path.join)(cur, e.name);
|
|
170
|
+
if (e.isDirectory()) return walk(abs);
|
|
171
|
+
if (e.isFile()) {
|
|
172
|
+
const { size } = await (0, node_fs_promises.stat)(abs);
|
|
173
|
+
const rel = (0, node_path.relative)(dir, abs).replace(/\\/g, "/");
|
|
174
|
+
const d = (0, node_path.dirname)(rel);
|
|
175
|
+
return [{
|
|
176
|
+
rel,
|
|
177
|
+
dir: d === "." ? "" : d,
|
|
178
|
+
filename: (0, node_path.basename)(rel),
|
|
179
|
+
abs,
|
|
180
|
+
size
|
|
181
|
+
}];
|
|
182
|
+
}
|
|
183
|
+
return [];
|
|
184
|
+
}))).flat();
|
|
185
|
+
}
|
|
186
|
+
return walk(dir);
|
|
187
|
+
}
|
|
188
|
+
async function hashFile(path, algo) {
|
|
189
|
+
return (0, node_crypto.createHash)(algo).update(await (0, node_fs_promises.readFile)(path)).digest("hex");
|
|
190
|
+
}
|
|
191
|
+
function applyTemplate(tpl, file) {
|
|
192
|
+
if (typeof tpl === "function") return tpl(file);
|
|
193
|
+
return (0, _torba_core.interpolate)(tpl, {
|
|
194
|
+
rel: file.rel,
|
|
195
|
+
dir: file.dir,
|
|
196
|
+
filename: file.filename
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
async function* scanDirectory(options, baseDir) {
|
|
200
|
+
const algo = options.hash ?? "sha1";
|
|
201
|
+
const sourceKind = options.source ?? "url";
|
|
202
|
+
const files = await walkDir(baseDir);
|
|
203
|
+
for (const file of files) {
|
|
204
|
+
const artifactPath = options.path ? applyTemplate(options.path, file) : file.rel;
|
|
205
|
+
let integrity;
|
|
206
|
+
let source;
|
|
207
|
+
if (sourceKind === "file") source = (0, _torba_core.sourceFile)(file.abs);
|
|
208
|
+
else {
|
|
209
|
+
source = (0, _torba_core.sourceUrl)(applyTemplate(options.url, file));
|
|
210
|
+
const digest = await hashFile(file.abs, algo);
|
|
211
|
+
integrity = algo === "sha1" ? { sha1: digest } : { sha256: digest };
|
|
212
|
+
}
|
|
213
|
+
yield {
|
|
214
|
+
path: artifactPath,
|
|
215
|
+
source,
|
|
216
|
+
size: file.size,
|
|
217
|
+
rules: [],
|
|
218
|
+
integrity
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
/** Scan a local directory tree into artifacts — a generic build-time plugin. */
|
|
223
|
+
function artifactScanner(options) {
|
|
224
|
+
return definePlugin({
|
|
225
|
+
name: "artifactScanner",
|
|
226
|
+
async build(ctx) {
|
|
227
|
+
const baseDir = (0, node_path.isAbsolute)(options.directory) ? options.directory : (0, node_path.resolve)(ctx.configDir, options.directory);
|
|
228
|
+
const scanned = [];
|
|
229
|
+
for await (const a of scanDirectory(options, baseDir)) scanned.push(a);
|
|
230
|
+
const artifacts = applyOverrides(scanned, options.overrides ?? []);
|
|
231
|
+
const dropped = scanned.length - artifacts.length;
|
|
232
|
+
ctx.log("artifactScanner", `scanned ${scanned.length} file(s)` + (dropped > 0 ? `, ${dropped} excluded` : ""));
|
|
233
|
+
return { artifacts };
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
//#endregion
|
|
239
|
+
exports.applyOverrides = applyOverrides;
|
|
240
|
+
exports.artifactScanner = artifactScanner;
|
|
78
241
|
exports.buildManifest = buildManifest;
|
|
242
|
+
exports.defineArtifactPlugin = defineArtifactPlugin;
|
|
79
243
|
exports.defineConfig = defineConfig;
|
|
80
244
|
exports.definePlugin = definePlugin;
|
|
81
|
-
exports.
|
|
245
|
+
exports.matchesSelector = matchesSelector;
|
|
246
|
+
exports.resolveConfig = resolveConfig;
|
|
247
|
+
exports.userDataDir = userDataDir;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Artifact, Manifest, Val, ValDefs, Valset } from "@torba/core";
|
|
1
|
+
import { Artifact, Manifest, Val, ValDefs, Valset, parseShortRuleset } from "@torba/core";
|
|
2
2
|
|
|
3
3
|
//#region lib/plugin.d.ts
|
|
4
4
|
/** Build-time context handed to every plugin's `build` hook. */
|
|
@@ -87,5 +87,108 @@ declare function resolveConfig(input: TorbaConfigInput, ctx: TorbaConfigContext)
|
|
|
87
87
|
*/
|
|
88
88
|
declare function buildManifest(config: TorbaConfig, ctx: BuildContext): Promise<Manifest>;
|
|
89
89
|
//#endregion
|
|
90
|
-
|
|
90
|
+
//#region lib/overrides.d.ts
|
|
91
|
+
/**
|
|
92
|
+
* Targets a subset of artifacts:
|
|
93
|
+
*
|
|
94
|
+
* - `string` / `string[]` — glob(s) matched against `artifact.path`
|
|
95
|
+
* (multiple = OR).
|
|
96
|
+
* - predicate — `(a) => boolean`, the escape hatch for matching on source
|
|
97
|
+
* kind, size, metadata, …
|
|
98
|
+
*/
|
|
99
|
+
type Selector = string | string[] | ((artifact: Artifact) => boolean);
|
|
100
|
+
declare function matchesSelector(selector: Selector, artifact: Artifact): boolean;
|
|
101
|
+
/** A ruleset in any form `parseShortRuleset` accepts (shorthand or full). */
|
|
102
|
+
type RulesetInput = Parameters<typeof parseShortRuleset>[0];
|
|
103
|
+
/**
|
|
104
|
+
* A per-selector patch over the artifacts a plugin produces. Each entry
|
|
105
|
+
* names the files it `match`es and the changes to apply — drop them,
|
|
106
|
+
* attach a ruleset, or clear integrity. Overrides run in list order; a
|
|
107
|
+
* later entry sees the effect of an earlier one.
|
|
108
|
+
*/
|
|
109
|
+
interface ArtifactOverride {
|
|
110
|
+
/** Files this override applies to. */
|
|
111
|
+
match: Selector;
|
|
112
|
+
/** Drop matched artifacts entirely. */
|
|
113
|
+
exclude?: boolean;
|
|
114
|
+
/**
|
|
115
|
+
* Ruleset to attach to matched artifacts — shorthand (`'allow.os.osx'`)
|
|
116
|
+
* or a full `Ruleset`. Appended to each artifact's existing `rules`.
|
|
117
|
+
*/
|
|
118
|
+
rules?: RulesetInput;
|
|
119
|
+
/** `null` clears `integrity` + `discovery` (skip verification). */
|
|
120
|
+
integrity?: null;
|
|
121
|
+
}
|
|
122
|
+
/** Apply an ordered list of {@link ArtifactOverride}s to a list of artifacts. */
|
|
123
|
+
declare function applyOverrides(artifacts: readonly Artifact[], overrides: readonly ArtifactOverride[]): Artifact[];
|
|
124
|
+
//#endregion
|
|
125
|
+
//#region lib/artifact-plugin.d.ts
|
|
126
|
+
/**
|
|
127
|
+
* Wraps a {@link TorbaPlugin} so that the artifacts it contributes are passed
|
|
128
|
+
* through an ordered list of {@link ArtifactOverride}s before being returned.
|
|
129
|
+
*
|
|
130
|
+
* Use this to give artifact-producing plugins (`forge`, `curseforge`, …) the
|
|
131
|
+
* same filter/patch support that `artifactScanner` already has natively. The
|
|
132
|
+
* inner plugin's `build` runs unchanged; only `Contribution.artifacts` is
|
|
133
|
+
* rewritten. Non-artifact contribution fields (`vars`, `launch`) pass through
|
|
134
|
+
* untouched.
|
|
135
|
+
*
|
|
136
|
+
* @param plugin The inner plugin to wrap.
|
|
137
|
+
* @param overrides Overrides applied to the inner plugin's artifacts. An empty
|
|
138
|
+
* list is a no-op (artifacts pass through unchanged).
|
|
139
|
+
*/
|
|
140
|
+
declare function defineArtifactPlugin(plugin: TorbaPlugin, overrides: readonly ArtifactOverride[]): TorbaPlugin;
|
|
141
|
+
//#endregion
|
|
142
|
+
//#region lib/paths.d.ts
|
|
143
|
+
/**
|
|
144
|
+
* Per-user data directory for an application, matching OS conventions:
|
|
145
|
+
*
|
|
146
|
+
* - Windows: `%APPDATA%\<name>` (e.g. `C:\Users\you\AppData\Roaming\<name>`)
|
|
147
|
+
* - macOS: `~/Library/Application Support/<name>`
|
|
148
|
+
* - Linux: `$XDG_DATA_HOME/<name>` or `~/.local/share/<name>`
|
|
149
|
+
*/
|
|
150
|
+
declare function userDataDir(name: string): string;
|
|
151
|
+
//#endregion
|
|
152
|
+
//#region lib/scanner.d.ts
|
|
153
|
+
/** A file discovered by {@link artifactScanner}, passed to `path`/`url` functions. */
|
|
154
|
+
interface ScannedFile {
|
|
155
|
+
/** Path relative to `directory`, POSIX separators. */
|
|
156
|
+
readonly rel: string;
|
|
157
|
+
/** Directory portion of `rel` (`''` at the root). */
|
|
158
|
+
readonly dir: string;
|
|
159
|
+
/** Final path segment. */
|
|
160
|
+
readonly filename: string;
|
|
161
|
+
/** Absolute path on the build machine. */
|
|
162
|
+
readonly abs: string;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* A `path` / `url` value: either a template string — interpolating the
|
|
166
|
+
* per-file placeholders `${rel}` / `${dir}` / `${filename}`, with any other
|
|
167
|
+
* `${var}` left for install — or a `(file) => string` function. Only the
|
|
168
|
+
* function form receives the build-machine `abs` path.
|
|
169
|
+
*/
|
|
170
|
+
type ScanTemplate = string | ((file: ScannedFile) => string);
|
|
171
|
+
interface ArtifactScannerOptions {
|
|
172
|
+
/** Directory to scan. */
|
|
173
|
+
directory: string;
|
|
174
|
+
/** URL for fetching each file — template string or `(file) => string`. */
|
|
175
|
+
url: ScanTemplate;
|
|
176
|
+
/** Destination path — template or function. Defaults to the file's `rel`. */
|
|
177
|
+
path?: ScanTemplate;
|
|
178
|
+
hash?: 'sha1' | 'sha256';
|
|
179
|
+
/**
|
|
180
|
+
* 'url' → emit sourceUrl + computed hash (default)
|
|
181
|
+
* 'file' → emit sourceFile pointing at the local copy; skip hashing entirely
|
|
182
|
+
*/
|
|
183
|
+
source?: 'url' | 'file';
|
|
184
|
+
/**
|
|
185
|
+
* Per-selector patches applied to the scanned artifacts — exclude files,
|
|
186
|
+
* attach rulesets (OS / feature gates), or clear integrity.
|
|
187
|
+
*/
|
|
188
|
+
overrides?: ArtifactOverride[];
|
|
189
|
+
}
|
|
190
|
+
/** Scan a local directory tree into artifacts — a generic build-time plugin. */
|
|
191
|
+
declare function artifactScanner(options: ArtifactScannerOptions): TorbaPlugin;
|
|
192
|
+
//#endregion
|
|
193
|
+
export { ArgItem, ArtifactOverride, ArtifactScannerOptions, BuildContext, Contribution, LaunchGroups, PluginMap, RulesetInput, ScanTemplate, ScannedFile, Selector, TorbaConfig, TorbaConfigContext, TorbaConfigInput, TorbaManifestConfig, TorbaPlugin, applyOverrides, artifactScanner, buildManifest, defineArtifactPlugin, defineConfig, definePlugin, matchesSelector, resolveConfig, userDataDir };
|
|
91
194
|
//# sourceMappingURL=index.d.cts.map
|
package/dist/index.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.cts","names":[],"sources":["../lib/plugin.ts","../lib/config.ts","../lib/engine.ts"],"mappings":";;;;UAGiB,YAAA;EAAA;EAEf,GAAA,GAAM,KAAA,UAAe,OAAA;;EAErB,SAAA;EAFA;EAIA,IAAA;AAAA;;;;;KAOU,YAAA,GAAe,MAAA,SAAe,MAAA,GAAS,GAAA;;UAGlC,YAAA;EAHyB;EAKxC,SAAA,GAAY,QAAA;EALa;EAOzB,IAAA,GAAO,OAAA;EAPwB;EAS/B,MAAA,GAAS,YAAA;AAAA;;;;AANX;;UAciB,WAAA;EACf,IAAA;EACA,KAAA,CAAM,GAAA,EAAK,YAAA,GAAe,OAAA,CAAQ,YAAA,IAAgB,YAAA;AAAA;;iBAIpC,YAAA,CAAa,MAAA,EAAQ,WAAA,GAAc,WAAA;;;;KCnCvC,SAAA,GAAY,MAAA,SAAe,YAAA;;KAG3B,OAAA,GAAU,MAAA,GAAS,GAAA;AAAA,UAEd,mBAAA;EDJf;ECMA,SAAA,GAAY,QAAA;EDNS;ECQrB,IAAA,GAAO,OAAA;EDJP;ECMA,OAAA,GAAU,OAAA,EAAS,SAAA;EDNf;ECQJ,IAAA,GAAO,OAAA,EAAS,SAAA,KAAc,OAAA;EDDR;ECGtB,OAAA,cAAqB,OAAA,EAAS,SAAA;EDHU;ECKxC,IAAA,GAAO,OAAA,KAAY,OAAA,EAAS,SAAA,KAAc,OAAA;EDLjB;ECOzB,QAAA;AAAA;AAAA,UAGe,WAAA;EDVyB;ECYxC,MAAA;EDZoD;ECcpD,OAAA,EAAS,WAAA;EDXM;ECaf,QAAA,EAAU,mBAAA;;;;;EAKV,SAAA,IAAa,QAAA,EAAU,QAAA,KAAa,OAAA,CAAQ,QAAA;AAAA;AAAA,UAG7B,kBAAA;EDnBH;ECqBZ,IAAA;AAAA;AAAA,KAGU,gBAAA,GACR,WAAA,KACE,GAAA,EAAK,kBAAA,KAAuB,WAAA,GAAc,OAAA,CAAQ,WAAA;;iBAGxC,YAAA,CAAa,KAAA,EAAO,gBAAA,GAAmB,gBAAA;;iBAKjC,aAAA,CACpB,KAAA,EAAO,gBAAA,EACP,GAAA,EAAK,kBAAA,GACJ,OAAA,CAAQ,WAAA;;;ADvDX;;;;AAAA,iBEwBsB,aAAA,CACpB,MAAA,EAAQ,WAAA,EACR,GAAA,EAAK,YAAA,GACJ,OAAA,CAAQ,QAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../lib/plugin.ts","../lib/config.ts","../lib/engine.ts","../lib/overrides.ts","../lib/artifact-plugin.ts","../lib/paths.ts","../lib/scanner.ts"],"mappings":";;;;UAGiB,YAAA;EAAA;EAEf,GAAA,GAAM,KAAA,UAAe,OAAA;;EAErB,SAAA;EAFA;EAIA,IAAA;AAAA;;;;;KAOU,YAAA,GAAe,MAAA,SAAe,MAAA,GAAS,GAAA;;UAGlC,YAAA;EAHyB;EAKxC,SAAA,GAAY,QAAA;EALa;EAOzB,IAAA,GAAO,OAAA;EAPwB;EAS/B,MAAA,GAAS,YAAA;AAAA;;;;AANX;;UAciB,WAAA;EACf,IAAA;EACA,KAAA,CAAM,GAAA,EAAK,YAAA,GAAe,OAAA,CAAQ,YAAA,IAAgB,YAAA;AAAA;;iBAIpC,YAAA,CAAa,MAAA,EAAQ,WAAA,GAAc,WAAA;;;;KCnCvC,SAAA,GAAY,MAAA,SAAe,YAAA;;KAG3B,OAAA,GAAU,MAAA,GAAS,GAAA;AAAA,UAEd,mBAAA;EDJf;ECMA,SAAA,GAAY,QAAA;EDNS;ECQrB,IAAA,GAAO,OAAA;EDJP;ECMA,OAAA,GAAU,OAAA,EAAS,SAAA;EDNf;ECQJ,IAAA,GAAO,OAAA,EAAS,SAAA,KAAc,OAAA;EDDR;ECGtB,OAAA,cAAqB,OAAA,EAAS,SAAA;EDHU;ECKxC,IAAA,GAAO,OAAA,KAAY,OAAA,EAAS,SAAA,KAAc,OAAA;EDLjB;ECOzB,QAAA;AAAA;AAAA,UAGe,WAAA;EDVyB;ECYxC,MAAA;EDZoD;ECcpD,OAAA,EAAS,WAAA;EDXM;ECaf,QAAA,EAAU,mBAAA;;;;;EAKV,SAAA,IAAa,QAAA,EAAU,QAAA,KAAa,OAAA,CAAQ,QAAA;AAAA;AAAA,UAG7B,kBAAA;EDnBH;ECqBZ,IAAA;AAAA;AAAA,KAGU,gBAAA,GACR,WAAA,KACE,GAAA,EAAK,kBAAA,KAAuB,WAAA,GAAc,OAAA,CAAQ,WAAA;;iBAGxC,YAAA,CAAa,KAAA,EAAO,gBAAA,GAAmB,gBAAA;;iBAKjC,aAAA,CACpB,KAAA,EAAO,gBAAA,EACP,GAAA,EAAK,kBAAA,GACJ,OAAA,CAAQ,WAAA;;;ADvDX;;;;AAAA,iBEwBsB,aAAA,CACpB,MAAA,EAAQ,WAAA,EACR,GAAA,EAAK,YAAA,GACJ,OAAA,CAAQ,QAAA;;;;;AF3BX;;;;;;KGYY,QAAA,yBAAiC,QAAA,EAAU,QAAA;AAAA,iBAEvC,eAAA,CACd,QAAA,EAAU,QAAA,EACV,QAAA,EAAU,QAAA;;KAUA,YAAA,GAAe,UAAA,QAAkB,iBAAA;;AHb7C;;;;;UGqBiB,gBAAA;EHrBgB;EGuB/B,KAAA,EAAO,QAAA;EHvBkB;EGyBzB,OAAA;EHzBiD;;;AAGnD;EG2BE,KAAA,GAAQ,YAAA;;EAER,SAAA;AAAA;;iBAIc,cAAA,CACd,SAAA,WAAoB,QAAA,IACpB,SAAA,WAAoB,gBAAA,KACnB,QAAA;;;;AHpDH;;;;;;;;;;;AAaA;;iBICgB,oBAAA,CACd,MAAA,EAAQ,WAAA,EACR,SAAA,WAAoB,gBAAA,KACnB,WAAA;;;;;;AJjBH;;;;iBKOgB,WAAA,CAAY,IAAA;;;;UCMX,WAAA;ENbY;EAAA,SMelB,GAAA;ENfkB;EAAA,SMiBlB,GAAA;ENfH;EAAA,SMiBG,QAAA;ENfT;EAAA,SMiBS,GAAA;AAAA;;ANRX;;;;;KMiBY,YAAA,cAA0B,IAAA,EAAM,WAAA;AAAA,UAE3B,sBAAA;ENnBgB;EMqB/B,SAAA;ENrBwC;EMuBxC,GAAA,EAAK,YAAA;ENvB+C;EMyBpD,IAAA,GAAO,YAAA;EACP,IAAA;ENvB2B;;;;EM4B3B,MAAA;ENtBqB;;;;EM2BrB,SAAA,GAAY,gBAAA;AAAA;;iBAwFE,eAAA,CAAgB,OAAA,EAAS,sBAAA,GAAyB,WAAA"}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Artifact, Manifest, Val, ValDefs, Valset } from "@torba/core";
|
|
1
|
+
import { Artifact, Manifest, Val, ValDefs, Valset, parseShortRuleset } from "@torba/core";
|
|
2
2
|
|
|
3
3
|
//#region lib/plugin.d.ts
|
|
4
4
|
/** Build-time context handed to every plugin's `build` hook. */
|
|
@@ -87,5 +87,108 @@ declare function resolveConfig(input: TorbaConfigInput, ctx: TorbaConfigContext)
|
|
|
87
87
|
*/
|
|
88
88
|
declare function buildManifest(config: TorbaConfig, ctx: BuildContext): Promise<Manifest>;
|
|
89
89
|
//#endregion
|
|
90
|
-
|
|
90
|
+
//#region lib/overrides.d.ts
|
|
91
|
+
/**
|
|
92
|
+
* Targets a subset of artifacts:
|
|
93
|
+
*
|
|
94
|
+
* - `string` / `string[]` — glob(s) matched against `artifact.path`
|
|
95
|
+
* (multiple = OR).
|
|
96
|
+
* - predicate — `(a) => boolean`, the escape hatch for matching on source
|
|
97
|
+
* kind, size, metadata, …
|
|
98
|
+
*/
|
|
99
|
+
type Selector = string | string[] | ((artifact: Artifact) => boolean);
|
|
100
|
+
declare function matchesSelector(selector: Selector, artifact: Artifact): boolean;
|
|
101
|
+
/** A ruleset in any form `parseShortRuleset` accepts (shorthand or full). */
|
|
102
|
+
type RulesetInput = Parameters<typeof parseShortRuleset>[0];
|
|
103
|
+
/**
|
|
104
|
+
* A per-selector patch over the artifacts a plugin produces. Each entry
|
|
105
|
+
* names the files it `match`es and the changes to apply — drop them,
|
|
106
|
+
* attach a ruleset, or clear integrity. Overrides run in list order; a
|
|
107
|
+
* later entry sees the effect of an earlier one.
|
|
108
|
+
*/
|
|
109
|
+
interface ArtifactOverride {
|
|
110
|
+
/** Files this override applies to. */
|
|
111
|
+
match: Selector;
|
|
112
|
+
/** Drop matched artifacts entirely. */
|
|
113
|
+
exclude?: boolean;
|
|
114
|
+
/**
|
|
115
|
+
* Ruleset to attach to matched artifacts — shorthand (`'allow.os.osx'`)
|
|
116
|
+
* or a full `Ruleset`. Appended to each artifact's existing `rules`.
|
|
117
|
+
*/
|
|
118
|
+
rules?: RulesetInput;
|
|
119
|
+
/** `null` clears `integrity` + `discovery` (skip verification). */
|
|
120
|
+
integrity?: null;
|
|
121
|
+
}
|
|
122
|
+
/** Apply an ordered list of {@link ArtifactOverride}s to a list of artifacts. */
|
|
123
|
+
declare function applyOverrides(artifacts: readonly Artifact[], overrides: readonly ArtifactOverride[]): Artifact[];
|
|
124
|
+
//#endregion
|
|
125
|
+
//#region lib/artifact-plugin.d.ts
|
|
126
|
+
/**
|
|
127
|
+
* Wraps a {@link TorbaPlugin} so that the artifacts it contributes are passed
|
|
128
|
+
* through an ordered list of {@link ArtifactOverride}s before being returned.
|
|
129
|
+
*
|
|
130
|
+
* Use this to give artifact-producing plugins (`forge`, `curseforge`, …) the
|
|
131
|
+
* same filter/patch support that `artifactScanner` already has natively. The
|
|
132
|
+
* inner plugin's `build` runs unchanged; only `Contribution.artifacts` is
|
|
133
|
+
* rewritten. Non-artifact contribution fields (`vars`, `launch`) pass through
|
|
134
|
+
* untouched.
|
|
135
|
+
*
|
|
136
|
+
* @param plugin The inner plugin to wrap.
|
|
137
|
+
* @param overrides Overrides applied to the inner plugin's artifacts. An empty
|
|
138
|
+
* list is a no-op (artifacts pass through unchanged).
|
|
139
|
+
*/
|
|
140
|
+
declare function defineArtifactPlugin(plugin: TorbaPlugin, overrides: readonly ArtifactOverride[]): TorbaPlugin;
|
|
141
|
+
//#endregion
|
|
142
|
+
//#region lib/paths.d.ts
|
|
143
|
+
/**
|
|
144
|
+
* Per-user data directory for an application, matching OS conventions:
|
|
145
|
+
*
|
|
146
|
+
* - Windows: `%APPDATA%\<name>` (e.g. `C:\Users\you\AppData\Roaming\<name>`)
|
|
147
|
+
* - macOS: `~/Library/Application Support/<name>`
|
|
148
|
+
* - Linux: `$XDG_DATA_HOME/<name>` or `~/.local/share/<name>`
|
|
149
|
+
*/
|
|
150
|
+
declare function userDataDir(name: string): string;
|
|
151
|
+
//#endregion
|
|
152
|
+
//#region lib/scanner.d.ts
|
|
153
|
+
/** A file discovered by {@link artifactScanner}, passed to `path`/`url` functions. */
|
|
154
|
+
interface ScannedFile {
|
|
155
|
+
/** Path relative to `directory`, POSIX separators. */
|
|
156
|
+
readonly rel: string;
|
|
157
|
+
/** Directory portion of `rel` (`''` at the root). */
|
|
158
|
+
readonly dir: string;
|
|
159
|
+
/** Final path segment. */
|
|
160
|
+
readonly filename: string;
|
|
161
|
+
/** Absolute path on the build machine. */
|
|
162
|
+
readonly abs: string;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* A `path` / `url` value: either a template string — interpolating the
|
|
166
|
+
* per-file placeholders `${rel}` / `${dir}` / `${filename}`, with any other
|
|
167
|
+
* `${var}` left for install — or a `(file) => string` function. Only the
|
|
168
|
+
* function form receives the build-machine `abs` path.
|
|
169
|
+
*/
|
|
170
|
+
type ScanTemplate = string | ((file: ScannedFile) => string);
|
|
171
|
+
interface ArtifactScannerOptions {
|
|
172
|
+
/** Directory to scan. */
|
|
173
|
+
directory: string;
|
|
174
|
+
/** URL for fetching each file — template string or `(file) => string`. */
|
|
175
|
+
url: ScanTemplate;
|
|
176
|
+
/** Destination path — template or function. Defaults to the file's `rel`. */
|
|
177
|
+
path?: ScanTemplate;
|
|
178
|
+
hash?: 'sha1' | 'sha256';
|
|
179
|
+
/**
|
|
180
|
+
* 'url' → emit sourceUrl + computed hash (default)
|
|
181
|
+
* 'file' → emit sourceFile pointing at the local copy; skip hashing entirely
|
|
182
|
+
*/
|
|
183
|
+
source?: 'url' | 'file';
|
|
184
|
+
/**
|
|
185
|
+
* Per-selector patches applied to the scanned artifacts — exclude files,
|
|
186
|
+
* attach rulesets (OS / feature gates), or clear integrity.
|
|
187
|
+
*/
|
|
188
|
+
overrides?: ArtifactOverride[];
|
|
189
|
+
}
|
|
190
|
+
/** Scan a local directory tree into artifacts — a generic build-time plugin. */
|
|
191
|
+
declare function artifactScanner(options: ArtifactScannerOptions): TorbaPlugin;
|
|
192
|
+
//#endregion
|
|
193
|
+
export { ArgItem, ArtifactOverride, ArtifactScannerOptions, BuildContext, Contribution, LaunchGroups, PluginMap, RulesetInput, ScanTemplate, ScannedFile, Selector, TorbaConfig, TorbaConfigContext, TorbaConfigInput, TorbaManifestConfig, TorbaPlugin, applyOverrides, artifactScanner, buildManifest, defineArtifactPlugin, defineConfig, definePlugin, matchesSelector, resolveConfig, userDataDir };
|
|
91
194
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../lib/plugin.ts","../lib/config.ts","../lib/engine.ts"],"mappings":";;;;UAGiB,YAAA;EAAA;EAEf,GAAA,GAAM,KAAA,UAAe,OAAA;;EAErB,SAAA;EAFA;EAIA,IAAA;AAAA;;;;;KAOU,YAAA,GAAe,MAAA,SAAe,MAAA,GAAS,GAAA;;UAGlC,YAAA;EAHyB;EAKxC,SAAA,GAAY,QAAA;EALa;EAOzB,IAAA,GAAO,OAAA;EAPwB;EAS/B,MAAA,GAAS,YAAA;AAAA;;;;AANX;;UAciB,WAAA;EACf,IAAA;EACA,KAAA,CAAM,GAAA,EAAK,YAAA,GAAe,OAAA,CAAQ,YAAA,IAAgB,YAAA;AAAA;;iBAIpC,YAAA,CAAa,MAAA,EAAQ,WAAA,GAAc,WAAA;;;;KCnCvC,SAAA,GAAY,MAAA,SAAe,YAAA;;KAG3B,OAAA,GAAU,MAAA,GAAS,GAAA;AAAA,UAEd,mBAAA;EDJf;ECMA,SAAA,GAAY,QAAA;EDNS;ECQrB,IAAA,GAAO,OAAA;EDJP;ECMA,OAAA,GAAU,OAAA,EAAS,SAAA;EDNf;ECQJ,IAAA,GAAO,OAAA,EAAS,SAAA,KAAc,OAAA;EDDR;ECGtB,OAAA,cAAqB,OAAA,EAAS,SAAA;EDHU;ECKxC,IAAA,GAAO,OAAA,KAAY,OAAA,EAAS,SAAA,KAAc,OAAA;EDLjB;ECOzB,QAAA;AAAA;AAAA,UAGe,WAAA;EDVyB;ECYxC,MAAA;EDZoD;ECcpD,OAAA,EAAS,WAAA;EDXM;ECaf,QAAA,EAAU,mBAAA;;;;;EAKV,SAAA,IAAa,QAAA,EAAU,QAAA,KAAa,OAAA,CAAQ,QAAA;AAAA;AAAA,UAG7B,kBAAA;EDnBH;ECqBZ,IAAA;AAAA;AAAA,KAGU,gBAAA,GACR,WAAA,KACE,GAAA,EAAK,kBAAA,KAAuB,WAAA,GAAc,OAAA,CAAQ,WAAA;;iBAGxC,YAAA,CAAa,KAAA,EAAO,gBAAA,GAAmB,gBAAA;;iBAKjC,aAAA,CACpB,KAAA,EAAO,gBAAA,EACP,GAAA,EAAK,kBAAA,GACJ,OAAA,CAAQ,WAAA;;;ADvDX;;;;AAAA,iBEwBsB,aAAA,CACpB,MAAA,EAAQ,WAAA,EACR,GAAA,EAAK,YAAA,GACJ,OAAA,CAAQ,QAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../lib/plugin.ts","../lib/config.ts","../lib/engine.ts","../lib/overrides.ts","../lib/artifact-plugin.ts","../lib/paths.ts","../lib/scanner.ts"],"mappings":";;;;UAGiB,YAAA;EAAA;EAEf,GAAA,GAAM,KAAA,UAAe,OAAA;;EAErB,SAAA;EAFA;EAIA,IAAA;AAAA;;;;;KAOU,YAAA,GAAe,MAAA,SAAe,MAAA,GAAS,GAAA;;UAGlC,YAAA;EAHyB;EAKxC,SAAA,GAAY,QAAA;EALa;EAOzB,IAAA,GAAO,OAAA;EAPwB;EAS/B,MAAA,GAAS,YAAA;AAAA;;;;AANX;;UAciB,WAAA;EACf,IAAA;EACA,KAAA,CAAM,GAAA,EAAK,YAAA,GAAe,OAAA,CAAQ,YAAA,IAAgB,YAAA;AAAA;;iBAIpC,YAAA,CAAa,MAAA,EAAQ,WAAA,GAAc,WAAA;;;;KCnCvC,SAAA,GAAY,MAAA,SAAe,YAAA;;KAG3B,OAAA,GAAU,MAAA,GAAS,GAAA;AAAA,UAEd,mBAAA;EDJf;ECMA,SAAA,GAAY,QAAA;EDNS;ECQrB,IAAA,GAAO,OAAA;EDJP;ECMA,OAAA,GAAU,OAAA,EAAS,SAAA;EDNf;ECQJ,IAAA,GAAO,OAAA,EAAS,SAAA,KAAc,OAAA;EDDR;ECGtB,OAAA,cAAqB,OAAA,EAAS,SAAA;EDHU;ECKxC,IAAA,GAAO,OAAA,KAAY,OAAA,EAAS,SAAA,KAAc,OAAA;EDLjB;ECOzB,QAAA;AAAA;AAAA,UAGe,WAAA;EDVyB;ECYxC,MAAA;EDZoD;ECcpD,OAAA,EAAS,WAAA;EDXM;ECaf,QAAA,EAAU,mBAAA;;;;;EAKV,SAAA,IAAa,QAAA,EAAU,QAAA,KAAa,OAAA,CAAQ,QAAA;AAAA;AAAA,UAG7B,kBAAA;EDnBH;ECqBZ,IAAA;AAAA;AAAA,KAGU,gBAAA,GACR,WAAA,KACE,GAAA,EAAK,kBAAA,KAAuB,WAAA,GAAc,OAAA,CAAQ,WAAA;;iBAGxC,YAAA,CAAa,KAAA,EAAO,gBAAA,GAAmB,gBAAA;;iBAKjC,aAAA,CACpB,KAAA,EAAO,gBAAA,EACP,GAAA,EAAK,kBAAA,GACJ,OAAA,CAAQ,WAAA;;;ADvDX;;;;AAAA,iBEwBsB,aAAA,CACpB,MAAA,EAAQ,WAAA,EACR,GAAA,EAAK,YAAA,GACJ,OAAA,CAAQ,QAAA;;;;;AF3BX;;;;;;KGYY,QAAA,yBAAiC,QAAA,EAAU,QAAA;AAAA,iBAEvC,eAAA,CACd,QAAA,EAAU,QAAA,EACV,QAAA,EAAU,QAAA;;KAUA,YAAA,GAAe,UAAA,QAAkB,iBAAA;;AHb7C;;;;;UGqBiB,gBAAA;EHrBgB;EGuB/B,KAAA,EAAO,QAAA;EHvBkB;EGyBzB,OAAA;EHzBiD;;;AAGnD;EG2BE,KAAA,GAAQ,YAAA;;EAER,SAAA;AAAA;;iBAIc,cAAA,CACd,SAAA,WAAoB,QAAA,IACpB,SAAA,WAAoB,gBAAA,KACnB,QAAA;;;;AHpDH;;;;;;;;;;;AAaA;;iBICgB,oBAAA,CACd,MAAA,EAAQ,WAAA,EACR,SAAA,WAAoB,gBAAA,KACnB,WAAA;;;;;;AJjBH;;;;iBKOgB,WAAA,CAAY,IAAA;;;;UCMX,WAAA;ENbY;EAAA,SMelB,GAAA;ENfkB;EAAA,SMiBlB,GAAA;ENfH;EAAA,SMiBG,QAAA;ENfT;EAAA,SMiBS,GAAA;AAAA;;ANRX;;;;;KMiBY,YAAA,cAA0B,IAAA,EAAM,WAAA;AAAA,UAE3B,sBAAA;ENnBgB;EMqB/B,SAAA;ENrBwC;EMuBxC,GAAA,EAAK,YAAA;ENvB+C;EMyBpD,IAAA,GAAO,YAAA;EACP,IAAA;ENvB2B;;;;EM4B3B,MAAA;ENtBqB;;;;EM2BrB,SAAA,GAAY,gBAAA;AAAA;;iBAwFE,eAAA,CAAgB,OAAA,EAAS,sBAAA,GAAyB,WAAA"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import { deduplicateArtifacts } from "@torba/core";
|
|
1
|
+
import { deduplicateArtifacts, globToRegex, interpolate, parseShortRuleset, sourceFile, sourceUrl } from "@torba/core";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
4
|
+
import { createHash } from "node:crypto";
|
|
5
|
+
import { readFile, readdir, stat } from "node:fs/promises";
|
|
2
6
|
|
|
3
7
|
//#region lib/plugin.ts
|
|
4
8
|
/** Identity helper for authoring a plugin with inferred types. */
|
|
@@ -74,5 +78,162 @@ async function buildManifest(config, ctx) {
|
|
|
74
78
|
}
|
|
75
79
|
|
|
76
80
|
//#endregion
|
|
77
|
-
|
|
81
|
+
//#region lib/overrides.ts
|
|
82
|
+
function matchesSelector(selector, artifact) {
|
|
83
|
+
if (typeof selector === "function") return selector(artifact);
|
|
84
|
+
return (Array.isArray(selector) ? selector : [selector]).map(globToRegex).some((re) => re.test(artifact.path));
|
|
85
|
+
}
|
|
86
|
+
/** Apply an ordered list of {@link ArtifactOverride}s to a list of artifacts. */
|
|
87
|
+
function applyOverrides(artifacts, overrides) {
|
|
88
|
+
if (overrides.length === 0) return [...artifacts];
|
|
89
|
+
const out = [];
|
|
90
|
+
for (const artifact of artifacts) {
|
|
91
|
+
let current = artifact;
|
|
92
|
+
for (const override of overrides) {
|
|
93
|
+
if (current === null) break;
|
|
94
|
+
if (!matchesSelector(override.match, current)) continue;
|
|
95
|
+
if (override.exclude) {
|
|
96
|
+
current = null;
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
if (override.rules !== void 0) {
|
|
100
|
+
const extra = parseShortRuleset(override.rules);
|
|
101
|
+
current = {
|
|
102
|
+
...current,
|
|
103
|
+
rules: [...current.rules, ...extra]
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
if (override.integrity === null) current = {
|
|
107
|
+
...current,
|
|
108
|
+
integrity: void 0,
|
|
109
|
+
discovery: void 0
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
if (current !== null) out.push(current);
|
|
113
|
+
}
|
|
114
|
+
return out;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
//#endregion
|
|
118
|
+
//#region lib/artifact-plugin.ts
|
|
119
|
+
/**
|
|
120
|
+
* Wraps a {@link TorbaPlugin} so that the artifacts it contributes are passed
|
|
121
|
+
* through an ordered list of {@link ArtifactOverride}s before being returned.
|
|
122
|
+
*
|
|
123
|
+
* Use this to give artifact-producing plugins (`forge`, `curseforge`, …) the
|
|
124
|
+
* same filter/patch support that `artifactScanner` already has natively. The
|
|
125
|
+
* inner plugin's `build` runs unchanged; only `Contribution.artifacts` is
|
|
126
|
+
* rewritten. Non-artifact contribution fields (`vars`, `launch`) pass through
|
|
127
|
+
* untouched.
|
|
128
|
+
*
|
|
129
|
+
* @param plugin The inner plugin to wrap.
|
|
130
|
+
* @param overrides Overrides applied to the inner plugin's artifacts. An empty
|
|
131
|
+
* list is a no-op (artifacts pass through unchanged).
|
|
132
|
+
*/
|
|
133
|
+
function defineArtifactPlugin(plugin, overrides) {
|
|
134
|
+
return {
|
|
135
|
+
name: plugin.name,
|
|
136
|
+
async build(ctx) {
|
|
137
|
+
const contribution = await plugin.build(ctx);
|
|
138
|
+
if (contribution.artifacts === void 0) return contribution;
|
|
139
|
+
return {
|
|
140
|
+
...contribution,
|
|
141
|
+
artifacts: applyOverrides(contribution.artifacts, overrides)
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
//#endregion
|
|
148
|
+
//#region lib/paths.ts
|
|
149
|
+
/**
|
|
150
|
+
* Per-user data directory for an application, matching OS conventions:
|
|
151
|
+
*
|
|
152
|
+
* - Windows: `%APPDATA%\<name>` (e.g. `C:\Users\you\AppData\Roaming\<name>`)
|
|
153
|
+
* - macOS: `~/Library/Application Support/<name>`
|
|
154
|
+
* - Linux: `$XDG_DATA_HOME/<name>` or `~/.local/share/<name>`
|
|
155
|
+
*/
|
|
156
|
+
function userDataDir(name) {
|
|
157
|
+
if (process.platform === "win32") return join(process.env.APPDATA ?? homedir(), name);
|
|
158
|
+
if (process.platform === "darwin") return join(homedir(), "Library", "Application Support", name);
|
|
159
|
+
return join(process.env.XDG_DATA_HOME ?? join(homedir(), ".local", "share"), name);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
//#endregion
|
|
163
|
+
//#region lib/scanner.ts
|
|
164
|
+
async function walkDir(dir) {
|
|
165
|
+
async function walk(cur) {
|
|
166
|
+
const entries = await readdir(cur, { withFileTypes: true });
|
|
167
|
+
return (await Promise.all(entries.map(async (e) => {
|
|
168
|
+
const abs = join(cur, e.name);
|
|
169
|
+
if (e.isDirectory()) return walk(abs);
|
|
170
|
+
if (e.isFile()) {
|
|
171
|
+
const { size } = await stat(abs);
|
|
172
|
+
const rel = relative(dir, abs).replace(/\\/g, "/");
|
|
173
|
+
const d = dirname(rel);
|
|
174
|
+
return [{
|
|
175
|
+
rel,
|
|
176
|
+
dir: d === "." ? "" : d,
|
|
177
|
+
filename: basename(rel),
|
|
178
|
+
abs,
|
|
179
|
+
size
|
|
180
|
+
}];
|
|
181
|
+
}
|
|
182
|
+
return [];
|
|
183
|
+
}))).flat();
|
|
184
|
+
}
|
|
185
|
+
return walk(dir);
|
|
186
|
+
}
|
|
187
|
+
async function hashFile(path, algo) {
|
|
188
|
+
return createHash(algo).update(await readFile(path)).digest("hex");
|
|
189
|
+
}
|
|
190
|
+
function applyTemplate(tpl, file) {
|
|
191
|
+
if (typeof tpl === "function") return tpl(file);
|
|
192
|
+
return interpolate(tpl, {
|
|
193
|
+
rel: file.rel,
|
|
194
|
+
dir: file.dir,
|
|
195
|
+
filename: file.filename
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
async function* scanDirectory(options, baseDir) {
|
|
199
|
+
const algo = options.hash ?? "sha1";
|
|
200
|
+
const sourceKind = options.source ?? "url";
|
|
201
|
+
const files = await walkDir(baseDir);
|
|
202
|
+
for (const file of files) {
|
|
203
|
+
const artifactPath = options.path ? applyTemplate(options.path, file) : file.rel;
|
|
204
|
+
let integrity;
|
|
205
|
+
let source;
|
|
206
|
+
if (sourceKind === "file") source = sourceFile(file.abs);
|
|
207
|
+
else {
|
|
208
|
+
source = sourceUrl(applyTemplate(options.url, file));
|
|
209
|
+
const digest = await hashFile(file.abs, algo);
|
|
210
|
+
integrity = algo === "sha1" ? { sha1: digest } : { sha256: digest };
|
|
211
|
+
}
|
|
212
|
+
yield {
|
|
213
|
+
path: artifactPath,
|
|
214
|
+
source,
|
|
215
|
+
size: file.size,
|
|
216
|
+
rules: [],
|
|
217
|
+
integrity
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
/** Scan a local directory tree into artifacts — a generic build-time plugin. */
|
|
222
|
+
function artifactScanner(options) {
|
|
223
|
+
return definePlugin({
|
|
224
|
+
name: "artifactScanner",
|
|
225
|
+
async build(ctx) {
|
|
226
|
+
const baseDir = isAbsolute(options.directory) ? options.directory : resolve(ctx.configDir, options.directory);
|
|
227
|
+
const scanned = [];
|
|
228
|
+
for await (const a of scanDirectory(options, baseDir)) scanned.push(a);
|
|
229
|
+
const artifacts = applyOverrides(scanned, options.overrides ?? []);
|
|
230
|
+
const dropped = scanned.length - artifacts.length;
|
|
231
|
+
ctx.log("artifactScanner", `scanned ${scanned.length} file(s)` + (dropped > 0 ? `, ${dropped} excluded` : ""));
|
|
232
|
+
return { artifacts };
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
//#endregion
|
|
238
|
+
export { applyOverrides, artifactScanner, buildManifest, defineArtifactPlugin, defineConfig, definePlugin, matchesSelector, resolveConfig, userDataDir };
|
|
78
239
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../lib/plugin.ts","../lib/config.ts","../lib/engine.ts"],"sourcesContent":["import type { Artifact, ValDefs, Val, Valset } from '@torba/core';\n\n/** Build-time context handed to every plugin's `build` hook. */\nexport interface BuildContext {\n /** Sanctioned build-time logging channel; auto-prefixed by plugin name. */\n log: (scope: string, message: string) => void;\n /** Absolute directory of `torba.config.mjs` — anchor for relative paths. */\n configDir: string;\n /** Value of `torba build --mode <m>`; empty string when unset. */\n mode: string;\n}\n\n/**\n * Named launch fragments a plugin exposes for the config's `command`/`args`\n * accessor functions — e.g. `{ jvmArgs, mainClass, gameArgs }` or `{ bin }`.\n */\nexport type LaunchGroups = Record<string, Valset | Val | string>;\n\n/** What a plugin's `build` hook returns. */\nexport interface Contribution {\n /** Artifacts to download/copy/extract. */\n artifacts?: Artifact[];\n /** Manifest vars this plugin owns. */\n vars?: ValDefs;\n /** Named launch fragments, exposed to the config's accessor functions. */\n launch?: LaunchGroups;\n}\n\n/**\n * A torba plugin — a pure-to-construct, bundler-style hook object. The\n * constructor (`forge('1.20.1-best')`, …) does zero I/O; all network/fs work\n * happens inside `build`, which the engine drives.\n */\nexport interface TorbaPlugin {\n name: string;\n build(ctx: BuildContext): Promise<Contribution> | Contribution;\n}\n\n/** Identity helper for authoring a plugin with inferred types. */\nexport function definePlugin(plugin: TorbaPlugin): TorbaPlugin {\n return plugin;\n}\n","import type { Artifact, ValDefs, Manifest, Val, Valset } from '@torba/core';\nimport type { TorbaPlugin, LaunchGroups } from './plugin';\n\n/** Map of plugin name → its launch groups, passed to launch accessors. */\nexport type PluginMap = Record<string, LaunchGroups>;\n\n/** One entry of a config's assembled `args` — flattened to a `Valset`. */\nexport type ArgItem = Valset | Val | string;\n\nexport interface TorbaManifestConfig {\n /** Hand-written literal artifacts, merged with plugin output (last wins). */\n artifacts?: Artifact[];\n /** Override/extra vars layered on top of the merged plugin vars. */\n vars?: ValDefs;\n /** Launch command — typically `({ java }) => java.bin`. */\n command: (plugins: PluginMap) => string;\n /** Launch args — author-ordered named groups, flattened to a `Valset`. */\n args: (plugins: PluginMap) => ArgItem[];\n /** Working directory for the launched process. */\n workdir?: string | ((plugins: PluginMap) => string);\n /** Environment variables for the launched process. */\n envs?: ValDefs | ((plugins: PluginMap) => ValDefs);\n /** `restrict` globs swept clean after install. */\n restrict?: string[];\n}\n\nexport interface TorbaConfig {\n /** Default manifest output path, relative to the config file. */\n output?: string;\n /** The plugins whose `build` hooks produce the manifest. */\n plugins: TorbaPlugin[];\n /** Declarative manifest fields, separate from tooling config. */\n manifest: TorbaManifestConfig;\n /**\n * Launch-time manifest patch. Re-run on every `torba launch`; the returned\n * partial is shallow-merged (per field) over the loaded manifest.\n */\n runClient?: (manifest: Manifest) => Partial<Manifest>;\n}\n\nexport interface TorbaConfigContext {\n /** Value of `torba build --mode <m>`; empty string when unset. */\n mode: string;\n}\n\nexport type TorbaConfigInput =\n | TorbaConfig\n | ((ctx: TorbaConfigContext) => TorbaConfig | Promise<TorbaConfig>);\n\n/** Use as the default export of `torba.config.mjs`. */\nexport function defineConfig(input: TorbaConfigInput): TorbaConfigInput {\n return input;\n}\n\n/** Resolve a config input to a concrete `TorbaConfig`. */\nexport async function resolveConfig(\n input: TorbaConfigInput,\n ctx: TorbaConfigContext,\n): Promise<TorbaConfig> {\n return typeof input === 'function' ? input(ctx) : input;\n}\n","import {\n type Manifest,\n type Artifact,\n type Launch,\n type ValDefs,\n type Val,\n type Valset,\n deduplicateArtifacts,\n} from '@torba/core';\nimport type { TorbaConfig, ArgItem, PluginMap } from './config';\nimport type { BuildContext } from './plugin';\n\n/** Flatten author-ordered launch groups into a single `Valset`. */\nfunction flattenArgs(items: ReadonlyArray<ArgItem>): Valset {\n const out: Val[] = [];\n for (const item of items) {\n if (typeof item === 'string') out.push({ rules: [], value: [item] });\n else if (Array.isArray(item)) out.push(...item);\n else out.push(item);\n }\n return out;\n}\n\n/**\n * Run every plugin's `build` hook in parallel, merge the contributions, and\n * assemble the final `Manifest`.\n */\nexport async function buildManifest(\n config: TorbaConfig,\n ctx: BuildContext,\n): Promise<Manifest> {\n ctx.log('torba', `resolving ${config.plugins.length} plugin(s)`);\n const results = await Promise.all(\n config.plugins.map(async (p) => ({\n name: p.name,\n contribution: await p.build(ctx),\n })),\n );\n\n // Artifacts: plugin output in list order, then literal manifest.artifacts.\n const artifacts: Artifact[] = [];\n for (const r of results) {\n if (r.contribution.artifacts) artifacts.push(...r.contribution.artifacts);\n }\n if (config.manifest.artifacts) artifacts.push(...config.manifest.artifacts);\n const deduped = deduplicateArtifacts(artifacts);\n\n // Vars: merge plugin vars (list order, last wins); warn on collisions.\n const vars: Record<string, ValDefs[string]> = {};\n const owner: Record<string, string> = {};\n for (const r of results) {\n if (!r.contribution.vars) continue;\n for (const [key, value] of Object.entries(r.contribution.vars)) {\n const prev = owner[key];\n if (prev !== undefined && prev !== r.name) {\n ctx.log(\n 'torba',\n `warning: var '${key}' set by both '${prev}' and '${r.name}' — using '${r.name}'`,\n );\n }\n vars[key] = value;\n owner[key] = r.name;\n }\n }\n // Config `vars` is the sanctioned override layer (silent).\n if (config.manifest.vars) {\n for (const [key, value] of Object.entries(config.manifest.vars)) {\n vars[key] = value;\n }\n }\n\n // Launch: author functions over the plugin → launch-groups map.\n const pluginMap: PluginMap = Object.fromEntries(\n results.map((r) => [r.name, r.contribution.launch ?? {}]),\n );\n const m = config.manifest;\n const launch: Launch = {\n command: m.command(pluginMap),\n workdir:\n typeof m.workdir === 'function'\n ? m.workdir(pluginMap)\n : (m.workdir ?? '.'),\n args: flattenArgs(m.args(pluginMap)),\n envs: typeof m.envs === 'function' ? m.envs(pluginMap) : (m.envs ?? {}),\n };\n\n ctx.log(\n 'torba',\n `merged ${deduped.length} artifact(s) (${artifacts.length - deduped.length} deduped)`,\n );\n\n return {\n vars,\n launch,\n artifacts: deduped,\n ...(m.restrict && m.restrict.length > 0 ? { restrict: m.restrict } : {}),\n };\n}\n"],"mappings":";;;;AAuCA,SAAgB,aAAa,QAAkC;AAC7D,QAAO;;;;;;ACUT,SAAgB,aAAa,OAA2C;AACtE,QAAO;;;AAIT,eAAsB,cACpB,OACA,KACsB;AACtB,QAAO,OAAO,UAAU,aAAa,MAAM,IAAI,GAAG;;;;;;AC9CpD,SAAS,YAAY,OAAuC;CAC1D,MAAM,MAAa,EAAE;AACrB,MAAK,MAAM,QAAQ,MACjB,KAAI,OAAO,SAAS,SAAU,KAAI,KAAK;EAAE,OAAO,EAAE;EAAE,OAAO,CAAC,KAAK;EAAE,CAAC;UAC3D,MAAM,QAAQ,KAAK,CAAE,KAAI,KAAK,GAAG,KAAK;KAC1C,KAAI,KAAK,KAAK;AAErB,QAAO;;;;;;AAOT,eAAsB,cACpB,QACA,KACmB;AACnB,KAAI,IAAI,SAAS,aAAa,OAAO,QAAQ,OAAO,YAAY;CAChE,MAAM,UAAU,MAAM,QAAQ,IAC5B,OAAO,QAAQ,IAAI,OAAO,OAAO;EAC/B,MAAM,EAAE;EACR,cAAc,MAAM,EAAE,MAAM,IAAI;EACjC,EAAE,CACJ;CAGD,MAAM,YAAwB,EAAE;AAChC,MAAK,MAAM,KAAK,QACd,KAAI,EAAE,aAAa,UAAW,WAAU,KAAK,GAAG,EAAE,aAAa,UAAU;AAE3E,KAAI,OAAO,SAAS,UAAW,WAAU,KAAK,GAAG,OAAO,SAAS,UAAU;CAC3E,MAAM,UAAU,qBAAqB,UAAU;CAG/C,MAAM,OAAwC,EAAE;CAChD,MAAM,QAAgC,EAAE;AACxC,MAAK,MAAM,KAAK,SAAS;AACvB,MAAI,CAAC,EAAE,aAAa,KAAM;AAC1B,OAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,EAAE,aAAa,KAAK,EAAE;GAC9D,MAAM,OAAO,MAAM;AACnB,OAAI,SAAS,UAAa,SAAS,EAAE,KACnC,KAAI,IACF,SACA,iBAAiB,IAAI,iBAAiB,KAAK,SAAS,EAAE,KAAK,aAAa,EAAE,KAAK,GAChF;AAEH,QAAK,OAAO;AACZ,SAAM,OAAO,EAAE;;;AAInB,KAAI,OAAO,SAAS,KAClB,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,OAAO,SAAS,KAAK,CAC7D,MAAK,OAAO;CAKhB,MAAM,YAAuB,OAAO,YAClC,QAAQ,KAAK,MAAM,CAAC,EAAE,MAAM,EAAE,aAAa,UAAU,EAAE,CAAC,CAAC,CAC1D;CACD,MAAM,IAAI,OAAO;CACjB,MAAM,SAAiB;EACrB,SAAS,EAAE,QAAQ,UAAU;EAC7B,SACE,OAAO,EAAE,YAAY,aACjB,EAAE,QAAQ,UAAU,GACnB,EAAE,WAAW;EACpB,MAAM,YAAY,EAAE,KAAK,UAAU,CAAC;EACpC,MAAM,OAAO,EAAE,SAAS,aAAa,EAAE,KAAK,UAAU,GAAI,EAAE,QAAQ,EAAE;EACvE;AAED,KAAI,IACF,SACA,UAAU,QAAQ,OAAO,gBAAgB,UAAU,SAAS,QAAQ,OAAO,WAC5E;AAED,QAAO;EACL;EACA;EACA,WAAW;EACX,GAAI,EAAE,YAAY,EAAE,SAAS,SAAS,IAAI,EAAE,UAAU,EAAE,UAAU,GAAG,EAAE;EACxE"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../lib/plugin.ts","../lib/config.ts","../lib/engine.ts","../lib/overrides.ts","../lib/artifact-plugin.ts","../lib/paths.ts","../lib/scanner.ts"],"sourcesContent":["import type { Artifact, ValDefs, Val, Valset } from '@torba/core';\n\n/** Build-time context handed to every plugin's `build` hook. */\nexport interface BuildContext {\n /** Sanctioned build-time logging channel; auto-prefixed by plugin name. */\n log: (scope: string, message: string) => void;\n /** Absolute directory of `torba.config.mjs` — anchor for relative paths. */\n configDir: string;\n /** Value of `torba build --mode <m>`; empty string when unset. */\n mode: string;\n}\n\n/**\n * Named launch fragments a plugin exposes for the config's `command`/`args`\n * accessor functions — e.g. `{ jvmArgs, mainClass, gameArgs }` or `{ bin }`.\n */\nexport type LaunchGroups = Record<string, Valset | Val | string>;\n\n/** What a plugin's `build` hook returns. */\nexport interface Contribution {\n /** Artifacts to download/copy/extract. */\n artifacts?: Artifact[];\n /** Manifest vars this plugin owns. */\n vars?: ValDefs;\n /** Named launch fragments, exposed to the config's accessor functions. */\n launch?: LaunchGroups;\n}\n\n/**\n * A torba plugin — a pure-to-construct, bundler-style hook object. The\n * constructor (`forge('1.20.1-best')`, …) does zero I/O; all network/fs work\n * happens inside `build`, which the engine drives.\n */\nexport interface TorbaPlugin {\n name: string;\n build(ctx: BuildContext): Promise<Contribution> | Contribution;\n}\n\n/** Identity helper for authoring a plugin with inferred types. */\nexport function definePlugin(plugin: TorbaPlugin): TorbaPlugin {\n return plugin;\n}\n","import type { Artifact, ValDefs, Manifest, Val, Valset } from '@torba/core';\nimport type { TorbaPlugin, LaunchGroups } from './plugin';\n\n/** Map of plugin name → its launch groups, passed to launch accessors. */\nexport type PluginMap = Record<string, LaunchGroups>;\n\n/** One entry of a config's assembled `args` — flattened to a `Valset`. */\nexport type ArgItem = Valset | Val | string;\n\nexport interface TorbaManifestConfig {\n /** Hand-written literal artifacts, merged with plugin output (last wins). */\n artifacts?: Artifact[];\n /** Override/extra vars layered on top of the merged plugin vars. */\n vars?: ValDefs;\n /** Launch command — typically `({ java }) => java.bin`. */\n command: (plugins: PluginMap) => string;\n /** Launch args — author-ordered named groups, flattened to a `Valset`. */\n args: (plugins: PluginMap) => ArgItem[];\n /** Working directory for the launched process. */\n workdir?: string | ((plugins: PluginMap) => string);\n /** Environment variables for the launched process. */\n envs?: ValDefs | ((plugins: PluginMap) => ValDefs);\n /** `restrict` globs swept clean after install. */\n restrict?: string[];\n}\n\nexport interface TorbaConfig {\n /** Default manifest output path, relative to the config file. */\n output?: string;\n /** The plugins whose `build` hooks produce the manifest. */\n plugins: TorbaPlugin[];\n /** Declarative manifest fields, separate from tooling config. */\n manifest: TorbaManifestConfig;\n /**\n * Launch-time manifest patch. Re-run on every `torba launch`; the returned\n * partial is shallow-merged (per field) over the loaded manifest.\n */\n runClient?: (manifest: Manifest) => Partial<Manifest>;\n}\n\nexport interface TorbaConfigContext {\n /** Value of `torba build --mode <m>`; empty string when unset. */\n mode: string;\n}\n\nexport type TorbaConfigInput =\n | TorbaConfig\n | ((ctx: TorbaConfigContext) => TorbaConfig | Promise<TorbaConfig>);\n\n/** Use as the default export of `torba.config.mjs`. */\nexport function defineConfig(input: TorbaConfigInput): TorbaConfigInput {\n return input;\n}\n\n/** Resolve a config input to a concrete `TorbaConfig`. */\nexport async function resolveConfig(\n input: TorbaConfigInput,\n ctx: TorbaConfigContext,\n): Promise<TorbaConfig> {\n return typeof input === 'function' ? input(ctx) : input;\n}\n","import {\n type Manifest,\n type Artifact,\n type Launch,\n type ValDefs,\n type Val,\n type Valset,\n deduplicateArtifacts,\n} from '@torba/core';\nimport type { TorbaConfig, ArgItem, PluginMap } from './config';\nimport type { BuildContext } from './plugin';\n\n/** Flatten author-ordered launch groups into a single `Valset`. */\nfunction flattenArgs(items: ReadonlyArray<ArgItem>): Valset {\n const out: Val[] = [];\n for (const item of items) {\n if (typeof item === 'string') out.push({ rules: [], value: [item] });\n else if (Array.isArray(item)) out.push(...item);\n else out.push(item);\n }\n return out;\n}\n\n/**\n * Run every plugin's `build` hook in parallel, merge the contributions, and\n * assemble the final `Manifest`.\n */\nexport async function buildManifest(\n config: TorbaConfig,\n ctx: BuildContext,\n): Promise<Manifest> {\n ctx.log('torba', `resolving ${config.plugins.length} plugin(s)`);\n const results = await Promise.all(\n config.plugins.map(async (p) => ({\n name: p.name,\n contribution: await p.build(ctx),\n })),\n );\n\n // Artifacts: plugin output in list order, then literal manifest.artifacts.\n const artifacts: Artifact[] = [];\n for (const r of results) {\n if (r.contribution.artifacts) artifacts.push(...r.contribution.artifacts);\n }\n if (config.manifest.artifacts) artifacts.push(...config.manifest.artifacts);\n const deduped = deduplicateArtifacts(artifacts);\n\n // Vars: merge plugin vars (list order, last wins); warn on collisions.\n const vars: Record<string, ValDefs[string]> = {};\n const owner: Record<string, string> = {};\n for (const r of results) {\n if (!r.contribution.vars) continue;\n for (const [key, value] of Object.entries(r.contribution.vars)) {\n const prev = owner[key];\n if (prev !== undefined && prev !== r.name) {\n ctx.log(\n 'torba',\n `warning: var '${key}' set by both '${prev}' and '${r.name}' — using '${r.name}'`,\n );\n }\n vars[key] = value;\n owner[key] = r.name;\n }\n }\n // Config `vars` is the sanctioned override layer (silent).\n if (config.manifest.vars) {\n for (const [key, value] of Object.entries(config.manifest.vars)) {\n vars[key] = value;\n }\n }\n\n // Launch: author functions over the plugin → launch-groups map.\n const pluginMap: PluginMap = Object.fromEntries(\n results.map((r) => [r.name, r.contribution.launch ?? {}]),\n );\n const m = config.manifest;\n const launch: Launch = {\n command: m.command(pluginMap),\n workdir:\n typeof m.workdir === 'function'\n ? m.workdir(pluginMap)\n : (m.workdir ?? '.'),\n args: flattenArgs(m.args(pluginMap)),\n envs: typeof m.envs === 'function' ? m.envs(pluginMap) : (m.envs ?? {}),\n };\n\n ctx.log(\n 'torba',\n `merged ${deduped.length} artifact(s) (${artifacts.length - deduped.length} deduped)`,\n );\n\n return {\n vars,\n launch,\n artifacts: deduped,\n ...(m.restrict && m.restrict.length > 0 ? { restrict: m.restrict } : {}),\n };\n}\n","import {\n type Artifact,\n type Ruleset,\n globToRegex,\n parseShortRuleset,\n} from '@torba/core';\n\n/**\n * Targets a subset of artifacts:\n *\n * - `string` / `string[]` — glob(s) matched against `artifact.path`\n * (multiple = OR).\n * - predicate — `(a) => boolean`, the escape hatch for matching on source\n * kind, size, metadata, …\n */\nexport type Selector = string | string[] | ((artifact: Artifact) => boolean);\n\nexport function matchesSelector(\n selector: Selector,\n artifact: Artifact,\n): boolean {\n if (typeof selector === 'function') return selector(artifact);\n const globs = (Array.isArray(selector) ? selector : [selector]).map(\n globToRegex,\n );\n return globs.some((re) => re.test(artifact.path));\n}\n\n/** A ruleset in any form `parseShortRuleset` accepts (shorthand or full). */\nexport type RulesetInput = Parameters<typeof parseShortRuleset>[0];\n\n/**\n * A per-selector patch over the artifacts a plugin produces. Each entry\n * names the files it `match`es and the changes to apply — drop them,\n * attach a ruleset, or clear integrity. Overrides run in list order; a\n * later entry sees the effect of an earlier one.\n */\nexport interface ArtifactOverride {\n /** Files this override applies to. */\n match: Selector;\n /** Drop matched artifacts entirely. */\n exclude?: boolean;\n /**\n * Ruleset to attach to matched artifacts — shorthand (`'allow.os.osx'`)\n * or a full `Ruleset`. Appended to each artifact's existing `rules`.\n */\n rules?: RulesetInput;\n /** `null` clears `integrity` + `discovery` (skip verification). */\n integrity?: null;\n}\n\n/** Apply an ordered list of {@link ArtifactOverride}s to a list of artifacts. */\nexport function applyOverrides(\n artifacts: readonly Artifact[],\n overrides: readonly ArtifactOverride[],\n): Artifact[] {\n if (overrides.length === 0) return [...artifacts];\n const out: Artifact[] = [];\n for (const artifact of artifacts) {\n let current: Artifact | null = artifact;\n for (const override of overrides) {\n if (current === null) break;\n if (!matchesSelector(override.match, current)) continue;\n if (override.exclude) {\n current = null;\n break;\n }\n if (override.rules !== undefined) {\n const extra: Ruleset = parseShortRuleset(override.rules);\n current = { ...current, rules: [...current.rules, ...extra] };\n }\n if (override.integrity === null) {\n current = { ...current, integrity: undefined, discovery: undefined };\n }\n }\n if (current !== null) out.push(current);\n }\n return out;\n}\n","import type { Contribution, TorbaPlugin } from './plugin';\nimport { applyOverrides, type ArtifactOverride } from './overrides';\n\n/**\n * Wraps a {@link TorbaPlugin} so that the artifacts it contributes are passed\n * through an ordered list of {@link ArtifactOverride}s before being returned.\n *\n * Use this to give artifact-producing plugins (`forge`, `curseforge`, …) the\n * same filter/patch support that `artifactScanner` already has natively. The\n * inner plugin's `build` runs unchanged; only `Contribution.artifacts` is\n * rewritten. Non-artifact contribution fields (`vars`, `launch`) pass through\n * untouched.\n *\n * @param plugin The inner plugin to wrap.\n * @param overrides Overrides applied to the inner plugin's artifacts. An empty\n * list is a no-op (artifacts pass through unchanged).\n */\nexport function defineArtifactPlugin(\n plugin: TorbaPlugin,\n overrides: readonly ArtifactOverride[],\n): TorbaPlugin {\n return {\n name: plugin.name,\n async build(ctx) {\n const contribution: Contribution = await plugin.build(ctx);\n if (contribution.artifacts === undefined) return contribution;\n return {\n ...contribution,\n artifacts: applyOverrides(contribution.artifacts, overrides),\n };\n },\n };\n}\n","import { homedir } from 'node:os';\nimport { join } from 'node:path';\n\n/**\n * Per-user data directory for an application, matching OS conventions:\n *\n * - Windows: `%APPDATA%\\<name>` (e.g. `C:\\Users\\you\\AppData\\Roaming\\<name>`)\n * - macOS: `~/Library/Application Support/<name>`\n * - Linux: `$XDG_DATA_HOME/<name>` or `~/.local/share/<name>`\n */\nexport function userDataDir(name: string): string {\n if (process.platform === 'win32') {\n return join(process.env.APPDATA ?? homedir(), name);\n }\n if (process.platform === 'darwin') {\n return join(homedir(), 'Library', 'Application Support', name);\n }\n const base = process.env.XDG_DATA_HOME ?? join(homedir(), '.local', 'share');\n return join(base, name);\n}\n","import { createHash } from 'node:crypto';\nimport { readFile, readdir, stat } from 'node:fs/promises';\nimport {\n basename,\n dirname,\n isAbsolute,\n join,\n relative,\n resolve,\n} from 'node:path';\nimport type { Artifact, Integrity, Source } from '@torba/core';\nimport { sourceFile, sourceUrl, interpolate } from '@torba/core';\nimport { definePlugin, type TorbaPlugin } from './plugin';\nimport { applyOverrides, type ArtifactOverride } from './overrides';\n\n/** A file discovered by {@link artifactScanner}, passed to `path`/`url` functions. */\nexport interface ScannedFile {\n /** Path relative to `directory`, POSIX separators. */\n readonly rel: string;\n /** Directory portion of `rel` (`''` at the root). */\n readonly dir: string;\n /** Final path segment. */\n readonly filename: string;\n /** Absolute path on the build machine. */\n readonly abs: string;\n}\n\n/**\n * A `path` / `url` value: either a template string — interpolating the\n * per-file placeholders `${rel}` / `${dir}` / `${filename}`, with any other\n * `${var}` left for install — or a `(file) => string` function. Only the\n * function form receives the build-machine `abs` path.\n */\nexport type ScanTemplate = string | ((file: ScannedFile) => string);\n\nexport interface ArtifactScannerOptions {\n /** Directory to scan. */\n directory: string;\n /** URL for fetching each file — template string or `(file) => string`. */\n url: ScanTemplate;\n /** Destination path — template or function. Defaults to the file's `rel`. */\n path?: ScanTemplate;\n hash?: 'sha1' | 'sha256';\n /**\n * 'url' → emit sourceUrl + computed hash (default)\n * 'file' → emit sourceFile pointing at the local copy; skip hashing entirely\n */\n source?: 'url' | 'file';\n /**\n * Per-selector patches applied to the scanned artifacts — exclude files,\n * attach rulesets (OS / feature gates), or clear integrity.\n */\n overrides?: ArtifactOverride[];\n}\n\n/** A {@link ScannedFile} carrying its on-disk `size`. */\ntype ScannedEntry = ScannedFile & { readonly size: number };\n\nasync function walkDir(dir: string): Promise<ScannedEntry[]> {\n async function walk(cur: string): Promise<ScannedEntry[]> {\n const entries = await readdir(cur, { withFileTypes: true });\n const results = await Promise.all(\n entries.map(async (e) => {\n const abs = join(cur, e.name);\n if (e.isDirectory()) return walk(abs);\n if (e.isFile()) {\n const { size } = await stat(abs);\n const rel = relative(dir, abs).replace(/\\\\/g, '/');\n const d = dirname(rel);\n return [\n {\n rel,\n dir: d === '.' ? '' : d,\n filename: basename(rel),\n abs,\n size,\n },\n ];\n }\n return [];\n }),\n );\n return results.flat();\n }\n return walk(dir);\n}\n\nasync function hashFile(\n path: string,\n algo: 'sha1' | 'sha256',\n): Promise<string> {\n return createHash(algo)\n .update(await readFile(path))\n .digest('hex');\n}\n\nfunction applyTemplate(tpl: ScanTemplate, file: ScannedFile): string {\n if (typeof tpl === 'function') return tpl(file);\n return interpolate(tpl, {\n rel: file.rel,\n dir: file.dir,\n filename: file.filename,\n });\n}\n\nasync function* scanDirectory(\n options: ArtifactScannerOptions,\n baseDir: string,\n): AsyncGenerator<Artifact> {\n const algo = options.hash ?? 'sha1';\n const sourceKind = options.source ?? 'url';\n const files = await walkDir(baseDir);\n\n for (const file of files) {\n const artifactPath = options.path\n ? applyTemplate(options.path, file)\n : file.rel;\n\n let integrity: Integrity | undefined;\n let source: Source;\n if (sourceKind === 'file') {\n source = sourceFile(file.abs);\n // trust local file by path — skip hash computation\n } else {\n source = sourceUrl(applyTemplate(options.url, file));\n const digest = await hashFile(file.abs, algo);\n integrity = algo === 'sha1' ? { sha1: digest } : { sha256: digest };\n }\n\n yield {\n path: artifactPath,\n source,\n size: file.size,\n rules: [],\n integrity,\n };\n }\n}\n\n/** Scan a local directory tree into artifacts — a generic build-time plugin. */\nexport function artifactScanner(options: ArtifactScannerOptions): TorbaPlugin {\n return definePlugin({\n name: 'artifactScanner',\n async build(ctx) {\n const baseDir = isAbsolute(options.directory)\n ? options.directory\n : resolve(ctx.configDir, options.directory);\n const scanned: Artifact[] = [];\n for await (const a of scanDirectory(options, baseDir)) scanned.push(a);\n const artifacts = applyOverrides(scanned, options.overrides ?? []);\n const dropped = scanned.length - artifacts.length;\n ctx.log(\n 'artifactScanner',\n `scanned ${scanned.length} file(s)` +\n (dropped > 0 ? `, ${dropped} excluded` : ''),\n );\n return { artifacts };\n },\n });\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@torba/dev",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.11",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"module": "./dist/index.js",
|
|
6
6
|
"exports": {
|
|
@@ -14,16 +14,20 @@
|
|
|
14
14
|
],
|
|
15
15
|
"scripts": {
|
|
16
16
|
"build": "tsdown lib/index.ts --format esm,cjs --dts --clean",
|
|
17
|
+
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
17
18
|
"test": "vitest run tests/unit --passWithNoTests"
|
|
18
19
|
},
|
|
19
20
|
"type": "module",
|
|
20
21
|
"dependencies": {
|
|
21
|
-
"@torba/core": "^1.0.
|
|
22
|
+
"@torba/core": "^1.0.11"
|
|
22
23
|
},
|
|
23
24
|
"peerDependencies": {
|
|
24
25
|
"zod": "^4.0.0"
|
|
25
26
|
},
|
|
26
27
|
"devDependencies": {
|
|
27
28
|
"vitest": "*"
|
|
29
|
+
},
|
|
30
|
+
"engines": {
|
|
31
|
+
"node": ">=20"
|
|
28
32
|
}
|
|
29
33
|
}
|