create-pixi-vn 2.1.3 → 2.1.4

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "create-pixi-vn",
3
3
  "description": "Create a new Pixi’VN project",
4
- "version": "2.1.3",
4
+ "version": "2.1.4",
5
5
  "type": "module",
6
6
  "license": "GPL-3.0",
7
7
  "author": "DRincs-Productions",
@@ -1,5 +1,83 @@
1
- import type { AssetPackConfig } from "@assetpack/core";
1
+ import type { AssetPackConfig, AssetPipe } from "@assetpack/core";
2
+ import { path } from "@assetpack/core";
2
3
  import { pixiPipes } from "@assetpack/core/pixi";
4
+ import fs from "node:fs/promises";
5
+
6
+ const manifestOutput = "src/assets/manifest.gen.json";
7
+
8
+ /**
9
+ * AssetPack only creates a manifest bundle for folders explicitly tagged
10
+ * with "{m}" (see pixiManifest's `manifest` tag); everything else lands in
11
+ * the single "default" bundle. This splits the "default" bundle into one
12
+ * bundle per top-level folder under the assets entry, named after that
13
+ * folder, so bundles mirror the folder structure without needing to tag
14
+ * every folder manually. Files directly at the entry root (no folder) stay
15
+ * in "default". Runs after the manifest has been written to disk, so it
16
+ * re-reads and rewrites the file.
17
+ */
18
+ function groupBundlesByFolder(options: { output: string }): AssetPipe<{ output: string }> {
19
+ return {
20
+ name: "group-bundles-by-folder",
21
+ defaultOptions: options,
22
+ async finish(_asset, opts) {
23
+ const manifest = JSON.parse(await fs.readFile(opts.output, "utf-8"));
24
+
25
+ const bundles = new Map<string, { name: string; assets: unknown[] }>();
26
+ for (const bundle of manifest.bundles) {
27
+ if (bundle.name !== "default") {
28
+ bundles.set(bundle.name, bundle);
29
+ continue;
30
+ }
31
+ for (const asset of bundle.assets) {
32
+ const folder = path.dirname(asset.src[0]).split("/")[0];
33
+ const bundleName = folder === "." ? "default" : folder;
34
+ const target = bundles.get(bundleName) ?? { name: bundleName, assets: [] };
35
+ target.assets.push(asset);
36
+ bundles.set(bundleName, target);
37
+ }
38
+ }
39
+ manifest.bundles = [...bundles.values()];
40
+
41
+ await fs.writeFile(opts.output, JSON.stringify(manifest, null, 2));
42
+ },
43
+ };
44
+ }
45
+
46
+ /**
47
+ * Removes file extensions from the aliases generated by the pixi manifest
48
+ * pipe and replaces path separators with underscores (e.g.
49
+ * "yard/yard1.png" -> "yard_yard1"). Runs after the manifest has been
50
+ * written to disk, so it re-reads and rewrites the file.
51
+ *
52
+ * If two assets end up with the same normalized alias, the later one is
53
+ * prefixed with its bundle name (e.g. "yard_yard_yard1") to keep aliases
54
+ * unique across the whole manifest.
55
+ */
56
+ function normalizeAliases(options: { output: string }): AssetPipe<{ output: string }> {
57
+ return {
58
+ name: "normalize-aliases",
59
+ defaultOptions: options,
60
+ async finish(_asset, opts) {
61
+ const manifest = JSON.parse(await fs.readFile(opts.output, "utf-8"));
62
+
63
+ const seen = new Set<string>();
64
+ for (const bundle of manifest.bundles) {
65
+ for (const asset of bundle.assets) {
66
+ asset.alias = asset.alias.map((alias: string) => {
67
+ const normalized = path.trimExt(alias).replaceAll("/", "_");
68
+ const unique = seen.has(normalized)
69
+ ? `${bundle.name}_${normalized}`
70
+ : normalized;
71
+ seen.add(normalized);
72
+ return unique;
73
+ });
74
+ }
75
+ }
76
+
77
+ await fs.writeFile(opts.output, JSON.stringify(manifest, null, 2));
78
+ },
79
+ };
80
+ }
3
81
 
