pixifact-cli 0.19.0 → 0.20.1

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.
@@ -0,0 +1 @@
1
+ import{b as e,d as t,l as n,u as r}from"./captureCompilerScenePreview-D1crYVOi.js";e.add(n,t),e.add(r);
@@ -0,0 +1 @@
1
+ import"./captureCompilerScenePreview-D1crYVOi.js";import"./init-ChKChotc.js";
@@ -3,8 +3,8 @@
3
3
  <head>
4
4
  <meta charset="UTF-8" />
5
5
  <title>Pixifact Scene Capture</title>
6
- <script type="module" crossorigin src="/assets/capture-BGSCjpR1.js"></script>
7
- <link rel="modulepreload" crossorigin href="/assets/captureCompilerScenePreview-BYHGr0JS.js">
6
+ <script type="module" crossorigin src="/assets/capture-C7kX9ccS.js"></script>
7
+ <link rel="modulepreload" crossorigin href="/assets/captureCompilerScenePreview-D1crYVOi.js">
8
8
  </head>
9
9
  <body>
10
10
  </body>
package/editor/index.html CHANGED
@@ -4,8 +4,8 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>Pixifact Editor</title>
7
- <script type="module" crossorigin src="/assets/editor-DdhHqfDl.js"></script>
8
- <link rel="modulepreload" crossorigin href="/assets/captureCompilerScenePreview-BYHGr0JS.js">
7
+ <script type="module" crossorigin src="/assets/editor-DXfKrkh3.js"></script>
8
+ <link rel="modulepreload" crossorigin href="/assets/captureCompilerScenePreview-D1crYVOi.js">
9
9
  <link rel="stylesheet" crossorigin href="/assets/editor-DmOIioK0.css">
10
10
  </head>
11
11
  <body>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pixifact-cli",
3
- "version": "0.19.0",
3
+ "version": "0.20.1",
4
4
  "description": "Pixifact Scene 自动化 CLI。",
5
5
  "packageManager": "bun@1.3.13",
6
6
  "type": "module",
@@ -32,7 +32,7 @@
32
32
  "start": "bun src/pixifact-cli.ts"
33
33
  },
34
34
  "dependencies": {
35
- "pixifact": "^0.19.0",
35
+ "pixifact": "^0.20.1",
36
36
  "playwright": "1.58.2",
37
37
  "vite": "^8.0.10"
38
38
  }
@@ -1,7 +1,8 @@
1
1
  import { createHash, randomBytes } from 'node:crypto';
2
2
  import fs from 'node:fs';
3
3
  import path from 'node:path';
