@heroiclands/package-build 23.12.7 → 23.12.8

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 CHANGED
@@ -1,5 +1,11 @@
1
1
  # @heroiclands/package-build
2
2
 
3
+ ## 23.12.8
4
+
5
+ ### Patch Changes
6
+
7
+ A `packageBuild.assets` row that copies files into `icons/` or `images/` now ships them beside the staged pictures instead of having them erased. A shipped picture whose path matches a file a row already staged is an error, and the row's file is left as it was.
8
+
3
9
  ## 23.12.7
4
10
 
5
11
  ### Patch Changes
@@ -47,7 +47,7 @@ role: portrait
47
47
 
48
48
  ## What the Foundry package ships
49
49
 
50
- The repository keeps every picture; the Foundry package carries a filtered copy, laid out flat. A picture ships when its record says `ai: false` and its licence is one of `LicenseRef-PublicDomain`, `CC0-1.0`, `CC0`, `CC-BY-3.0`, `CC-BY-4.0`, `CC-BY-SA-4.0` or `LicenseRef-NounProject-Pro`, together with any licence the package adds under `packageBuild.shippingLicenses`. A shipped picture sits at `icons/<basename>` or `images/<basename>` at the package root, whatever directories it is kept in here, and the package's asset set, `asset-sets/default.json`, lists what it ships.
50
+ The repository keeps every picture; the Foundry package carries a filtered copy, laid out flat. A picture ships when its record says `ai: false` and its licence is one of `LicenseRef-PublicDomain`, `CC0-1.0`, `CC0`, `CC-BY-3.0`, `CC-BY-4.0`, `CC-BY-SA-4.0` or `LicenseRef-NounProject-Pro`, together with any licence the package adds under `packageBuild.shippingLicenses`. A shipped picture sits at `icons/<basename>` or `images/<basename>` at the package root, whatever directories it is kept in here, and the package's asset set, `asset-sets/default.json`, lists what it ships. A `packageBuild.assets` row may copy other files under `icons/` or `images/`; the shipped pictures are written beside them, and a row's file at the same path as a shipped picture is an error.
51
51
 