4
82
  const config: AssetPackConfig = {
5
83
  entry: "./src/assets",
@@ -8,10 +86,12 @@ const config: AssetPackConfig = {
8
86
  pipes: [
9
87
  ...pixiPipes({
10
88
  manifest: {
11
- output: "src/assets/manifest.gen.json",
89
+ output: manifestOutput,
12
90
  createShortcuts: true,
13
91
  },
14
92
  }),
93
+ groupBundlesByFolder({ output: manifestOutput }),
94
+ normalizeAliases({ output: manifestOutput }),
15
95
  ],
16
96
  };
17
97
 
@@ -1,5 +1,83 @@
1
- import type { AssetPackConfig } from "@assetpack/core";
1
+ import type { AssetPackConfig, AssetPipe } from "@assetpack/core";
2
+ import { path } from "@assetpack/core";
2
3
  import { pixiPipes } from "@assetpack/core/pixi";
4
+ import fs from "node:fs/promises";
5
+
6
+ const manifestOutput = "src/assets/manifest.gen.json";
7
+
8
+ /**
9
+ * AssetPack only creates a manifest bundle for folders explicitly tagged
10
+ * with "{m}" (see pixiManifest's `manifest` tag); everything else lands in
11
+ * the single "default" bundle. This splits the "default" bundle into one
12
+ * bundle per top-level folder under the assets entry, named after that
13
+ * folder, so bundles mirror the folder structure without needing to tag
14
+ * every folder manually. Files directly at the entry root (no folder) stay
15
+ * in "default". Runs after the manifest has been written to disk, so it
16
+ * re-reads and rewrites the file.
17
+ */
18
+ function groupBundlesByFolder(options: { output: string }): AssetPipe<{ output: string }> {
19
+ return {
20
+ name: "group-bundles-by-folder",
21
+ defaultOptions: options,
22
+ async finish(_asset, opts) {
23
+ const manifest = JSON.parse(await fs.readFile(opts.output, "utf-8"));
24
+
25
+ const bundles = new Map<string, { name: string; assets: unknown[] }>();
26
+ for (const bundle of manifest.bundles) {
27
+ if (bundle.name !== "default") {
28
+ bundles.set(bundle.name, bundle);
29
+ continue;
30
+ }
31
+ for (const asset of bundle.assets) {
32
+ const folder = path.dirname(asset.src[0]).split("/")[0];
33
+ const bundleName = folder === "." ? "default" : folder;
34
+ const target = bundles.get(bundleName) ?? { name: bundleName, assets: [] };
35
+ target.assets.push(asset);
36
+ bundles.set(bundleName, target);
37
+ }
38
+ }
39
+ manifest.bundles = [...bundles.values()];
40
+
41
+ await fs.writeFile(opts.output, JSON.stringify(manifest, null, 2));
42
+ },
43
+ };
44
+ }
45
+
46
+ /**
47
+ * Removes file extensions from the aliases generated by the pixi manifest
48
+ * pipe and replaces path separators with underscores (e.g.
49
+ * "yard/yard1.png" -> "yard_yard1"). Runs after the manifest has been
50
+ * written to disk, so it re-reads and rewrites the file.
51
+ *
52
+ * If two assets end up with the same normalized alias, the later one is
53
+ * prefixed with its bundle name (e.g. "yard_yard_yard1") to keep aliases
54
+ * unique across the whole manifest.
55
+ */
56
+ function normalizeAliases(options: { output: string }): AssetPipe<{ output: string }> {
57
+ return {
58
+ name: "normalize-aliases",
59
+ defaultOptions: options,
60
+ async finish(_asset, opts) {
61
+ const manifest = JSON.parse(await fs.readFile(opts.output, "utf-8"));
62
+
63
+ const seen = new Set<string>();
64
+ for (const bundle of manifest.bundles) {
65
+ for (const asset of bundle.assets) {
66
+ asset.alias = asset.alias.map((alias: string) => {
67
+ const normalized = path.trimExt(alias).replaceAll("/", "_");
68
+ const unique = seen.has(normalized)
69
+ ? `${bundle.name}_${normalized}`
70
+ : normalized;
71
+ seen.add(normalized);
72
+ return unique;
73
+ });
74
+ }
75
+ }
76
+
77
+ await fs.writeFile(opts.output, JSON.stringify(manifest, null, 2));
78
+ },
79
+ };
80
+ }
3
81
 
4
82
  const config: AssetPackConfig = {
5
83
  entry: "./src/assets",
@@ -8,10 +86,12 @@ const config: AssetPackConfig = {
8
86
  pipes: [
9
87
  ...pixiPipes({
10
88
  manifest: {
11
- output: "src/assets/manifest.gen.json",
89
+ output: manifestOutput,
12
90
  createShortcuts: true,
13
91
  },
14
92
  }),
93
+ groupBundlesByFolder({ output: manifestOutput }),
94
+ normalizeAliases({ output: manifestOutput }),
15
95
  ],
16
96
  };
17
97
 
@@ -1,9 +1,87 @@
1
- import type { AssetPackConfig } from "@assetpack/core";
1
+ import type { AssetPackConfig, AssetPipe } from "@assetpack/core";
2
+ import { path } from "@assetpack/core";
2
3
  import { pixiPipes } from "@assetpack/core/pixi";
4
+ import fs from "node:fs/promises";
3
5
 
4
6
  // TAURI_ENV_TARGET_TRIPLE is set by `tauri build` before running beforeBuildCommand
5
7
  const isTauri = !!process.env.TAURI_ENV_TARGET_TRIPLE;
6
8
 
9
+ const manifestOutput = "src/assets/manifest.gen.json";
10
+
11
+ /**
12
+ * AssetPack only creates a manifest bundle for folders explicitly tagged
13
+ * with "{m}" (see pixiManifest's `manifest` tag); everything else lands in
14
+ * the single "default" bundle. This splits the "default" bundle into one
15
+ * bundle per top-level folder under the assets entry, named after that
16
+ * folder, so bundles mirror the folder structure without needing to tag
17
+ * every folder manually. Files directly at the entry root (no folder) stay
18
+ * in "default". Runs after the manifest has been written to disk, so it
19
+ * re-reads and rewrites the file.
20
+ */
21
+ function groupBundlesByFolder(options: { output: string }): AssetPipe<{ output: string }> {
22
+ return {
23
+ name: "group-bundles-by-folder",
24
+ defaultOptions: options,
25
+ async finish(_asset, opts) {
26
+ const manifest = JSON.parse(await fs.readFile(opts.output, "utf-8"));
27
+
28
+ const bundles = new Map<string, { name: string; assets: unknown[] }>();
29
+ for (const bundle of manifest.bundles) {
30
+ if (bundle.name !== "default") {
31
+ bundles.set(bundle.name, bundle);
32
+ continue;
33
+ }
34
+ for (const asset of bundle.assets) {
35
+ const folder = path.dirname(asset.src[0]).split("/")[0];
36
+ const bundleName = folder === "." ? "default" : folder;
37
+ const target = bundles.get(bundleName) ?? { name: bundleName, assets: [] };
38
+ target.assets.push(asset);
39
+ bundles.set(bundleName, target);
40
+ }
41
+ }
42
+ manifest.bundles = [...bundles.values()];
43
+
44
+ await fs.writeFile(opts.output, JSON.stringify(manifest, null, 2));
45
+ },
46
+ };
47
+ }
48
+
49
+ /**
50
+ * Removes file extensions from the aliases generated by the pixi manifest
51
+ * pipe and replaces path separators with underscores (e.g.
52
+ * "yard/yard1.png" -> "yard_yard1"). Runs after the manifest has been
53
+ * written to disk, so it re-reads and rewrites the file.
54
+ *
55
+ * If two assets end up with the same normalized alias, the later one is
56
+ * prefixed with its bundle name (e.g. "yard_yard_yard1") to keep aliases
57
+ * unique across the whole manifest.
58
+ */
59
+ function normalizeAliases(options: { output: string }): AssetPipe<{ output: string }> {
60
+ return {
61
+ name: "normalize-aliases",
62
+ defaultOptions: options,
63
+ async finish(_asset, opts) {
64
+ const manifest = JSON.parse(await fs.readFile(opts.output, "utf-8"));
65
+
66
+ const seen = new Set<string>();
67
+ for (const bundle of manifest.bundles) {
68
+ for (const asset of bundle.assets) {
69
+ asset.alias = asset.alias.map((alias: string) => {
70
+ const normalized = path.trimExt(alias).replaceAll("/", "_");
71
+ const unique = seen.has(normalized)
72
+ ? `${bundle.name}_${normalized}`
73
+ : normalized;
74
+ seen.add(normalized);
75
+ return unique;
76
+ });
77
+ }
78
+ }
79
+
80
+ await fs.writeFile(opts.output, JSON.stringify(manifest, null, 2));
81
+ },
82
+ };
83
+ }
84
+
7
85
  const config: AssetPackConfig = {
8
86
  entry: "./src/assets",
9
87
  output: "./public/assets",
@@ -11,7 +89,7 @@ const config: AssetPackConfig = {
11
89
  pipes: [
12
90
  ...pixiPipes({
13
91
  manifest: {
14
- output: "src/assets/manifest.gen.json",
92
+ output: manifestOutput,
15
93
  createShortcuts: true,
16
94
  },
17
95
  // For Tauri: skip @0.5x mipmaps (unused on desktop, WebView handles DPR)
@@ -21,6 +99,8 @@ const config: AssetPackConfig = {
21
99
  ? { png: true, jpg: true, webp: { quality: 88, alphaQuality: 88 } }
22
100
  : undefined,
23
101
  }),
102
+ groupBundlesByFolder({ output: manifestOutput }),
103
+ normalizeAliases({ output: manifestOutput }),
24
104
  ],
25
105
  };
26
106
 
@@ -1,9 +1,87 @@
1
- import type { AssetPackConfig } from "@assetpack/core";
1
+ import type { AssetPackConfig, AssetPipe } from "@assetpack/core";
2
+ import { path } from "@assetpack/core";
2
3
  import { pixiPipes } from "@assetpack/core/pixi";
4
+ import fs from "node:fs/promises";
3
5
 
4
6
  // TAURI_ENV_TARGET_TRIPLE is set by `tauri build` before running beforeBuildCommand
5
7
  const isTauri = !!process.env.TAURI_ENV_TARGET_TRIPLE;
6
8
 
9
+ const manifestOutput = "src/assets/manifest.gen.json";
10
+
11
+ /**
12
+ * AssetPack only creates a manifest bundle for folders explicitly tagged
13
+ * with "{m}" (see pixiManifest's `manifest` tag); everything else lands in
14
+ * the single "default" bundle. This splits the "default" bundle into one
15
+ * bundle per top-level folder under the assets entry, named after that
16
+ * folder, so bundles mirror the folder structure without needing to tag
17
+ * every folder manually. Files directly at the entry root (no folder) stay
18
+ * in "default". Runs after the manifest has been written to disk, so it
19
+ * re-reads and rewrites the file.
20
+ */
21
+ function groupBundlesByFolder(options: { output: string }): AssetPipe<{ output: string }> {
22
+ return {
23
+ name: "group-bundles-by-folder",
24
+ defaultOptions: options,
25
+ async finish(_asset, opts) {
26
+ const manifest = JSON.parse(await fs.readFile(opts.output, "utf-8"));
27
+
28
+ const bundles = new Map<string, { name: string; assets: unknown[] }>();
29
+ for (const bundle of manifest.bundles) {
30
+ if (bundle.name !== "default") {
31
+ bundles.set(bundle.name, bundle);
32
+ continue;
33
+ }
34
+ for (const asset of bundle.assets) {
35
+ const folder = path.dirname(asset.src[0]).split("/")[0];
36
+ const bundleName = folder === "." ? "default" : folder;
37
+ const target = bundles.get(bundleName) ?? { name: bundleName, assets: [] };
38
+ target.assets.push(asset);
39
+ bundles.set(bundleName, target);
40
+ }
41
+ }
42
+ manifest.bundles = [...bundles.values()];
43
+
44
+ await fs.writeFile(opts.output, JSON.stringify(manifest, null, 2));
45
+ },
46
+ };
47
+ }
48
+
49
+ /**
50
+ * Removes file extensions from the aliases generated by the pixi manifest
51
+ * pipe and replaces path separators with underscores (e.g.
52
+ * "yard/yard1.png" -> "yard_yard1"). Runs after the manifest has been
53
+ * written to disk, so it re-reads and rewrites the file.
54
+ *
55
+ * If two assets end up with the same normalized alias, the later one is
56
+ * prefixed with its bundle name (e.g. "yard_yard_yard1") to keep aliases
57
+ * unique across the whole manifest.
58
+ */
59
+ function normalizeAliases(options: { output: string }): AssetPipe<{ output: string }> {
60
+ return {
61
+ name: "normalize-aliases",
62
+ defaultOptions: options,
63
+ async finish(_asset, opts) {
64
+ const manifest = JSON.parse(await fs.readFile(opts.output, "utf-8"));
65
+
66
+ const seen = new Set<string>();
67
+ for (const bundle of manifest.bundles) {
68
+ for (const asset of bundle.assets) {
69
+ asset.alias = asset.alias.map((alias: string) => {
70
+ const normalized = path.trimExt(alias).replaceAll("/", "_");
71
+ const unique = seen.has(normalized)
72
+ ? `${bundle.name}_${normalized}`
73
+ : normalized;
74
+ seen.add(normalized);
75
+ return unique;
76
+ });
77
+ }
78
+ }
79
+
80
+ await fs.writeFile(opts.output, JSON.stringify(manifest, null, 2));
81
+ },
82
+ };
83
+ }
84
+
7
85
  const config: AssetPackConfig = {
8
86
  entry: "./src/assets",
9
87
  output: "./public/assets",
@@ -11,7 +89,7 @@ const config: AssetPackConfig = {
11
89
  pipes: [
12
90
  ...pixiPipes({
13
91
  manifest: {
14
- output: "src/assets/manifest.gen.json",
92
+ output: manifestOutput,
15
93
  createShortcuts: true,
16
94
  },
17
95
  // For Tauri: skip @0.5x mipmaps (unused on desktop, WebView handles DPR)
@@ -21,6 +99,8 @@ const config: AssetPackConfig = {
21
99
  ? { png: true, jpg: true, webp: { quality: 88, alphaQuality: 88 } }
22
100
  : undefined,
23
101
  }),
102
+ groupBundlesByFolder({ output: manifestOutput }),
103
+ normalizeAliases({ output: manifestOutput }),
24
104
  ],
25
105
  };
26
106
 
@@ -1,5 +1,83 @@
1
- import type { AssetPackConfig } from "@assetpack/core";
1
+ import type { AssetPackConfig, AssetPipe } from "@assetpack/core";
2
+ import { path } from "@assetpack/core";
2
3
  import { pixiPipes } from "@assetpack/core/pixi";
4
+ import fs from "node:fs/promises";
5
+
6
+ const manifestOutput = "src/assets/manifest.gen.json";
7
+
8
+ /**
9
+ * AssetPack only creates a manifest bundle for folders explicitly tagged
10
+ * with "{m}" (see pixiManifest's `manifest` tag); everything else lands in
11
+ * the single "default" bundle. This splits the "default" bundle into one
12
+ * bundle per top-level folder under the assets entry, named after that
13
+ * folder, so bundles mirror the folder structure without needing to tag
14
+ * every folder manually. Files directly at the entry root (no folder) stay
15
+ * in "default". Runs after the manifest has been written to disk, so it
16
+ * re-reads and rewrites the file.
17
+ */
18
+ function groupBundlesByFolder(options: { output: string }): AssetPipe<{ output: string }> {
19
+ return {
20
+ name: "group-bundles-by-folder",
21
+ defaultOptions: options,
22
+ async finish(_asset, opts) {
23
+ const manifest = JSON.parse(await fs.readFile(opts.output, "utf-8"));
24
+
25
+ const bundles = new Map<string, { name: string; assets: unknown[] }>();
26
+ for (const bundle of manifest.bundles) {
27
+ if (bundle.name !== "default") {
28
+ bundles.set(bundle.name, bundle);
29
+ continue;
30
+ }
31
+ for (const asset of bundle.assets) {
32
+ const folder = path.dirname(asset.src[0]).split("/")[0];
33
+ const bundleName = folder === "." ? "default" : folder;
34
+ const target = bundles.get(bundleName) ?? { name: bundleName, assets: [] };
35
+ target.assets.push(asset);
36
+ bundles.set(bundleName, target);
37
+ }
38
+ }
39
+ manifest.bundles = [...bundles.values()];
40
+
41
+ await fs.writeFile(opts.output, JSON.stringify(manifest, null, 2));
42
+ },
43
+ };
44
+ }
45
+
46
+ /**
47
+ * Removes file extensions from the aliases generated by the pixi manifest
48
+ * pipe and replaces path separators with underscores (e.g.
49
+ * "yard/yard1.png" -> "yard_yard1"). Runs after the manifest has been
50
+ * written to disk, so it re-reads and rewrites the file.
51
+ *
52
+ * If two assets end up with the same normalized alias, the later one is
53
+ * prefixed with its bundle name (e.g. "yard_yard_yard1") to keep aliases
54
+ * unique across the whole manifest.
55
+ */
56
+ function normalizeAliases(options: { output: string }): AssetPipe<{ output: string }> {
57
+ return {
58
+ name: "normalize-aliases",
59
+ defaultOptions: options,
60
+ async finish(_asset, opts) {
61
+ const manifest = JSON.parse(await fs.readFile(opts.output, "utf-8"));
62
+
63
+ const seen = new Set<string>();
64
+ for (const bundle of manifest.bundles) {
65
+ for (const asset of bundle.assets) {
66
+ asset.alias = asset.alias.map((alias: string) => {
67
+ const normalized = path.trimExt(alias).replaceAll("/", "_");
68
+ const unique = seen.has(normalized)
69
+ ? `${bundle.name}_${normalized}`
70
+ : normalized;
71
+ seen.add(normalized);
72
+ return unique;
73
+ });
74
+ }
75
+ }
76
+
77
+ await fs.writeFile(opts.output, JSON.stringify(manifest, null, 2));
78
+ },
79
+ };
80
+ }
3
81
 
4
82
  const config: AssetPackConfig = {
5
83
  entry: "./src/assets",
@@ -8,10 +86,12 @@ const config: AssetPackConfig = {
8
86
  pipes: [
9
87
  ...pixiPipes({
10
88
  manifest: {
11
- output: "src/assets/manifest.gen.json",
89
+ output: manifestOutput,
12
90
  createShortcuts: true,
13
91
  },
14
92
  }),
93
+ groupBundlesByFolder({ output: manifestOutput }),
94
+ normalizeAliases({ output: manifestOutput }),
15
95
  ],
16
96
  };
17
97
 
@@ -1,5 +1,83 @@
1
- import type { AssetPackConfig } from "@assetpack/core";
1
+ import type { AssetPackConfig, AssetPipe } from "@assetpack/core";
2
+ import { path } from "@assetpack/core";
2
3
  import { pixiPipes } from "@assetpack/core/pixi";
4
+ import fs from "node:fs/promises";
5
+
6
+ const manifestOutput = "src/assets/manifest.gen.json";
7
+
8
+ /**
9
+ * AssetPack only creates a manifest bundle for folders explicitly tagged
10
+ * with "{m}" (see pixiManifest's `manifest` tag); everything else lands in
11
+ * the single "default" bundle. This splits the "default" bundle into one
12
+ * bundle per top-level folder under the assets entry, named after that
13
+ * folder, so bundles mirror the folder structure without needing to tag
14
+ * every folder manually. Files directly at the entry root (no folder) stay
15
+ * in "default". Runs after the manifest has been written to disk, so it
16
+ * re-reads and rewrites the file.
17
+ */
18
+ function groupBundlesByFolder(options: { output: string }): AssetPipe<{ output: string }> {
19
+ return {
20
+ name: "group-bundles-by-folder",
21
+ defaultOptions: options,
22
+ async finish(_asset, opts) {
23
+ const manifest = JSON.parse(await fs.readFile(opts.output, "utf-8"));
24
+
25
+ const bundles = new Map<string, { name: string; assets: unknown[] }>();
26
+ for (const bundle of manifest.bundles) {
27
+ if (bundle.name !== "default") {
28
+ bundles.set(bundle.name, bundle);
29
+ continue;
30
+ }
31
+ for (const asset of bundle.assets) {
32
+ const folder = path.dirname(asset.src[0]).split("/")[0];
33
+ const bundleName = folder === "." ? "default" : folder;
34
+ const target = bundles.get(bundleName) ?? { name: bundleName, assets: [] };
35
+ target.assets.push(asset);
36
+ bundles.set(bundleName, target);
37
+ }
38
+ }
39
+ manifest.bundles = [...bundles.values()];
40
+
41
+ await fs.writeFile(opts.output, JSON.stringify(manifest, null, 2));
42
+ },
43
+ };
44
+ }
45
+
46
+ /**
47
+ * Removes file extensions from the aliases generated by the pixi manifest
48
+ * pipe and replaces path separators with underscores (e.g.
49
+ * "yard/yard1.png" -> "yard_yard1"). Runs after the manifest has been
50
+ * written to disk, so it re-reads and rewrites the file.
51
+ *
52
+ * If two assets end up with the same normalized alias, the later one is
53
+ * prefixed with its bundle name (e.g. "yard_yard_yard1") to keep aliases
54
+ * unique across the whole manifest.
55
+ */
56
+ function normalizeAliases(options: { output: string }): AssetPipe<{ output: string }> {
57
+ return {
58
+ name: "normalize-aliases",
59
+ defaultOptions: options,
60
+ async finish(_asset, opts) {
61
+ const manifest = JSON.parse(await fs.readFile(opts.output, "utf-8"));
62
+
63
+ const seen = new Set<string>();
64
+ for (const bundle of manifest.bundles) {
65
+ for (const asset of bundle.assets) {
66
+ asset.alias = asset.alias.map((alias: string) => {
67
+ const normalized = path.trimExt(alias).replaceAll("/", "_");
68
+ const unique = seen.has(normalized)
69
+ ? `${bundle.name}_${normalized}`
70
+ : normalized;
71
+ seen.add(normalized);
72
+ return unique;
73
+ });
74
+ }
75
+ }
76
+
77
+ await fs.writeFile(opts.output, JSON.stringify(manifest, null, 2));
78
+ },
79
+ };
80
+ }
3
81
 
4
82
  const config: AssetPackConfig = {
5
83
  entry: "./src/assets",
@@ -8,10 +86,12 @@ const config: AssetPackConfig = {
8
86
  pipes: [
9
87
  ...pixiPipes({
10
88
  manifest: {
11
- output: "src/assets/manifest.gen.json",
89
+ output: manifestOutput,
12
90
  createShortcuts: true,
13
91
  },
14
92
  }),
93
+ groupBundlesByFolder({ output: manifestOutput }),
94
+ normalizeAliases({ output: manifestOutput }),
15
95
  ],
16
96
  };
17
97
 
@@ -1,9 +1,87 @@
1
- import type { AssetPackConfig } from "@assetpack/core";
1
+ import type { AssetPackConfig, AssetPipe } from "@assetpack/core";
2
+ import { path } from "@assetpack/core";
2
3
  import { pixiPipes } from "@assetpack/core/pixi";
4
+ import fs from "node:fs/promises";
3
5
 
4
6
  // TAURI_ENV_TARGET_TRIPLE is set by `tauri build` before running beforeBuildCommand
5
7
  const isTauri = !!process.env.TAURI_ENV_TARGET_TRIPLE;
6
8
 
9
+ const manifestOutput = "src/assets/manifest.gen.json";
10
+
11
+ /**
12
+ * AssetPack only creates a manifest bundle for folders explicitly tagged
13
+ * with "{m}" (see pixiManifest's `manifest` tag); everything else lands in
14
+ * the single "default" bundle. This splits the "default" bundle into one
15
+ * bundle per top-level folder under the assets entry, named after that
16
+ * folder, so bundles mirror the folder structure without needing to tag
17
+ * every folder manually. Files directly at the entry root (no folder) stay
18
+ * in "default". Runs after the manifest has been written to disk, so it
19
+ * re-reads and rewrites the file.
20
+ */
21
+ function groupBundlesByFolder(options: { output: string }): AssetPipe<{ output: string }> {
22
+ return {
23
+ name: "group-bundles-by-folder",
24
+ defaultOptions: options,
25
+ async finish(_asset, opts) {
26
+ const manifest = JSON.parse(await fs.readFile(opts.output, "utf-8"));
27
+
28
+ const bundles = new Map<string, { name: string; assets: unknown[] }>();
29
+ for (const bundle of manifest.bundles) {
30
+ if (bundle.name !== "default") {
31
+ bundles.set(bundle.name, bundle);
32
+ continue;
33
+ }
34
+ for (const asset of bundle.assets) {
35
+ const folder = path.dirname(asset.src[0]).split("/")[0];
36
+ const bundleName = folder === "." ? "default" : folder;
37
+ const target = bundles.get(bundleName) ?? { name: bundleName, assets: [] };
38
+ target.assets.push(asset);
39
+ bundles.set(bundleName, target);
40
+ }
41
+ }
42
+ manifest.bundles = [...bundles.values()];
43
+
44
+ await fs.writeFile(opts.output, JSON.stringify(manifest, null, 2));
45
+ },
46
+ };
47
+ }
48
+
49
+ /**
50
+ * Removes file extensions from the aliases generated by the pixi manifest
51
+ * pipe and replaces path separators with underscores (e.g.
52
+ * "yard/yard1.png" -> "yard_yard1"). Runs after the manifest has been
53
+ * written to disk, so it re-reads and rewrites the file.
54
+ *
55
+ * If two assets end up with the same normalized alias, the later one is
56
+ * prefixed with its bundle name (e.g. "yard_yard_yard1") to keep aliases
57
+ * unique across the whole manifest.
58
+ */
59
+ function normalizeAliases(options: { output: string }): AssetPipe<{ output: string }> {
60
+ return {
61
+ name: "normalize-aliases",
62
+ defaultOptions: options,
63
+ async finish(_asset, opts) {
64
+ const manifest = JSON.parse(await fs.readFile(opts.output, "utf-8"));
65
+
66
+ const seen = new Set<string>();
67
+ for (const bundle of manifest.bundles) {
68
+ for (const asset of bundle.assets) {
69
+ asset.alias = asset.alias.map((alias: string) => {
70
+ const normalized = path.trimExt(alias).replaceAll("/", "_");
71
+ const unique = seen.has(normalized)
72
+ ? `${bundle.name}_${normalized}`
73
+ : normalized;
74
+ seen.add(normalized);
75
+ return unique;
76
+ });
77
+ }
78
+ }
79
+
80
+ await fs.writeFile(opts.output, JSON.stringify(manifest, null, 2));
81
+ },
82
+ };
83
+ }
84
+
7
85
  const config: AssetPackConfig = {
8
86
  entry: "./src/assets",
9
87
  output: "./public/assets",
@@ -11,7 +89,7 @@ const config: AssetPackConfig = {
11
89
  pipes: [
12
90
  ...pixiPipes({
13
91
  manifest: {
14
- output: "src/assets/manifest.gen.json",
92
+ output: manifestOutput,
15
93
  createShortcuts: true,
16
94
  },
17
95
  // For Tauri: skip @0.5x mipmaps (unused on desktop, WebView handles DPR)
@@ -21,6 +99,8 @@ const config: AssetPackConfig = {
21
99
  ? { png: true, jpg: true, webp: { quality: 88, alphaQuality: 88 } }
22
100
  : undefined,
23
101
  }),
102
+ groupBundlesByFolder({ output: manifestOutput }),
103
+ normalizeAliases({ output: manifestOutput }),
24
104
  ],
25
105
  };
26
106
 
@@ -1,9 +1,87 @@
1
- import type { AssetPackConfig } from "@assetpack/core";
1
+ import type { AssetPackConfig, AssetPipe } from "@assetpack/core";
2
+ import { path } from "@assetpack/core";
2
3
  import { pixiPipes } from "@assetpack/core/pixi";
4
+ import fs from "node:fs/promises";
3
5
 
4
6
  // TAURI_ENV_TARGET_TRIPLE is set by `tauri build` before running beforeBuildCommand
5
7
  const isTauri = !!process.env.TAURI_ENV_TARGET_TRIPLE;
6
8
 
9
+ const manifestOutput = "src/assets/manifest.gen.json";
10
+
11
+ /**
12
+ * AssetPack only creates a manifest bundle for folders explicitly tagged
13
+ * with "{m}" (see pixiManifest's `manifest` tag); everything else lands in
14
+ * the single "default" bundle. This splits the "default" bundle into one
15
+ * bundle per top-level folder under the assets entry, named after that
16
+ * folder, so bundles mirror the folder structure without needing to tag
17
+ * every folder manually. Files directly at the entry root (no folder) stay
18
+ * in "default". Runs after the manifest has been written to disk, so it
19
+ * re-reads and rewrites the file.
20
+ */
21
+ function groupBundlesByFolder(options: { output: string }): AssetPipe<{ output: string }> {
22
+ return {
23
+ name: "group-bundles-by-folder",
24
+ defaultOptions: options,
25
+ async finish(_asset, opts) {
26
+ const manifest = JSON.parse(await fs.readFile(opts.output, "utf-8"));
27
+
28
+ const bundles = new Map<string, { name: string; assets: unknown[] }>();
29
+ for (const bundle of manifest.bundles) {
30
+ if (bundle.name !== "default") {
31
+ bundles.set(bundle.name, bundle);
32
+ continue;
33
+ }
34
+ for (const asset of bundle.assets) {
35
+ const folder = path.dirname(asset.src[0]).split("/")[0];
36
+ const bundleName = folder === "." ? "default" : folder;
37
+ const target = bundles.get(bundleName) ?? { name: bundleName, assets: [] };
38
+ target.assets.push(asset);
39
+ bundles.set(bundleName, target);
40
+ }
41
+ }
42
+ manifest.bundles = [...bundles.values()];
43
+
44
+ await fs.writeFile(opts.output, JSON.stringify(manifest, null, 2));
45
+ },
46
+ };
47
+ }
48
+
49
+ /**
50
+ * Removes file extensions from the aliases generated by the pixi manifest
51
+ * pipe and replaces path separators with underscores (e.g.
52
+ * "yard/yard1.png" -> "yard_yard1"). Runs after the manifest has been
53
+ * written to disk, so it re-reads and rewrites the file.
54
+ *
55
+ * If two assets end up with the same normalized alias, the later one is
56
+ * prefixed with its bundle name (e.g. "yard_yard_yard1") to keep aliases
57
+ * unique across the whole manifest.
58
+ */
59
+ function normalizeAliases(options: { output: string }): AssetPipe<{ output: string }> {
60
+ return {
61
+ name: "normalize-aliases",
62
+ defaultOptions: options,
63
+ async finish(_asset, opts) {
64
+ const manifest = JSON.parse(await fs.readFile(opts.output, "utf-8"));
65
+
66
+ const seen = new Set<string>();
67
+ for (const bundle of manifest.bundles) {
68
+ for (const asset of bundle.assets) {
69
+ asset.alias = asset.alias.map((alias: string) => {
70
+ const normalized = path.trimExt(alias).replaceAll("/", "_");
71
+ const unique = seen.has(normalized)
72
+ ? `${bundle.name}_${normalized}`
73
+ : normalized;
74
+ seen.add(normalized);
75
+ return unique;
76
+ });
77
+ }
78
+ }
79
+
80
+ await fs.writeFile(opts.output, JSON.stringify(manifest, null, 2));
81
+ },
82
+ };
83
+ }
84
+
7
85
  const config: AssetPackConfig = {
8
86
  entry: "./src/assets",
9
87
  output: "./public/assets",
@@ -11,7 +89,7 @@ const config: AssetPackConfig = {
11
89
  pipes: [
12
90
  ...pixiPipes({
13
91
  manifest: {
14
- output: "src/assets/manifest.gen.json",
92
+ output: manifestOutput,
15
93
  createShortcuts: true,
16
94
  },
17
95
  // For Tauri: skip @0.5x mipmaps (unused on desktop, WebView handles DPR)
@@ -21,6 +99,8 @@ const config: AssetPackConfig = {
21
99
  ? { png: true, jpg: true, webp: { quality: 88, alphaQuality: 88 } }
22
100
  : undefined,
23
101
  }),
102
+ groupBundlesByFolder({ output: manifestOutput }),
103
+ normalizeAliases({ output: manifestOutput }),
24
104
  ],
25
105
  };
26
106