4
- import { spawn } from 'node:child_process';
4
+ import childProcess from 'node:child_process';
5
+ import { promisify } from 'node:util';
5
6
  import {
6
7
  normalizeSceneAssetId,
7
8
  pairedSceneScriptPath,
@@ -138,8 +139,8 @@ function listProjectFiles(projectRoot: string) {
138
139
  return files.sort((left, right) => left.path.localeCompare(right.path));
139
140
  }
140
141
 
141
- function readProjectSceneBindings(projectRoot: string) {
142
- const scenePaths = listProjectFiles(projectRoot)
142
+ function readProjectSceneBindings(projectRoot: string, files: readonly EditorProjectFile[]) {
143
+ const scenePaths = files
143
144
  .filter((file) => file.kind === 'scene')
144
145
  .map((file) => normalizeSceneAssetId(file.path));
145
146
  return extractSceneScriptInterfaces(scenePaths.map((scene) => {
@@ -249,12 +250,27 @@ function serveStaticFile(staticRoot: string, pathname: string) {
249
250
  export function createEditorProjectService(options: EditorProjectServiceOptions) {
250
251
  const projectRoot = fs.realpathSync(options.projectRoot);
251
252
  const staticRoot = fs.realpathSync(options.staticRoot);
253
+ let files: EditorProjectFile[] | undefined;
254
+ let bindings: ReturnType<typeof readProjectSceneBindings> | undefined;
255
+
256
+ function invalidate(changedPath: string) {
257
+ const known = files?.some((file) => file.path === changedPath);
258
+ const entry = fs.statSync(path.resolve(projectRoot, changedPath), { throwIfNoEntry: false });
259
+ if (!known || !entry?.isFile()) files = undefined;
260
+ if (!known || !entry?.isFile() || scriptExtensions.has(path.extname(changedPath))) {
261
+ bindings = undefined;
262
+ }
263
+ }
252
264
 
253
265
  async function fetch(request: Request): Promise<Response> {
254
266
  try {
255
267
  const url = new URL(request.url);
256
268
  if (url.pathname === '/api/project' && request.method === 'GET') {
257
- const files = listProjectFiles(projectRoot);
269
+ if (url.searchParams.get('refresh') === '1') {
270
+ files = undefined;
271
+ bindings = undefined;
272
+ }
273
+ files ??= listProjectFiles(projectRoot);
258
274
  return jsonResponse({
259
275
  name: path.basename(projectRoot),
260
276
  root: projectRoot,
@@ -283,7 +299,9 @@ export function createEditorProjectService(options: EditorProjectServiceOptions)
283
299
  return jsonResponse({ path: scenePath, source, version: sceneVersion(source) });
284
300
  }
285
301
  if (url.pathname === '/api/scene-bindings' && request.method === 'GET') {
286
- return jsonResponse(readProjectSceneBindings(projectRoot));
302
+ files ??= listProjectFiles(projectRoot);
303
+ bindings ??= readProjectSceneBindings(projectRoot, files);
304
+ return jsonResponse(bindings);
287
305
  }
288
306
  if (url.pathname === '/api/scene' && request.method === 'PUT') {
289
307
  const scenePath = requestPath(url);
@@ -300,6 +318,7 @@ export function createEditorProjectService(options: EditorProjectServiceOptions)
300
318
  throw new EditorRequestError(409, 'Scene file version changed.');
301
319
  }
302
320
  fs.writeFileSync(filePath, body.source, 'utf8');
321
+ invalidate(scenePath);
303
322
  return jsonResponse({ path: scenePath, version: sceneVersion(body.source) });
304
323
  }
305
324
  if (url.pathname === '/api/file' && request.method === 'GET') {
@@ -308,9 +327,33 @@ export function createEditorProjectService(options: EditorProjectServiceOptions)
308
327
  return new Response(fs.readFileSync(filePath), {
309
328
  headers: {
310
329
  'content-type': contentTypes[extension] ?? 'application/octet-stream',
330
+ 'cache-control': 'no-store',
311
331
  },
312
332
  });
313
333
  }
334
+ if (url.pathname === '/api/open-file' && request.method === 'POST') {
335
+ if (request.headers.get('origin') !== url.origin) {
336
+ throw new EditorRequestError(403, '资源打开请求必须来自当前 Editor。');
337
+ }
338
+ let filePath: string;
339
+ try {
340
+ filePath = resolveInsideRoot(projectRoot, requestPath(url));
341
+ if (!fs.statSync(filePath).isFile()) {
342
+ throw new EditorRequestError(400, '只能使用系统默认程序打开项目内的文件。');
343
+ }
344
+ } catch (error) {
345
+ if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
346
+ throw new EditorRequestError(404, '资源文件不存在,请刷新资产列表。');
347
+ }
348
+ throw error;
349
+ }
350
+ try {
351
+ await openSystemFile(filePath);
352
+ } catch (error) {
353
+ throw new EditorRequestError(500, `无法使用系统默认程序打开文件:${error instanceof Error ? error.message : String(error)}`);
354
+ }
355
+ return jsonResponse({ ok: true });
356
+ }
314
357
  if (url.pathname.startsWith('/api/')) {
315
358
  return jsonResponse({ ok: false, error: 'Editor API route was not found.' }, 404);
316
359
  }
@@ -323,32 +366,42 @@ export function createEditorProjectService(options: EditorProjectServiceOptions)
323
366
  }
324
367
  }
325
368
 
326
- return { fetch };
369
+ return { fetch, invalidate };
327
370
  }
328
371
 
329
372
  function defaultEditorStaticRoot() {
330
373
  return path.resolve(import.meta.dir, '..', 'editor');
331
374
  }
332
375
 
376
+ async function openSystemFile(filePath: string) {
377
+ const command = process.platform === 'win32'
378
+ ? [
379
+ 'powershell.exe', '-NoProfile', '-NonInteractive', '-Command',
380
+ `Start-Process -FilePath ([Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('${Buffer.from(filePath).toString('base64')}'))) -ErrorAction Stop`,
381
+ ]
382
+ : [process.platform === 'darwin' ? 'open' : 'xdg-open', filePath];
383
+ await promisify(childProcess.execFile)(command[0], command.slice(1), { windowsHide: true });
384
+ }
385
+
333
386
  function openBrowser(url: string) {
334
387
  const command = process.platform === 'darwin'
335
388
  ? ['open', url]
336
389
  : process.platform === 'win32'
337
390
  ? ['cmd', '/c', 'start', '', url]
338
391
  : ['xdg-open', url];
339
- spawn(command[0], command.slice(1), { detached: true, stdio: 'ignore' }).unref();
392
+ childProcess.spawn(command[0], command.slice(1), { detached: true, stdio: 'ignore' }).unref();
340
393
  }
341
394
 
342
395
  export function watchEditorProject(projectRoot: string, onProjectFileChanged: (path: string) => void) {
343
396
  const changedPathTimers = new Map<string, ReturnType<typeof setTimeout>>();
344
- const watcher = fs.watch(projectRoot, { recursive: true }, (_event, fileName) => {
397
+ const watcher = fs.watch(projectRoot, { recursive: true }, (event, fileName) => {
345
398
  if (!fileName) return;
346
399
  const filePath = String(fileName);
347
400
  if (filePath.split(/[\\/]/).some((segment) => ignoredDirectories.has(segment))) {
348
401
  return;
349
402
  }
350
403
  const absolutePath = path.resolve(projectRoot, filePath);
351
- if (fs.existsSync(absolutePath) && fs.statSync(absolutePath).isDirectory()) {
404
+ if (event !== 'rename' && fs.existsSync(absolutePath) && fs.statSync(absolutePath).isDirectory()) {
352
405
  return;
353
406
  }
354
407
  const changedPath = filePath.split(path.sep).join('/');
@@ -414,7 +467,10 @@ export async function startEditorServer(options: StartEditorOptions): Promise<Ed
414
467
  },
415
468
  },
416
469
  });
417
- const stopWatcher = watchEditorProject(projectRoot, hostSession.notifyProjectFileChanged);
470
+ const stopWatcher = watchEditorProject(projectRoot, (changedPath) => {
471
+ service.invalidate(changedPath);
472
+ hostSession.notifyProjectFileChanged(changedPath);
473
+ });
418
474
  const url = `http://${server.hostname}:${server.port}`;
419
475
  const descriptor: EditorSessionDescriptor = {
420
476
  protocolVersion: editorSessionProtocolVersion,
package/src/viteTarget.ts CHANGED
@@ -71,6 +71,7 @@ export async function validatePixifactTarget(projectRootInput: string, mode: str
71
71
  'pixifact-platform',
72
72
  'pixifact-assets',
73
73
  'pixifact-resources',
74
+ 'pixifact-code-packages',
74
75
  'pixifact-minigame',
75
76
  ]) {
76
77
  if (!pluginNames.has(name)) {
@@ -232,7 +233,7 @@ async function collectDiagnostics(
232
233
  if (gameJson[property] !== undefined) {
233
234
  diagnostics.push({
234
235
  asset: `platforms/${platform}/game.json`,
235
- message: `${property} 由 Pixifact 根据 resourcePacks 生成,不能在源模板中重复声明。`,
236
+ message: `${property} 由 Pixifact 根据资源包和代码分包生成,不能在源模板中重复声明。`,
236
237
  });
237
238
  }
238
239
  }
@@ -1 +0,0 @@
1
- import{_ as e,d as t,f as n,g as r,h as i,p as a,y as o}from"./captureCompilerScenePreview-BYHGr0JS.js";import"./init-BOfNyIRQ.js";o.add(r),o.mixin(e,i),o.add(a),o.add(n),o.mixin(e,t);
@@ -1 +0,0 @@
1
- import{A as e,C as t,T as n,m as r,n as i,r as a,t as o,x as s}from"./captureCompilerScenePreview-BYHGr0JS.js";window.capturePixifactScene=async c=>{let[l,u]=await Promise.all([t(),n(c)]),d=await i({document:{template:e(u.source),sceneInterfaces:{}},projectTree:s(l),scenePath:c}),f=new r,p=!1;try{return await f.init({width:d.width,height:d.height,backgroundAlpha:0,antialias:!0,autoStart:!1,resolution:1}),p=!0,f.stage.addChild(d.root),{scenePath:u.path,revision:u.version,width:d.width,height:d.height,dataUrl:await o(f,d)}}finally{p&&(f.stage.removeChild(d.root),f.destroy({removeView:!0},{children:!1})),a(d)}};