52
52
  A note may still name a picture the package does not ship. Its compiled document draws the package's stand-in instead — `icons/missing.<ext>` or `images/missing.<ext>`, which you provide as an ordinary human-made picture licensed to ship, addressed `<package>-none-icon-missing` and `<package>-none-image-missing` — and keeps the real picture's address, so a world that holds the real file can point back at it. A package that leaves out a picture a note names, and ships no stand-in of that type, fails the build at the note. See [what a package ships](../reference/format-details.md#what-a-package-ships).
53
53
 
package/docs/commands.md CHANGED
@@ -350,9 +350,16 @@ the shipping list (the default list plus `packageBuild.shippingLicenses`) is cop
350
350
  at the package root, through the same transform, and the package's asset set,
351
351
  `asset-sets/default.json`, lists what was staged. Everything else stays in the repository. See
352
352
  [what a package ships](reference/format-details.md#what-a-package-ships).
353
- A `packageBuild.assets` row that copies a picture root, a directory inside
354
- one, or a directory holding one is refused, because it would ship every file
355
- unfiltered.
353
+
354
+ The pictures are written beside whatever a `packageBuild.assets` row copied
355
+ into `icons/` or `images/`, and nothing there is removed. A picture whose
356
+ deployed path is already a file a row staged is an error, and no picture is
357
+ staged:
358
+
359
+ > ``build/stage/images/gate.png: error: `images/gate.png` is already in the stage, copied by a `packageBuild.assets` row; the picture `assets/images/places/gate.png` was not staged over it``
360
+ > A `packageBuild.assets` row that copies a picture root, a directory inside
361
+ > one, or a directory holding one is refused, because it would ship every file
362
+ > unfiltered.
356
363
 
357
364
  **OPTIONS**
358
365
 
@@ -1905,6 +1905,13 @@ holding one is refused at the row:
1905
1905
 
1906
1906
  > ``package-build.config.yaml:<line>:<column>: error: `packageBuild.assets[<index>]` copies `<from>`, which reaches assets/images/ — the stage copies the icon and image roots itself, flat and filtered by each file's provenance, so a row copying them would ship every file unfiltered. Remove the row``
1907
1907
 
1908
+ A row may still copy other files into `icons/` or `images/` — a subdirectory of
1909
+ older art, say. The staged pictures are written beside them and remove nothing.
1910
+ A picture whose flat path is already a file a row staged is an error, and the
1911
+ file the row staged stays as it is:
1912
+
1913
+ > ``<stage>/images/<basename>: error: `images/<basename>` is already in the stage, copied by a `packageBuild.assets` row; the picture `<source>` was not staged over it``
1914
+
1908
1915
  | Key | Type | Required |
1909
1916
  | ------------------------------- | -------------------------- | -------- |
1910
1917
  | `packageBuild.assets[].from` | string | yes |
@@ -2493,6 +2493,12 @@ layout serves Foundry (`modules/thalorna/images/thorn.webp`), the website's CDN
2493
2493
  (`<cdn base>/thalorna/images/thorn.webp`), and every location a world copies
2494
2494
  the pictures to.
2495
2495
 
2496
+ **A row's files are kept.** Staging the pictures writes them beside whatever a
2497
+ `packageBuild.assets` row copied under `icons/` or `images/`, such as a
2498
+ subdirectory of other art, and removes none of it. A picture whose flat path is
2499
+ already a file a row staged is an error, and the row's file stays. Clearing an
2500
+ earlier build's pictures is the stage reset's work.
2501
+
2496
2502
  **The package's asset set**, `asset-sets/default.json` under the package
2497
2503
  root, lists what it ships. The name is fixed, so a world always finds the
2498
2504
  package's own set there:
package/e2e.mjs CHANGED
@@ -799,7 +799,7 @@ export function suiteExecutables(command) {
799
799
  }
800
800
 
801
801
  /** The Chrome for Testing build shared by local and CI Cypress runs. */
802
- export const CHROME_BUILD_ID = "155.0.8059.84";
802
+ export const CHROME_BUILD_ID = "158.0.8110.0";
803
803
 
804
804
  /**
805
805
  * The browser cache survives reseeding and container teardown.
@@ -270,6 +270,11 @@ export function assetsManifest(staged, stageDir) {
270
270
  * stage before a file is copied: a package whose provenance cannot be read is
271
271
  * not one whose shipping can be decided.
272
272
  *
273
+ * The pictures are written beside whatever a `packageBuild.assets` row already
274
+ * put under `icons/` and `images/`; nothing there is removed. A picture whose
275
+ * deployed path is already a file in the stage is a finding, and no picture is
276
+ * written. Clearing an earlier run's pictures is the stage reset's work.
277
+ *
273
278
  * @param {object} opts - Options.
274
279
  * @param {string} opts.assetsBase - The package's asset directory, the roots'
275
280
  * parent.
@@ -304,15 +309,24 @@ export function stagePackageAssets({
304
309
  if (findings.length) return { staged: [], excluded: [], findings, manifest: null };
305
310
 
306
311
  const { staged, excluded } = selectStagedAssets(records, shippingLicenses);
307
- for (const { root } of DEPLOYED_ASSET_TYPES) {
308
- fs.rmSync(path.join(stageDir, root), { recursive: true, force: true });
312
+ const planned = staged.map((record) => ({
313
+ record,
314
+ from: path.join(assetsBase, ...record.asset.path.split("/")),
315
+ to: path.join(stageDir, .../** @type {string} */ (deployedAssetPath(record)).split("/")),
316
+ }));
317
+ for (const { record, from, to } of planned) {
318
+ if (!fs.existsSync(to)) continue;
319
+ findings.push({
320
+ file: to,
321
+ severity: "error",
322
+ message:
323
+ `\`${deployedAssetPath(record)}\` is already in the stage, copied by a ` +
324
+ `\`packageBuild.assets\` row; the picture \`${path.relative(process.cwd(), from)}\` ` +
325
+ "was not staged over it",
326
+ });
309
327
  }
