@geastack/core 0.1.41 → 0.1.43

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.
@@ -11,6 +11,7 @@ import { execFileSync } from "node:child_process";
11
11
  import { createHash } from "node:crypto";
12
12
  import { createRequire } from "node:module";
13
13
  import { fileURLToPath } from "node:url";
14
+ import { fileAssetBundle, fileAssetLoader } from "./gea-file-asset-bundle.mjs";
14
15
 
15
16
  const args = process.argv.slice(2);
16
17
 
@@ -1060,6 +1061,7 @@ function generatedCpp(fonts, families) {
1060
1061
  // this TU too (a weak symbol can't be defined twice in one TU); `font_data`
1061
1062
  // points at the shared symbol either way.
1062
1063
  const atlasSymbols = new Map();
1064
+ const atlasEntries = [];
1063
1065
  for (const font of fonts) {
1064
1066
  const hash = createHash("sha1")
1065
1067
  .update(Buffer.from(font.atlasData))
@@ -1069,14 +1071,23 @@ function generatedCpp(fonts, families) {
1069
1071
  font.atlasSymbol = symbol;
1070
1072
  if (atlasSymbols.has(hash)) continue;
1071
1073
  atlasSymbols.set(hash, symbol);
1074
+ atlasEntries.push({ symbol, data: font.atlasData });
1075
+ }
1076
+ const bundle = fileAssetBundle(appDir, outCpp, 'fonts', atlasEntries);
1077
+ if (bundle) lines.unshift('#include "platform/asset_bundle.h"');
1078
+ for (const { symbol, data } of atlasEntries) {
1079
+ if (bundle) {
1080
+ lines.push(`__attribute__((weak)) GEA_FILE_ASSET_BUFFER std::uint8_t ${symbol}[${Math.max(1, data.length)}];`);
1081
+ continue;
1082
+ }
1072
1083
  // `extern` + initializer: a namespace-scope `const` is internal by default,
1073
1084
  // and `weak` requires external linkage ("weak declaration must be public").
1074
1085
  lines.push(
1075
- `extern __attribute__((weak)) const std::uint8_t ${symbol}[${font.atlasData.length}] = {`,
1086
+ `extern __attribute__((weak)) const std::uint8_t ${symbol}[${data.length}] = {`,
1076
1087
  );
1077
- for (let i = 0; i < font.atlasData.length; i += 32) {
1088
+ for (let i = 0; i < data.length; i += 32) {
1078
1089
  const chunk = Array.from(
1079
- font.atlasData.slice(i, Math.min(i + 32, font.atlasData.length)),
1090
+ data.slice(i, Math.min(i + 32, data.length)),
1080
1091
  );
1081
1092
  lines.push(
1082
1093
  ` ${chunk.map((byte) => `0x${byte.toString(16).padStart(2, "0")}`).join(", ")},`,
@@ -1085,6 +1096,7 @@ function generatedCpp(fonts, families) {
1085
1096
  lines.push("};");
1086
1097
  lines.push("");
1087
1098
  }
1099
+ if (bundle) lines.push(...fileAssetLoader(bundle, 'prepareFontAssets'));
1088
1100
 
1089
1101
  for (const font of fonts) {
1090
1102
  // `atlasBits` defaults to 8 in RasterizedFontData, so an 8-bit atlas leaves
@@ -1144,12 +1156,14 @@ function generatedCpp(fonts, families) {
1144
1156
  lines.push("");
1145
1157
  lines.push(`void ${names.ensureLinked}()`);
1146
1158
  lines.push("{");
1159
+ if (bundle) lines.push(' (void)prepareFontAssets();');
1147
1160
  lines.push("}");
1148
1161
  lines.push("");
1149
1162
  lines.push(
1150
1163
  `const gea::framework::graphics::RasterizedFontData *${names.lookupFont}(int fontId)`,
1151
1164
  );
1152
1165
  lines.push("{");
1166
+ if (bundle) lines.push(' if (!prepareFontAssets()) return nullptr;');
1153
1167
  lines.push(" for (const auto &entry : generated_fonts) {");
1154
1168
  lines.push(
1155
1169
  " if (entry.font && entry.font->id == fontId) return entry.font;",
@@ -1162,6 +1176,7 @@ function generatedCpp(fonts, families) {
1162
1176
  `const gea::framework::graphics::RasterizedFontData *${names.lookupFontForFamily}(int familyId, int sizePx)`,
1163
1177
  );
1164
1178
  lines.push("{");
1179
+ if (bundle) lines.push(' if (!prepareFontAssets()) return nullptr;');
1165
1180
  lines.push(" const RasterizedFontData *best = nullptr;");
1166
1181
  lines.push(" int bestDiff = 2147483647;");
1167
1182
  lines.push(" for (const auto &entry : generated_fonts) {");
@@ -1510,7 +1525,13 @@ function writeFileIfChanged(filePath, contents) {
1510
1525
  function writeGeneratedFonts() {
1511
1526
  const fontFaces = collectSourceFontFaces();
1512
1527
  const tuples = collectUsedFontTuples(fontFaces);
1513
- const familyNames = [...new Set(tuples.map((tuple) => tuple.family))].sort();
1528
+ // Runtime fonts are selected at actual layout time, including CSS variables
1529
+ // and programmatic styles with no statically discoverable family/size tuple.
1530
+ // Keep every explicitly declared local face; raster-only builds retain their
1531
+ // existing tuple-pruned size/charset policy.
1532
+ const familyNames = runtimeTtfFonts
1533
+ ? [...fontFaces.keys()].sort()
1534
+ : [...new Set(tuples.map((tuple) => tuple.family))].sort();
1514
1535
  const families = familyNames.map((name, id) => ({ id, name }));
1515
1536
  const familyIds = new Map(families.map((family) => [family.name, family.id]));
1516
1537
 
@@ -0,0 +1,46 @@
1
+ import test from 'node:test'
2
+ import assert from 'node:assert/strict'
3
+ import fs from 'node:fs'
4
+ import os from 'node:os'
5
+ import path from 'node:path'
6
+ import { resolveAppGeaVitePlugin } from '../gea-vite-plugin-resolution.mjs'
7
+
8
+ test('declared app plugin honors its real package/version and symlink; undeclared alone inherits Core', () => {
9
+ const root = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'gea-plugin-resolution-')))
10
+ try {
11
+ const app = path.join(root,'app'), core = path.join(root,'core')
12
+ for (const p of [app,core]) { fs.mkdirSync(p); fs.writeFileSync(path.join(p,'package.json'),'{}') }
13
+ const install = (parent, version) => {
14
+ const pkg=path.join(parent,'node_modules/@geajs/vite-plugin'); fs.mkdirSync(pkg,{recursive:true})
15
+ fs.writeFileSync(path.join(pkg,'package.json'),JSON.stringify({name:'@geajs/vite-plugin',version,type:'module',exports:{'.':{import:'./index.mjs'}}}))
16
+ fs.writeFileSync(path.join(pkg,'index.mjs'),`export const version=${JSON.stringify(version)}`)
17
+ return pkg
18
+ }
19
+ const legacy=install(core,'1.4.1')
20
+ assert.equal(resolveAppGeaVitePlugin(app,path.join(core,'package.json')),path.join(legacy,'index.mjs'))
21
+ fs.writeFileSync(path.join(app,'package.json'),JSON.stringify({devDependencies:{'@geajs/vite-plugin':'file:../source'}}))
22
+ assert.throws(()=>resolveAppGeaVitePlugin(app,path.join(core,'package.json')),/App declares/)
23
+ const source=install(root,'1.5.0')
24
+ fs.mkdirSync(path.join(app,'node_modules/@geajs'),{recursive:true})
25
+ fs.symlinkSync(source,path.join(app,'node_modules/@geajs/vite-plugin'))
26
+ assert.equal(resolveAppGeaVitePlugin(app,path.join(core,'package.json')),path.join(source,'index.mjs'))
27
+ fs.unlinkSync(path.join(source,'index.mjs'))
28
+ assert.throws(()=>resolveAppGeaVitePlugin(app,path.join(core,'package.json')),/App declares/)
29
+ } finally { fs.rmSync(root,{recursive:true,force:true}) }
30
+ })
31
+
32
+ test('declared app plugin hoisted to its npm workspace root resolves; an unlisted ancestor install does not', () => {
33
+ const root = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'gea-plugin-workspace-')))
34
+ try {
35
+ const app = path.join(root,'apps/clock'), stray = path.join(root,'tools/stray'), core = path.join(root,'core')
36
+ for (const p of [app,stray,core]) fs.mkdirSync(p,{recursive:true})
37
+ for (const p of [app,stray]) fs.writeFileSync(path.join(p,'package.json'),JSON.stringify({devDependencies:{'@geajs/vite-plugin':'^1.4.2'}}))
38
+ fs.writeFileSync(path.join(core,'package.json'),'{}')
39
+ fs.writeFileSync(path.join(root,'package.json'),JSON.stringify({private:true,workspaces:['apps/*']}))
40
+ const pkg=path.join(root,'node_modules/@geajs/vite-plugin'); fs.mkdirSync(pkg,{recursive:true})
41
+ fs.writeFileSync(path.join(pkg,'package.json'),JSON.stringify({name:'@geajs/vite-plugin',version:'1.4.2',type:'module',exports:{'.':{import:'./index.mjs'}}}))
42
+ fs.writeFileSync(path.join(pkg,'index.mjs'),'export const version="1.4.2"')
43
+ assert.equal(resolveAppGeaVitePlugin(app,path.join(core,'package.json')),path.join(pkg,'index.mjs'))
44
+ assert.throws(()=>resolveAppGeaVitePlugin(stray,path.join(core,'package.json')),/App declares/)
45
+ } finally { fs.rmSync(root,{recursive:true,force:true}) }
46
+ })