@caperjs/core 0.2.4 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/assetpack.mjs +90 -0
- package/build/index.mjs +3 -1
- package/build/internal/discovery.mjs +1 -1
- package/build/internal/manifest.mjs +8 -3
- package/build/internal/validate.mjs +3 -1
- package/build/plugins/assetTypes.mjs +4 -4
- package/build/plugins/caperConfig.mjs +12 -5
- package/build/plugins/lists.mjs +29 -9
- package/build/plugins/pruneFallbacks.mjs +25 -2
- package/cli/create.mjs +57 -6
- package/cli/create.test.mjs +36 -0
- package/cli/update.mjs +1 -1
- package/lib/{CaptionsPlugin-WPAcfAkx.js → CaptionsPlugin-cSJj-R9M.js} +5 -5
- package/lib/{CaptionsPlugin-WPAcfAkx.js.map → CaptionsPlugin-cSJj-R9M.js.map} +1 -1
- package/lib/{DataAdapter-DS-twowe.js → DataAdapter-D2tc6hx9.js} +15 -10
- package/lib/DataAdapter-D2tc6hx9.js.map +1 -0
- package/lib/{DebugRenderer-D1ryrRYx.js → DebugRenderer-CGUKy6Fy.js} +2 -2
- package/lib/{DebugRenderer-D1ryrRYx.js.map → DebugRenderer-CGUKy6Fy.js.map} +1 -1
- package/lib/{DevToolsPlugin-DH7zX7HU.js → DevToolsPlugin-B8GX3H9b.js} +2 -2
- package/lib/{DevToolsPlugin-DH7zX7HU.js.map → DevToolsPlugin-B8GX3H9b.js.map} +1 -1
- package/lib/{GSAPPlugin-BBtRjUqe.js → GSAPPlugin-Br9AjuTD.js} +6 -6
- package/lib/{GSAPPlugin-BBtRjUqe.js.map → GSAPPlugin-Br9AjuTD.js.map} +1 -1
- package/lib/{LayoutPlugin-Co6vcoV4.js → LayoutPlugin-BX3OP7pw.js} +4 -4
- package/lib/{LayoutPlugin-Co6vcoV4.js.map → LayoutPlugin-BX3OP7pw.js.map} +1 -1
- package/lib/{SpinePlugin-uN641Hmk.js → SpinePlugin-C-JDdXpf.js} +690 -793
- package/lib/SpinePlugin-C-JDdXpf.js.map +1 -0
- package/lib/{StatsPlugin-vZQRm6wh.js → StatsPlugin-yJlJtoQ2.js} +7 -7
- package/lib/StatsPlugin-yJlJtoQ2.js.map +1 -0
- package/lib/{VoiceOverPlugin-KlZdGj1G.js → VoiceOverPlugin-DjSZp4Ko.js} +7 -6
- package/lib/VoiceOverPlugin-DjSZp4Ko.js.map +1 -0
- package/lib/caper.mjs +159 -1917
- package/lib/caper.mjs.map +1 -1
- package/lib/const-C3sxeYhl.js +1746 -0
- package/lib/const-C3sxeYhl.js.map +1 -0
- package/lib/core/Application.d.ts +8 -3
- package/lib/core/Application.d.ts.map +1 -1
- package/lib/core/Application.test.d.ts +2 -0
- package/lib/core/Application.test.d.ts.map +1 -0
- package/lib/core/config.d.ts +6 -1
- package/lib/core/config.d.ts.map +1 -1
- package/lib/core/config.test.d.ts +2 -0
- package/lib/core/config.test.d.ts.map +1 -0
- package/lib/core/create.d.ts.map +1 -1
- package/lib/core/interfaces/IApplication.d.ts +6 -6
- package/lib/core/interfaces/IApplication.d.ts.map +1 -1
- package/lib/core/interfaces/ICoreSignals.d.ts +2 -1
- package/lib/core/interfaces/ICoreSignals.d.ts.map +1 -1
- package/lib/display/Camera.test.d.ts +2 -0
- package/lib/display/Camera.test.d.ts.map +1 -0
- package/lib/display/Entity.d.ts +2 -2
- package/lib/display/SceneTransition.test.d.ts +1 -0
- package/lib/display/SceneTransition.test.d.ts.map +1 -0
- package/lib/display/SpineAnimation.d.ts +3 -3
- package/lib/mixins/factory/methods.d.ts.map +1 -1
- package/lib/padding-1kHDpGlQ.js +41 -0
- package/lib/padding-1kHDpGlQ.js.map +1 -0
- package/lib/plugins/AssetsPlugin.d.ts.map +1 -1
- package/lib/plugins/AssetsPlugin.test.d.ts +2 -0
- package/lib/plugins/AssetsPlugin.test.d.ts.map +1 -0
- package/lib/plugins/DataAdapter.d.ts.map +1 -1
- package/lib/plugins/DataAdapter.test.d.ts +2 -0
- package/lib/plugins/DataAdapter.test.d.ts.map +1 -0
- package/lib/plugins/FullScreenPlugin.d.ts +0 -5
- package/lib/plugins/FullScreenPlugin.d.ts.map +1 -1
- package/lib/plugins/FullScreenPlugin.test.d.ts +2 -0
- package/lib/plugins/FullScreenPlugin.test.d.ts.map +1 -0
- package/lib/plugins/GSAPPlugin.d.ts +0 -2
- package/lib/plugins/GSAPPlugin.d.ts.map +1 -1
- package/lib/plugins/GSAPPlugin.test.d.ts +2 -0
- package/lib/plugins/GSAPPlugin.test.d.ts.map +1 -0
- package/lib/plugins/KeyboardPlugin.d.ts +0 -1
- package/lib/plugins/KeyboardPlugin.d.ts.map +1 -1
- package/lib/plugins/LookupPlugin.d.ts +6 -1
- package/lib/plugins/LookupPlugin.d.ts.map +1 -1
- package/lib/plugins/LookupPlugin.test.d.ts +2 -0
- package/lib/plugins/LookupPlugin.test.d.ts.map +1 -0
- package/lib/plugins/Plugin.d.ts +101 -2
- package/lib/plugins/Plugin.d.ts.map +1 -1
- package/lib/plugins/PopupManagerPlugin.d.ts.map +1 -1
- package/lib/plugins/PopupManagerPlugin.test.d.ts +1 -0
- package/lib/plugins/PopupManagerPlugin.test.d.ts.map +1 -0
- package/lib/plugins/SceneManagerPlugin.d.ts +1 -1
- package/lib/plugins/SceneManagerPlugin.d.ts.map +1 -1
- package/lib/plugins/SceneManagerPlugin.test.d.ts +2 -0
- package/lib/plugins/SceneManagerPlugin.test.d.ts.map +1 -0
- package/lib/plugins/StatsPlugin.d.ts.map +1 -1
- package/lib/plugins/TimerPlugin.d.ts.map +1 -1
- package/lib/plugins/WebEventsPlugin.d.ts +0 -1
- package/lib/plugins/WebEventsPlugin.d.ts.map +1 -1
- package/lib/plugins/actions/ActionsPlugin.test.d.ts +2 -0
- package/lib/plugins/actions/ActionsPlugin.test.d.ts.map +1 -0
- package/lib/plugins/audio/AudioChannel.d.ts.map +1 -1
- package/lib/plugins/audio/AudioManagerPlugin.d.ts.map +1 -1
- package/lib/plugins/audio/AudioManagerPlugin.test.d.ts +2 -0
- package/lib/plugins/audio/AudioManagerPlugin.test.d.ts.map +1 -0
- package/lib/plugins/audio/VoiceOverPlugin.d.ts.map +1 -1
- package/lib/plugins/breakpoints/BreakpointPlugin.d.ts.map +1 -1
- package/lib/plugins/captions/CaptionsPlugin.test.d.ts +2 -0
- package/lib/plugins/captions/CaptionsPlugin.test.d.ts.map +1 -0
- package/lib/plugins/focus/FocusManagerPlugin.d.ts.map +1 -1
- package/lib/plugins/focus/FocusManagerPlugin.test.d.ts +2 -0
- package/lib/plugins/focus/FocusManagerPlugin.test.d.ts.map +1 -0
- package/lib/plugins/gesture/GesturePlugin.d.ts.map +1 -1
- package/lib/plugins/input/AbstractControls.d.ts +47 -3
- package/lib/plugins/input/AbstractControls.d.ts.map +1 -1
- package/lib/plugins/input/Controls.test.d.ts +2 -0
- package/lib/plugins/input/Controls.test.d.ts.map +1 -0
- package/lib/plugins/input/InputPlugin.d.ts.map +1 -1
- package/lib/plugins/input/controlsCore.d.ts +58 -0
- package/lib/plugins/input/controlsCore.d.ts.map +1 -0
- package/lib/plugins/input/controlsCore.test.d.ts +2 -0
- package/lib/plugins/input/controlsCore.test.d.ts.map +1 -0
- package/lib/plugins/input/keyboard/KeyboardControls.d.ts +15 -0
- package/lib/plugins/input/keyboard/KeyboardControls.d.ts.map +1 -0
- package/lib/plugins/input/keyboard/index.d.ts +1 -1
- package/lib/plugins/input/keyboard/index.d.ts.map +1 -1
- package/lib/plugins/input/touch/VirtualControls.d.ts +8 -14
- package/lib/plugins/input/touch/VirtualControls.d.ts.map +1 -1
- package/lib/plugins/input/touch/VirtualControls.test.d.ts +2 -0
- package/lib/plugins/input/touch/VirtualControls.test.d.ts.map +1 -0
- package/lib/{registries-BLrxALZ0.js → registries-BfV-VF7a.js} +665 -597
- package/lib/registries-BfV-VF7a.js.map +1 -0
- package/lib/store/Store.d.ts.map +1 -1
- package/lib/ui/Button.d.ts.map +1 -1
- package/lib/ui/UICanvas.d.ts +10 -1
- package/lib/ui/UICanvas.d.ts.map +1 -1
- package/lib/ui/UICanvas.test.d.ts +0 -1
- package/lib/ui/UICanvas.test.d.ts.map +1 -1
- package/lib/utils/color.d.ts.map +1 -1
- package/lib/utils/color.test.d.ts +2 -0
- package/lib/utils/color.test.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/core/Application.test.ts +168 -0
- package/src/core/Application.ts +37 -27
- package/src/core/config.test.ts +79 -0
- package/src/core/config.ts +28 -6
- package/src/core/create.ts +11 -0
- package/src/core/interfaces/IApplication.ts +6 -6
- package/src/core/interfaces/ICoreSignals.ts +2 -0
- package/src/display/Camera.test.ts +40 -0
- package/src/display/Camera.ts +3 -3
- package/src/display/Entity.ts +2 -2
- package/src/display/SceneTransition.test.ts +58 -0
- package/src/display/SceneTransition.ts +1 -1
- package/src/display/SpineAnimation.ts +4 -4
- package/src/mixins/factory/methods.ts +0 -1
- package/src/plugins/AssetsPlugin.test.ts +48 -0
- package/src/plugins/AssetsPlugin.ts +3 -1
- package/src/plugins/DataAdapter.test.ts +32 -0
- package/src/plugins/DataAdapter.ts +5 -1
- package/src/plugins/FullScreenPlugin.test.ts +48 -0
- package/src/plugins/FullScreenPlugin.ts +9 -18
- package/src/plugins/GSAPPlugin.test.ts +31 -0
- package/src/plugins/GSAPPlugin.ts +1 -4
- package/src/plugins/KeyboardPlugin.ts +3 -10
- package/src/plugins/LookupPlugin.test.ts +42 -0
- package/src/plugins/LookupPlugin.ts +15 -3
- package/src/plugins/Plugin.test.ts +130 -2
- package/src/plugins/Plugin.ts +159 -3
- package/src/plugins/PopupManagerPlugin.test.ts +53 -0
- package/src/plugins/PopupManagerPlugin.ts +5 -3
- package/src/plugins/SceneManagerPlugin.test.ts +76 -0
- package/src/plugins/SceneManagerPlugin.ts +18 -13
- package/src/plugins/StatsPlugin.ts +3 -2
- package/src/plugins/TimerPlugin.ts +1 -2
- package/src/plugins/WebEventsPlugin.test.ts +32 -0
- package/src/plugins/WebEventsPlugin.ts +9 -16
- package/src/plugins/actions/ActionsPlugin.test.ts +68 -0
- package/src/plugins/actions/ActionsPlugin.ts +5 -5
- package/src/plugins/audio/AudioChannel.test.ts +19 -0
- package/src/plugins/audio/AudioChannel.ts +8 -1
- package/src/plugins/audio/AudioManagerPlugin.test.ts +41 -0
- package/src/plugins/audio/AudioManagerPlugin.ts +3 -2
- package/src/plugins/audio/VoiceOverPlugin.ts +1 -0
- package/src/plugins/breakpoints/BreakpointPlugin.ts +1 -2
- package/src/plugins/captions/CaptionsPlugin.test.ts +40 -0
- package/src/plugins/captions/CaptionsPlugin.ts +2 -2
- package/src/plugins/focus/FocusManagerPlugin.test.ts +71 -0
- package/src/plugins/focus/FocusManagerPlugin.ts +7 -0
- package/src/plugins/gesture/GesturePlugin.ts +4 -8
- package/src/plugins/input/AbstractControls.ts +104 -3
- package/src/plugins/input/Controls.test.ts +205 -0
- package/src/plugins/input/InputPlugin.ts +6 -14
- package/src/plugins/input/controlsCore.test.ts +151 -0
- package/src/plugins/input/controlsCore.ts +162 -0
- package/src/plugins/input/keyboard/KeyboardControls.ts +84 -0
- package/src/plugins/input/keyboard/index.ts +1 -1
- package/src/plugins/input/touch/VirtualControls.test.ts +38 -0
- package/src/plugins/input/touch/VirtualControls.ts +53 -120
- package/src/plugins/spine/SpinePlugin.ts +2 -2
- package/src/store/Store.test.ts +15 -0
- package/src/store/Store.ts +4 -1
- package/src/ui/Button.test.ts +12 -0
- package/src/ui/Button.ts +1 -0
- package/src/ui/UICanvas.test.ts +109 -6
- package/src/ui/UICanvas.ts +41 -3
- package/src/utils/color.test.ts +16 -0
- package/src/utils/color.ts +2 -1
- package/src/utils/platform.ts +1 -1
- package/src/version.ts +1 -1
- package/lib/DataAdapter-DS-twowe.js.map +0 -1
- package/lib/SpinePlugin-uN641Hmk.js.map +0 -1
- package/lib/StatsPlugin-vZQRm6wh.js.map +0 -1
- package/lib/VoiceOverPlugin-KlZdGj1G.js.map +0 -1
- package/lib/plugins/input/keyboard/KeyboardContols.d.ts +0 -28
- package/lib/plugins/input/keyboard/KeyboardContols.d.ts.map +0 -1
- package/lib/registries-BLrxALZ0.js.map +0 -1
- package/src/plugins/input/keyboard/KeyboardContols.ts +0 -182
package/build/assetpack.mjs
CHANGED
|
@@ -33,6 +33,92 @@ function fontWeights() {
|
|
|
33
33
|
};
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
/**
|
|
37
|
+
* Pipes an exempted asset must not enter. AssetPack's `mergePipeOptions` reads
|
|
38
|
+
* `asset.settings[pipe.name]` and treats `false` as "skip this pipe for this
|
|
39
|
+
* asset" — the same per-asset switch the `assetSettings` config exposes, only
|
|
40
|
+
* set programmatically here because the page names are inside the font file,
|
|
41
|
+
* not in anything an app author could glob.
|
|
42
|
+
*
|
|
43
|
+
* `cache-buster` is the one that actually breaks production, and it is also the
|
|
44
|
+
* one no tag can turn off (`cacheBuster().test` is `!asset.isFolder`, full
|
|
45
|
+
* stop), which is why this uses settings rather than the `nomip`/`nc` tags.
|
|
46
|
+
*/
|
|
47
|
+
const BITMAP_FONT_SKIPPED_PIPES = ['mipmap', 'compress', 'cache-buster'];
|
|
48
|
+
|
|
49
|
+
/** Both text `.fnt` and XML bitmap fonts name their pages as `file="…png"`. */
|
|
50
|
+
const fontPagePattern = /file="([^"]+\.png)"/g;
|
|
51
|
+
|
|
52
|
+
const toPosix = (value) => value.replace(/\\/g, '/');
|
|
53
|
+
const dirOf = (value) => toPosix(value).replace(/\/[^/]*$/, '');
|
|
54
|
+
const baseOf = (value) => toPosix(value).replace(/^.*\//, '');
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* AssetPack pipe that keeps pre-rasterized bitmap fonts out of the image
|
|
58
|
+
* pipeline entirely — no resolution variants, no webp conversion, no
|
|
59
|
+
* cache-bust rename — for the descriptor and every texture page it names.
|
|
60
|
+
*
|
|
61
|
+
* A `.fnt` hardcodes `page id=0 file="Syncopate.png"` and PixiJS fetches that
|
|
62
|
+
* exact name relative to the descriptor's own URL; the manifest is never
|
|
63
|
+
* consulted for it. So the moment assetpack hashes the page to
|
|
64
|
+
* `Syncopate-MPx5Vw.webp`, the loader asks for a file that no longer exists,
|
|
65
|
+
* gets vite's SPA fallback HTML back, and the app fails to boot. Resizing is
|
|
66
|
+
* just as fatal in the other direction: the glyph rects in the descriptor are
|
|
67
|
+
* exact pixel coordinates into that one image.
|
|
68
|
+
*
|
|
69
|
+
* Detection is automatic — no folder tagging asked of app authors. `start` runs
|
|
70
|
+
* against the whole asset tree before any transform, which is the only point
|
|
71
|
+
* where a font's descriptor can be read in time to exempt its siblings.
|
|
72
|
+
*/
|
|
73
|
+
export function bitmapFontPassthrough() {
|
|
74
|
+
return {
|
|
75
|
+
folder: false,
|
|
76
|
+
name: 'bitmap-font-passthrough',
|
|
77
|
+
defaultOptions: null,
|
|
78
|
+
async start(rootAsset) {
|
|
79
|
+
const files = new Map();
|
|
80
|
+
const descriptors = [];
|
|
81
|
+
|
|
82
|
+
const walk = (asset) => {
|
|
83
|
+
if (!asset.isFolder) {
|
|
84
|
+
files.set(toPosix(asset.path), asset);
|
|
85
|
+
if (/\.(fnt|xml)$/i.test(asset.path)) descriptors.push(asset);
|
|
86
|
+
}
|
|
87
|
+
for (const child of asset.children ?? []) walk(child);
|
|
88
|
+
};
|
|
89
|
+
walk(rootAsset);
|
|
90
|
+
|
|
91
|
+
const exempt = (asset) => {
|
|
92
|
+
asset.settings = { ...asset.settings };
|
|
93
|
+
for (const pipe of BITMAP_FONT_SKIPPED_PIPES) asset.settings[pipe] = false;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
for (const descriptor of descriptors) {
|
|
97
|
+
let text;
|
|
98
|
+
try {
|
|
99
|
+
// A deleted asset is still on the tree, and a binary .fnt decodes to
|
|
100
|
+
// noise — either way the page match below simply finds nothing.
|
|
101
|
+
text = descriptor.buffer.toString('utf8');
|
|
102
|
+
} catch {
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const pages = [...text.matchAll(fontPagePattern)].map((match) => match[1]);
|
|
107
|
+
// No page reference means it is not a bitmap font — an ordinary .xml
|
|
108
|
+
// data file must keep its cache-bust hash.
|
|
109
|
+
if (!pages.length) continue;
|
|
110
|
+
|
|
111
|
+
exempt(descriptor);
|
|
112
|
+
const directory = dirOf(descriptor.path);
|
|
113
|
+
for (const page of pages) {
|
|
114
|
+
const sibling = files.get(`${directory}/${baseOf(page)}`);
|
|
115
|
+
if (sibling) exempt(sibling);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
36
122
|
const defaultManifestUrl = 'assets.json';
|
|
37
123
|
|
|
38
124
|
const defaultPixiPipesConfig = {
|
|
@@ -185,6 +271,10 @@ export const assetpackConfig = (manifestUrl = defaultManifestUrl, pixiPipesConfi
|
|
|
185
271
|
const wfIdx = pipes.findIndex((p) => p.name === 'webfont');
|
|
186
272
|
pipes.splice(wfIdx >= 0 ? wfIdx : 0, 0, fontWeights());
|
|
187
273
|
|
|
274
|
+
// Position is irrelevant — this one only has a `start` hook, and AssetPack
|
|
275
|
+
// runs every pipe's `start` before the first asset is transformed.
|
|
276
|
+
pipes.unshift(bitmapFontPassthrough());
|
|
277
|
+
|
|
188
278
|
return {
|
|
189
279
|
manifestUrl,
|
|
190
280
|
entry: './assets',
|
package/build/index.mjs
CHANGED
|
@@ -76,7 +76,9 @@ function caperPluginList({ assets = {}, pwa } = {}) {
|
|
|
76
76
|
entityListPlugin(),
|
|
77
77
|
uiListPlugin(),
|
|
78
78
|
...assetPlugins,
|
|
79
|
-
|
|
79
|
+
// The manifest name reaches build-time validation this way — it is the only
|
|
80
|
+
// place that knows a project overrode it.
|
|
81
|
+
caperConfigPlugin(true, manifestUrl),
|
|
80
82
|
caperDevHelperPlugin(),
|
|
81
83
|
...(pwa ? caperPwaPlugins(pwa) : []),
|
|
82
84
|
];
|
|
@@ -37,7 +37,7 @@ export async function findTypeScriptFiles(dir) {
|
|
|
37
37
|
|
|
38
38
|
if (entry.isDirectory()) {
|
|
39
39
|
await scan(fullPath);
|
|
40
|
-
} else if (entry.isFile() && /\.
|
|
40
|
+
} else if (entry.isFile() && /\.tsx?$/.test(entry.name)) {
|
|
41
41
|
files.push(fullPath);
|
|
42
42
|
}
|
|
43
43
|
}
|
|
@@ -8,10 +8,15 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import fs from 'node:fs';
|
|
10
10
|
import path from 'node:path';
|
|
11
|
-
import {
|
|
11
|
+
import { logger } from './util.mjs';
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
/**
|
|
14
|
+
* `root` is vite's resolved project root — not `process.cwd()`, which is
|
|
15
|
+
* whatever directory the build was launched from. See `internal/discovery.mjs`
|
|
16
|
+
* for the same contract.
|
|
17
|
+
*/
|
|
18
|
+
export function loadManifestBundleNames(root, manifestUrl = 'assets.json') {
|
|
19
|
+
const manifestPath = path.join(root, 'public', 'assets', manifestUrl);
|
|
15
20
|
if (!fs.existsSync(manifestPath)) return null;
|
|
16
21
|
try {
|
|
17
22
|
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
@@ -12,6 +12,8 @@ import { logger } from './util.mjs';
|
|
|
12
12
|
|
|
13
13
|
export function runBuildTimeValidation({
|
|
14
14
|
server,
|
|
15
|
+
root,
|
|
16
|
+
manifestUrl,
|
|
15
17
|
configPath,
|
|
16
18
|
configObject,
|
|
17
19
|
scenes,
|
|
@@ -21,7 +23,7 @@ export function runBuildTimeValidation({
|
|
|
21
23
|
breakpointsName,
|
|
22
24
|
}) {
|
|
23
25
|
const warnings = [];
|
|
24
|
-
const bundleNames = loadManifestBundleNames();
|
|
26
|
+
const bundleNames = loadManifestBundleNames(root, manifestUrl);
|
|
25
27
|
|
|
26
28
|
// 1. Scene bundle references
|
|
27
29
|
if (bundleNames && bundleNames.size > 0) {
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import fs from 'node:fs';
|
|
10
10
|
import path from 'node:path';
|
|
11
11
|
import { loadManifestBundleNames } from '../internal/manifest.mjs';
|
|
12
|
-
import { ASSET_DTS_FILE_NAME, debounce, delay, logger } from '../internal/util.mjs';
|
|
12
|
+
import { ASSET_DTS_FILE_NAME, debounce, delay, env, logger } from '../internal/util.mjs';
|
|
13
13
|
|
|
14
14
|
export { loadManifestBundleNames };
|
|
15
15
|
|
|
@@ -399,12 +399,12 @@ export function assetTypesPlugin(manifestUrl = 'assets.json') {
|
|
|
399
399
|
|
|
400
400
|
return {
|
|
401
401
|
name: 'vite-plugin-asset-types',
|
|
402
|
-
config(config) {
|
|
403
|
-
ispPwaEnabled = config.plugins.some((p) => p.name === 'vite-plugin-pwa');
|
|
404
|
-
},
|
|
405
402
|
configResolved(config) {
|
|
406
403
|
publicDir = config.publicDir;
|
|
407
404
|
root = config.root;
|
|
405
|
+
// Resolved, so the list is flat: in the `config` hook `caper()` is still
|
|
406
|
+
// one nested array and vite-plugin-pwa is never found at the top level.
|
|
407
|
+
ispPwaEnabled = config.plugins.some((p) => p.name === 'vite-plugin-pwa');
|
|
408
408
|
},
|
|
409
409
|
async buildStart() {
|
|
410
410
|
// a short delay to allow assetpack to generate the manifest
|
|
@@ -93,7 +93,7 @@ async function validateCaperConfig(server, root) {
|
|
|
93
93
|
|
|
94
94
|
// write a debounce function
|
|
95
95
|
|
|
96
|
-
export function caperConfigPlugin(isProject = true) {
|
|
96
|
+
export function caperConfigPlugin(isProject = true, manifestUrl = 'assets.json') {
|
|
97
97
|
// vite's resolved project root. Everything below resolves against this rather
|
|
98
98
|
// than process.cwd(), so `vite --root elsewhere` and monorepo invocations from
|
|
99
99
|
// a parent directory find the right caper.config.ts and src/ tree.
|
|
@@ -228,6 +228,8 @@ export function caperConfigPlugin(isProject = true) {
|
|
|
228
228
|
// defaultScene pointing at a non-existent scene, duplicate IDs).
|
|
229
229
|
runBuildTimeValidation({
|
|
230
230
|
server,
|
|
231
|
+
root,
|
|
232
|
+
manifestUrl,
|
|
231
233
|
configPath,
|
|
232
234
|
configObject,
|
|
233
235
|
scenes,
|
|
@@ -444,6 +446,7 @@ declare module '@caperjs/core' {
|
|
|
444
446
|
const pluginsDir = path.resolve(root, 'src/plugins');
|
|
445
447
|
const popupsDir = path.resolve(root, 'src/popups');
|
|
446
448
|
const entitiesDir = path.resolve(root, 'src/entities');
|
|
449
|
+
const uisDir = path.resolve(root, 'src/ui');
|
|
447
450
|
const localesDir = path.resolve(root, 'src/locales');
|
|
448
451
|
|
|
449
452
|
const handleFileChange = async (file) => {
|
|
@@ -451,10 +454,11 @@ declare module '@caperjs/core' {
|
|
|
451
454
|
const isPlugin = file.startsWith(pluginsDir);
|
|
452
455
|
const isPopup = file.startsWith(popupsDir);
|
|
453
456
|
const isEntity = file.startsWith(entitiesDir);
|
|
457
|
+
const isUI = file.startsWith(uisDir);
|
|
454
458
|
const isLocale = file.startsWith(localesDir);
|
|
455
459
|
const isConfig = file === configPath;
|
|
456
460
|
|
|
457
|
-
if (!isScene && !isPlugin && !isPopup && !isEntity && !isLocale && !isConfig) return;
|
|
461
|
+
if (!isScene && !isPlugin && !isPopup && !isEntity && !isUI && !isLocale && !isConfig) return;
|
|
458
462
|
|
|
459
463
|
const msg = isScene
|
|
460
464
|
? 'Scene file changed'
|
|
@@ -464,9 +468,11 @@ declare module '@caperjs/core' {
|
|
|
464
468
|
? 'Popup file changed'
|
|
465
469
|
: isEntity
|
|
466
470
|
? 'Entity file changed'
|
|
467
|
-
:
|
|
468
|
-
? '
|
|
469
|
-
:
|
|
471
|
+
: isUI
|
|
472
|
+
? 'UI file changed'
|
|
473
|
+
: isLocale
|
|
474
|
+
? 'Locale file changed'
|
|
475
|
+
: 'Config file changed';
|
|
470
476
|
await build(`${msg}, regenerating types...`, server);
|
|
471
477
|
};
|
|
472
478
|
|
|
@@ -475,6 +481,7 @@ declare module '@caperjs/core' {
|
|
|
475
481
|
server.watcher.add(pluginsDir);
|
|
476
482
|
server.watcher.add(popupsDir);
|
|
477
483
|
server.watcher.add(entitiesDir);
|
|
484
|
+
server.watcher.add(uisDir);
|
|
478
485
|
server.watcher.add(localesDir);
|
|
479
486
|
|
|
480
487
|
server.watcher.on('change', handleFileChange);
|
package/build/plugins/lists.mjs
CHANGED
|
@@ -9,14 +9,11 @@
|
|
|
9
9
|
import path from 'node:path';
|
|
10
10
|
import {
|
|
11
11
|
discoverEntities,
|
|
12
|
-
discoverLocaleKeys,
|
|
13
12
|
discoverPlugins,
|
|
14
13
|
discoverPopups,
|
|
15
14
|
discoverScenes,
|
|
16
15
|
discoverUIs,
|
|
17
16
|
} from '../internal/discovery.mjs';
|
|
18
|
-
import { logger } from '../internal/util.mjs';
|
|
19
|
-
import { loadManifestBundleNames } from '../internal/manifest.mjs';
|
|
20
17
|
|
|
21
18
|
export function createClassListPlugin({ virtualModuleId, discoverFn, exportName, pluginName }) {
|
|
22
19
|
// vite's resolved project root, captured for the discovery pass. See
|
|
@@ -32,7 +29,15 @@ export function createClassListPlugin({ virtualModuleId, discoverFn, exportName,
|
|
|
32
29
|
|
|
33
30
|
const generate = (items) => {
|
|
34
31
|
const staticItems = items.filter((i) => i.module && !i.module.isFunction);
|
|
35
|
-
|
|
32
|
+
// Basenames alone collide when two files share a name in different
|
|
33
|
+
// directories (e.g. src/entities/a/Foo.ts and src/entities/b/Foo.ts),
|
|
34
|
+
// producing two `import Foo from '...'` lines — a duplicate-binding
|
|
35
|
+
// syntax error. Suffix with the item's index in the static-import list
|
|
36
|
+
// to keep identifiers unique while staying stable and readable, and
|
|
37
|
+
// look them up from the same map when emitting the object literal so
|
|
38
|
+
// the import and its `module:` reference always agree.
|
|
39
|
+
const importNames = new Map(staticItems.map((i, index) => [i, `${extractClassName(i)}_${index}`]));
|
|
40
|
+
const imports = staticItems.map((i) => `import ${importNames.get(i)} from '${i.module}';`);
|
|
36
41
|
|
|
37
42
|
return `
|
|
38
43
|
${imports.join('\n')}
|
|
@@ -41,7 +46,7 @@ export function createClassListPlugin({ virtualModuleId, discoverFn, exportName,
|
|
|
41
46
|
.map((item) => {
|
|
42
47
|
const moduleExpr =
|
|
43
48
|
item.module && !item.module.isFunction
|
|
44
|
-
?
|
|
49
|
+
? importNames.get(item)
|
|
45
50
|
: (item.module?.toString?.() ?? `() => import('${item.importPath}')`);
|
|
46
51
|
return `{
|
|
47
52
|
id: '${item.id}',
|
|
@@ -157,7 +162,15 @@ export function pluginListPlugin(isProject = true) {
|
|
|
157
162
|
// npm static imports would collide on class-name derivation from the pkg
|
|
158
163
|
// path, so only local plugins support `dynamic = false`. npm packages are
|
|
159
164
|
// always dynamic.
|
|
160
|
-
|
|
165
|
+
//
|
|
166
|
+
// Basenames alone collide when two files share a name in different
|
|
167
|
+
// directories, producing two `import Foo from '...'` lines — a
|
|
168
|
+
// duplicate-binding syntax error. Suffix with the item's index in the
|
|
169
|
+
// static-import list to keep identifiers unique while staying stable
|
|
170
|
+
// and readable, and look them up from the same map when emitting the
|
|
171
|
+
// object literal so the import and its `module:` reference always agree.
|
|
172
|
+
const importNames = new Map(staticPlugins.map((p, index) => [p, `${extractClassName(p)}_${index}`]));
|
|
173
|
+
const imports = staticPlugins.map((p) => `import ${importNames.get(p)} from '${p.module}';`);
|
|
161
174
|
|
|
162
175
|
return `
|
|
163
176
|
${imports.join('\n')}
|
|
@@ -166,7 +179,7 @@ export function pluginListPlugin(isProject = true) {
|
|
|
166
179
|
.map((plugin) => {
|
|
167
180
|
const moduleExpr =
|
|
168
181
|
plugin.module && !plugin.module.isFunction && plugin.isLocal
|
|
169
|
-
?
|
|
182
|
+
? importNames.get(plugin)
|
|
170
183
|
: (plugin.module?.toString?.() ?? `() => import('${plugin.importPath}')`);
|
|
171
184
|
return `{
|
|
172
185
|
name: '${plugin.name}',
|
|
@@ -223,7 +236,14 @@ export function sceneListPlugin(isProject = true) {
|
|
|
223
236
|
// extract non function scenes from the list
|
|
224
237
|
const nonFunctionScenes = scenes.filter((scene) => !scene.module.isFunction);
|
|
225
238
|
|
|
226
|
-
|
|
239
|
+
// Basenames alone collide when two files share a name in different
|
|
240
|
+
// directories, producing two `import Foo from '...'` lines — a
|
|
241
|
+
// duplicate-binding syntax error. Suffix with the item's index in the
|
|
242
|
+
// static-import list to keep identifiers unique while staying stable
|
|
243
|
+
// and readable, and look them up from the same map when emitting the
|
|
244
|
+
// object literal so the import and its `module:` reference always agree.
|
|
245
|
+
const importNames = new Map(nonFunctionScenes.map((scene, index) => [scene, `${extractClassName(scene)}_${index}`]));
|
|
246
|
+
const imports = nonFunctionScenes.map((scene) => `import ${importNames.get(scene)} from '${scene.module}';`);
|
|
227
247
|
|
|
228
248
|
const result = `
|
|
229
249
|
${imports.join('\n')}
|
|
@@ -233,7 +253,7 @@ export function sceneListPlugin(isProject = true) {
|
|
|
233
253
|
(scene) => `{
|
|
234
254
|
id: '${scene.id}',
|
|
235
255
|
active: ${scene.active},
|
|
236
|
-
module: ${scene.module.isFunction ? scene.module.toString() :
|
|
256
|
+
module: ${scene.module.isFunction ? scene.module.toString() : importNames.get(scene)},
|
|
237
257
|
debugLabel: ${JSON.stringify(scene.debugLabel)},
|
|
238
258
|
debugGroup: ${JSON.stringify(scene.debugGroup)},
|
|
239
259
|
debugOrder: ${scene.debugOrder},
|
|
@@ -28,6 +28,17 @@ import path from 'node:path';
|
|
|
28
28
|
/** A png image, a texture-packer descriptor, or a spine atlas for one. */
|
|
29
29
|
const isPngSrc = (src) => /\.png(\.json|\.atlas)?$/.test(src);
|
|
30
30
|
|
|
31
|
+
/** A bitmap font descriptor — text `.fnt` or XML `.fnt`/`.xml`, both quote `file="…"`. */
|
|
32
|
+
const isFontSrc = (src) => /\.(fnt|xml)$/.test(src);
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* PixiJS bitmap fonts hardcode their texture page filename inside the
|
|
36
|
+
* descriptor and fetch it relative to the descriptor's own URL — the asset
|
|
37
|
+
* manifest is never consulted for that request, so rewriting `src` there
|
|
38
|
+
* doesn't stop the loader asking for the exact name printed here.
|
|
39
|
+
*/
|
|
40
|
+
const fontPageNames = (text) => new Set([...text.matchAll(/file="([^"]+\.png)"/g)].map((match) => path.basename(match[1])));
|
|
41
|
+
|
|
31
42
|
/**
|
|
32
43
|
* Works out what to delete without touching the filesystem, so the decision is
|
|
33
44
|
* testable on its own.
|
|
@@ -77,10 +88,22 @@ export function planPngPrune(manifest, readText) {
|
|
|
77
88
|
doomed.push(rel);
|
|
78
89
|
};
|
|
79
90
|
|
|
91
|
+
const protectedPngs = new Set();
|
|
92
|
+
for (const bundle of manifest.bundles ?? []) {
|
|
93
|
+
for (const asset of bundle.assets ?? []) {
|
|
94
|
+
for (const src of asset.src ?? []) {
|
|
95
|
+
if (!isFontSrc(src)) continue;
|
|
96
|
+
const text = readText(src);
|
|
97
|
+
if (text) for (const name of fontPageNames(text)) protectedPngs.add(name);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
80
102
|
for (const bundle of manifest.bundles ?? []) {
|
|
81
103
|
for (const asset of bundle.assets ?? []) {
|
|
82
|
-
const
|
|
83
|
-
const
|
|
104
|
+
const isProtected = (src) => isPngSrc(src) && protectedPngs.has(path.basename(src));
|
|
105
|
+
const png = (asset.src ?? []).filter((src) => isPngSrc(src) && !isProtected(src));
|
|
106
|
+
const kept = (asset.src ?? []).filter((src) => !isPngSrc(src) || isProtected(src));
|
|
84
107
|
if (!png.length || !kept.length) continue;
|
|
85
108
|
|
|
86
109
|
for (const rel of png) {
|
package/cli/create.mjs
CHANGED
|
@@ -88,6 +88,60 @@ function getFiles(dirPath) {
|
|
|
88
88
|
return files;
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
+
/**
|
|
92
|
+
* Injects wizard-selected plugin config entries into a caper.config.ts file's
|
|
93
|
+
* `plugins:` array, whether that array is currently empty or already has entries.
|
|
94
|
+
*
|
|
95
|
+
* @param {string} configContents
|
|
96
|
+
* @param {string[]} pluginConfigs
|
|
97
|
+
* @returns {string}
|
|
98
|
+
*/
|
|
99
|
+
export function injectPluginsIntoConfig(configContents, pluginConfigs) {
|
|
100
|
+
// Empty array: replace it wholesale with the new entries.
|
|
101
|
+
if (/plugins:\s*\[\s*\]/.test(configContents)) {
|
|
102
|
+
return configContents.replace(
|
|
103
|
+
/plugins:\s*\[\s*\]/,
|
|
104
|
+
`plugins: [\n ${pluginConfigs.join(',\n ')}\n ]`,
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Non-empty array: find the `plugins:` array's matching brackets (which may
|
|
109
|
+
// contain nested arrays, e.g. `['name', {...}]` entries) and append the new
|
|
110
|
+
// entries alongside the existing ones.
|
|
111
|
+
const keyMatch = configContents.match(/plugins:\s*\[/);
|
|
112
|
+
if (!keyMatch) {
|
|
113
|
+
return configContents;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const openBracketIndex = keyMatch.index + keyMatch[0].length - 1;
|
|
117
|
+
let depth = 0;
|
|
118
|
+
let closeBracketIndex = -1;
|
|
119
|
+
for (let i = openBracketIndex; i < configContents.length; i++) {
|
|
120
|
+
if (configContents[i] === '[') depth++;
|
|
121
|
+
else if (configContents[i] === ']') {
|
|
122
|
+
depth--;
|
|
123
|
+
if (depth === 0) {
|
|
124
|
+
closeBracketIndex = i;
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
if (closeBracketIndex === -1) {
|
|
130
|
+
return configContents;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const existingEntries = configContents.slice(openBracketIndex + 1, closeBracketIndex).trim();
|
|
134
|
+
const allEntries = existingEntries
|
|
135
|
+
? `${existingEntries},\n ${pluginConfigs.join(',\n ')}`
|
|
136
|
+
: pluginConfigs.join(',\n ');
|
|
137
|
+
|
|
138
|
+
return (
|
|
139
|
+
configContents.slice(0, openBracketIndex) +
|
|
140
|
+
`[\n ${allEntries}\n ]` +
|
|
141
|
+
configContents.slice(closeBracketIndex + 1)
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
91
145
|
/**
|
|
92
146
|
* @param {string} template
|
|
93
147
|
* @param {string} applicationNameForPkg
|
|
@@ -180,11 +234,8 @@ function write_template_files(cwd, template, applicationNameForPkg, applicationN
|
|
|
180
234
|
return `['${shortName}', { autoLoad: false }]`;
|
|
181
235
|
});
|
|
182
236
|
|
|
183
|
-
//
|
|
184
|
-
configContents = configContents
|
|
185
|
-
/plugins:\s*\[\s*\]/,
|
|
186
|
-
`plugins: [\n ${pluginConfigs.join(',\n ')}\n ]`,
|
|
187
|
-
);
|
|
237
|
+
// Inject the configured plugins into the plugins array
|
|
238
|
+
configContents = injectPluginsIntoConfig(configContents, pluginConfigs);
|
|
188
239
|
}
|
|
189
240
|
|
|
190
241
|
fs.writeFileSync(configPath, configContents.replace(/__APPLICATION_NAME__/g, applicationName), 'utf-8');
|
|
@@ -304,7 +355,7 @@ export async function create(cwd = '.', packageManagerOverride) {
|
|
|
304
355
|
});
|
|
305
356
|
|
|
306
357
|
if (!appName) {
|
|
307
|
-
appName =
|
|
358
|
+
appName = defaultName;
|
|
308
359
|
}
|
|
309
360
|
|
|
310
361
|
// derive an application name from the project name by camel casing it
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { injectPluginsIntoConfig } from './create.mjs';
|
|
3
|
+
|
|
4
|
+
describe('injectPluginsIntoConfig', () => {
|
|
5
|
+
it('inserts plugins into an empty array', () => {
|
|
6
|
+
const configContents = `export default {\n plugins: [],\n};\n`;
|
|
7
|
+
const pluginConfigs = [`['rollbar', { autoLoad: false }]`];
|
|
8
|
+
|
|
9
|
+
const result = injectPluginsIntoConfig(configContents, pluginConfigs);
|
|
10
|
+
|
|
11
|
+
expect(result).toContain(`plugins: [\n ['rollbar', { autoLoad: false }]\n ]`);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('appends plugins alongside an existing non-empty array', () => {
|
|
15
|
+
const configContents = `export default {\n plugins: [['existing', { autoLoad: false }]],\n};\n`;
|
|
16
|
+
const pluginConfigs = [`['rollbar', { autoLoad: false }]`];
|
|
17
|
+
|
|
18
|
+
const result = injectPluginsIntoConfig(configContents, pluginConfigs);
|
|
19
|
+
|
|
20
|
+
expect(result).toContain(`['existing', { autoLoad: false }]`);
|
|
21
|
+
expect(result).toContain(`['rollbar', { autoLoad: false }]`);
|
|
22
|
+
// both entries should end up inside a single well-formed array
|
|
23
|
+
expect(result).toMatch(/plugins: \[[\s\S]*existing[\s\S]*rollbar[\s\S]*\]/);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('appends plugins alongside an existing multiline array', () => {
|
|
27
|
+
const configContents = `export default {\n plugins: [\n ['existing', { autoLoad: false }],\n ],\n};\n`;
|
|
28
|
+
const pluginConfigs = [`['rollbar', { autoLoad: false }]`, `['rive', { autoLoad: false }]`];
|
|
29
|
+
|
|
30
|
+
const result = injectPluginsIntoConfig(configContents, pluginConfigs);
|
|
31
|
+
|
|
32
|
+
expect(result).toContain(`['existing', { autoLoad: false }]`);
|
|
33
|
+
expect(result).toContain(`['rollbar', { autoLoad: false }]`);
|
|
34
|
+
expect(result).toContain(`['rive', { autoLoad: false }]`);
|
|
35
|
+
});
|
|
36
|
+
});
|
package/cli/update.mjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {execSync} from 'node:child_process';
|
|
3
3
|
import {package_manager} from './utils.mjs';
|
|
4
4
|
|
|
5
|
-
const install_path = '
|
|
5
|
+
const install_path = '@caperjs/core@latest';
|
|
6
6
|
|
|
7
7
|
export async function update() {
|
|
8
8
|
return execSync(`${package_manager} install ${install_path}`);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Dt as e, H as t, bt as n, f as r, jt as i } from "./registries-
|
|
2
|
-
import {
|
|
1
|
+
import { Dt as e, H as t, bt as n, f as r, jt as i } from "./registries-BfV-VF7a.js";
|
|
2
|
+
import { t as a } from "./padding-1kHDpGlQ.js";
|
|
3
3
|
import { Assets as o, Container as s, Texture as c } from "pixi.js";
|
|
4
4
|
import { gsap as l } from "gsap";
|
|
5
5
|
//#region src/plugins/captions/CaptionsRenderer.ts
|
|
@@ -194,13 +194,13 @@ var m = class extends t {
|
|
|
194
194
|
postInitialize() {
|
|
195
195
|
this.app.i18n.onLocaleChanged.connect(this._handleLocaleChange), this.app.voiceover.onVoiceOverStart.connect(this._handleVoiceOverStart), this.app.voiceover.onVoiceOverPaused.connect(this._handleVoiceOverPaused), this.app.voiceover.onVoiceOverResumed.connect(this._handleVoiceOverResumed), this.app.voiceover.onVoiceOverComplete.connect(this._handleVoiceoverComplete), this.app.voiceover.onVoiceOverStopped.connect(this._handleVoiceoverStopped), this.app.stage.addChild(this.view), this._options.maxWidth = p(this._originalOptions.maxWidth, this.app.size.width);
|
|
196
196
|
let e = this.options.renderer;
|
|
197
|
-
this.renderer = this.view.addChild(new e(this)), this.
|
|
197
|
+
this.renderer = this.view.addChild(new e(this)), this.addTickerCallback(this.update), this.app.scenes.onSceneChangeStart.connect(this.stopAllCaptions);
|
|
198
198
|
}
|
|
199
199
|
async setLocale(e) {
|
|
200
200
|
return this._locale === e ? this._locale : (this._activeCaptionId && this.stopCaption(this._activeCaptionId), this._activeCaptionId = void 0, await this._loadAndSetLocale(e), this._locale = e, this._locale);
|
|
201
201
|
}
|
|
202
202
|
async loadLocale(t) {
|
|
203
|
-
if (this.
|
|
203
|
+
if (this._dicts[t]) return;
|
|
204
204
|
let r = this._options.files.find((e) => t === e.id);
|
|
205
205
|
if (!r) {
|
|
206
206
|
e.error(`i18n:: Could not find locale file for ${t}`);
|
|
@@ -275,4 +275,4 @@ var m = class extends t {
|
|
|
275
275
|
//#endregion
|
|
276
276
|
export { m as CaptionsPlugin };
|
|
277
277
|
|
|
278
|
-
//# sourceMappingURL=CaptionsPlugin-
|
|
278
|
+
//# sourceMappingURL=CaptionsPlugin-cSJj-R9M.js.map
|