310
- for (const record of staged) {
311
- const from = path.join(assetsBase, ...record.asset.path.split("/"));
312
- const to = path.join(
313
- stageDir,
314
- .../** @type {string} */ (deployedAssetPath(record)).split("/"),
315
- );
328
+ if (findings.length) return { staged: [], excluded: [], findings, manifest: null };
329
+ for (const { from, to } of planned) {
316
330
  fs.mkdirSync(path.dirname(to), { recursive: true });
317
331
  const transformed = transform ? transform(from) : null;
318
332
  if (transformed != null) fs.writeFileSync(to, transformed);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heroiclands/package-build",
3
- "version": "23.12.7",
3
+ "version": "23.12.8",
4
4
  "description": "Shared toolchain for building and shipping a HeroicLands Foundry VTT package — content compilation, manifest, localization, staging, bundle, release and deployment.",
5
5
  "license": "GPL-3.0-or-later",
6
6
  "type": "module",
@@ -160,7 +160,7 @@
160
160
  "markdown-it": "^15.0.0",
161
161
  "markdown-it-deflist": "^4.0.0",
162
162
  "markdown-it-footnote": "^4.0.0",
163
- "markdownlint": "^0.41.1",
163
+ "markdownlint": "^0.42.0",
164
164
  "minimatch": "^10.2.6",
165
165
  "pagefind": "^1.5.2",
166
166
  "prettier": "^3.9.6",
package/types/e2e.d.mts CHANGED
@@ -419,7 +419,7 @@ export function suiteCommand(config: object, mode: "run" | "open"): string[];
419
419
  export function e2eRun({ config, packageJson, mode, suiteArgs, env, log, }: {
420
420
  config: object;
421
421
  packageJson: object;
422
- mode?: "open" | "run" | undefined;
422
+ mode?: "run" | "open" | undefined;
423
423
  suiteArgs?: string[] | undefined;
424
424
  env?: NodeJS.ProcessEnv | undefined;
425
425
  log?: ((message: string) => void) | undefined;
@@ -494,7 +494,7 @@ export const E2E_SCENE_ID: "heroiclandsScene";
494
494
  /** How the suite may be run. */
495
495
  export const E2E_MODES: readonly string[];
496
496
  /** The Chrome for Testing build shared by local and CI Cypress runs. */
497
- export const CHROME_BUILD_ID: "155.0.8059.84";
497
+ export const CHROME_BUILD_ID: "158.0.8110.0";
498
498
  /**
499
499
  * The disposable world's identity and credentials.
500
500
  */
@@ -69,7 +69,7 @@ export type AddressPosition = {
69
69
  * - Where the Addresses are.
70
70
  * Defaults to `value`.
71
71
  */
72
- holds?: "keys" | "value" | "items" | undefined;
72
+ holds?: "keys" | "items" | "value" | undefined;
73
73
  /**
74
74
  * - Resolve each exact target to its published Item UUID.
75
75
  */
@@ -75,7 +75,7 @@ export class AddressLink {
75
75
  constructor(target: object, anchor: string, kind?: "prose" | "event" | null);
76
76
  target: object;
77
77
  anchor: string;
78
- kind: "event" | "prose" | null;
78
+ kind: "prose" | "event" | null;
79
79
  }
80
80
  /** The keys of an Address as the published index writes it, and no others. */
81
81
  export const PUBLISHED_ADDRESS_KEYS: readonly string[];
@@ -110,6 +110,11 @@ export function assetsManifest(staged: readonly object[], stageDir: string): Ass
110
110
  * stage before a file is copied: a package whose provenance cannot be read is
111
111
  * not one whose shipping can be decided.
112
112
  *
113
+ * The pictures are written beside whatever a `packageBuild.assets` row already
114
+ * put under `icons/` and `images/`; nothing there is removed. A picture whose
115
+ * deployed path is already a file in the stage is a finding, and no picture is
116
+ * written. Clearing an earlier run's pictures is the stage reset's work.
117
+ *
113
118
  * @param {object} opts - Options.
114
119
  * @param {string} opts.assetsBase - The package's asset directory, the roots'
115
120
  * parent.
@@ -117,7 +117,7 @@ export function checkDeclaredFields({ format, itemFields, system, severity }: {
117
117
  format: import("./content-format.mjs").ContentFormat;
118
118
  itemFields: Record<string, readonly object[]>;
119
119
  system: string;
120
- severity?: "error" | "warning" | undefined;
120
+ severity?: "warning" | "error" | undefined;
121
121
  }): {
122
122
  findings: object[];
123
123
  coverage: object[];
@@ -423,7 +423,7 @@ export type DataFieldSpec = {
423
423
  * - The anchor kinds an
424
424
  * Address here may name with `#<anchor>`. Absent, an anchor is refused.
425
425
  */
426
- anchors?: readonly ("event" | "prose")[] | undefined;
426
+ anchors?: readonly ("prose" | "event")[] | undefined;
427
427
  /**
428
428
  * - The Address names exactly one event: an
429
429
  * anchor of kind `event`, or a note holding one event and no more.
@@ -37,7 +37,7 @@ export function coverage({ books, corpus, unprinted, mode, locate }: {
37
37
  }>;
38
38
  corpus: object[];
39
39
  unprinted?: object[] | undefined;
40
- mode?: "error" | "off" | "warning" | undefined;
40
+ mode?: "warning" | "error" | "off" | undefined;
41
41
  locate?: ((record: object) => {
42
42
  file?: string;
43
43
  line?: number;
@@ -126,7 +126,7 @@ export type MapRow = {
126
126
  /**
127
127
  * - Where the match must sit.
128
128
  */
129
- at?: "end" | "start" | undefined;
129
+ at?: "start" | "end" | undefined;
130
130
  };
131
131
  /**
132
132
  * A map ready to apply.
@@ -38,7 +38,7 @@
38
38
  export function packRelease({ stageDir, outDir, artifact, metadataDir, pdf, }?: {
39
39
  stageDir?: string | undefined;
40
40
  outDir?: string | undefined;
41
- artifact?: "module" | "system" | undefined;
41
+ artifact?: "system" | "module" | undefined;
42
42
  metadataDir?: string | undefined;
43
43
  pdf?: boolean | undefined;
44
44
  }): Promise<{