@joplo-app/server 0.0.0-stage → 0.1.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,127 @@
1
+ /**
2
+ * 看板与发布站资源在哪里(D36)。两种形态:
3
+ *
4
+ * - source:在仓库里从 TS 源码运行(`pnpm joplo …`、测试)。Vite 直接编译 packages/web/src,界面本身也能热更新。
5
+ * - dist:从 npm 安装的包运行。资源是 @joplo-app/web 发包前预构建的 dist/(见 packages/web/scripts/build-dist.mjs),
6
+ * 运行时不读取 packages/web/src。作者的 className 用 dist/project.css 现场编译(Tailwind,@source 指向项目目录)。
7
+ *
8
+ * 选择规则:本文件以 .ts 运行且 packages/web/src 存在 → source;否则 dist。`JOPLO_WEB=dist` 可在仓库里强制 dist
9
+ * (用于验证预构建产物)。
10
+ */
11
+ import fs from 'node:fs';
12
+ import { createRequire } from 'node:module';
13
+ import path from 'node:path';
14
+ import { fileURLToPath } from 'node:url';
15
+ import { JoploError } from '@joplo-app/core';
16
+ const require = createRequire(import.meta.url);
17
+ const FROM_SOURCE = import.meta.filename.endsWith('.ts');
18
+ const REPO_WEB = path.resolve(import.meta.dirname, '../../web');
19
+ const REPO_KIT = path.resolve(import.meta.dirname, '../../kit/src');
20
+ const broken = (webDir, what) => new JoploError('NOT_FOUND', `@joplo-app/web(${webDir})缺少预构建资源:${what}。请重新安装 joplo(npm install),或在 Joplo 仓库里运行 pnpm --filter @joplo-app/web build`);
21
+ /** 读取并校验 @joplo-app/web 的 dist/manifest.json。 */
22
+ export function readWebDist(webDir) {
23
+ const dist = path.join(webDir, 'dist');
24
+ const manifestFile = path.join(dist, 'manifest.json');
25
+ let manifest;
26
+ try {
27
+ manifest = JSON.parse(fs.readFileSync(manifestFile, 'utf8'));
28
+ }
29
+ catch {
30
+ throw broken(webDir, 'dist/manifest.json');
31
+ }
32
+ if (manifest.format !== 1)
33
+ throw broken(webDir, `不认识的清单格式 ${String(manifest.format)}`);
34
+ const inside = (rel, label) => {
35
+ if (typeof rel !== 'string' || !rel)
36
+ throw broken(webDir, label);
37
+ const abs = path.resolve(dist, rel);
38
+ if (abs !== dist && !abs.startsWith(dist + path.sep))
39
+ throw broken(webDir, `${label} 越出了 dist/`);
40
+ if (!fs.existsSync(abs))
41
+ throw broken(webDir, `dist/${rel}`);
42
+ return abs;
43
+ };
44
+ const board = inside(manifest.board, 'board');
45
+ inside(manifest.site, 'site');
46
+ const projectCss = inside(manifest.projectCss, 'projectCss');
47
+ const appDir = path.dirname(board);
48
+ let siteFiles;
49
+ try {
50
+ siteFiles = JSON.parse(fs.readFileSync(inside(manifest.siteFiles, 'siteFiles'), 'utf8'));
51
+ }
52
+ catch (e) {
53
+ if (e instanceof JoploError)
54
+ throw e;
55
+ throw broken(webDir, 'site-files.json 不是合法 JSON');
56
+ }
57
+ if (!Array.isArray(siteFiles) || !siteFiles.every((f) => typeof f === 'string' && f && !path.isAbsolute(f) && !f.split(/[\\/]/).includes('..')))
58
+ throw broken(webDir, 'site-files.json 应为 appDir 内的相对路径列表');
59
+ return { mode: 'dist', webDir, appDir, siteFiles, projectCss, version: typeof manifest.version === 'string' ? manifest.version : '' };
60
+ }
61
+ let cached = null;
62
+ /** 看板与发布站资源。结果按进程缓存(env 只在首次调用时生效;测试可传入 env 绕过缓存)。 */
63
+ export function locateWebAssets(env) {
64
+ if (!env && cached)
65
+ return cached;
66
+ const e = env ?? process.env;
67
+ let out;
68
+ if (FROM_SOURCE && e.JOPLO_WEB !== 'dist' && fs.existsSync(path.join(REPO_WEB, 'src/main.tsx')))
69
+ out = { mode: 'source', webDir: REPO_WEB, indexCss: path.join(REPO_WEB, 'src/index.css'), kitSrc: REPO_KIT };
70
+ else {
71
+ let pkg;
72
+ try {
73
+ pkg = require.resolve('@joplo-app/web/package.json');
74
+ }
75
+ catch {
76
+ throw new JoploError('NOT_FOUND', '找不到 @joplo-app/web(看板与发布站资源)。请重新安装 joplo(npm install)');
77
+ }
78
+ out = readWebDist(path.dirname(fs.realpathSync(pkg)));
79
+ }
80
+ if (!env)
81
+ cached = out;
82
+ return out;
83
+ }
84
+ /** 作者 className 的 CSS 入口(source:packages/web/src/index.css;dist:@joplo-app/web/dist/project.css)。 */
85
+ export const cssEntry = (web) => (web.mode === 'source' ? web.indexCss : web.projectCss);
86
+ /** Tailwind 的 index.css,从 server 自己的依赖解析(pnpm 严格布局下 @joplo-app/web 没有 tailwindcss 依赖)。 */
87
+ export function tailwindIndexCss() {
88
+ return require.resolve('tailwindcss/index.css');
89
+ }
90
+ /** 从 dir 起向上找 node_modules/<name>/package.json(与 Node 的包解析顺序一致,不依赖 exports)。 */
91
+ export function findPackageJson(dir, name) {
92
+ let cur = path.resolve(dir);
93
+ for (;;) {
94
+ const f = path.join(cur, 'node_modules', name, 'package.json');
95
+ if (fs.existsSync(f))
96
+ return f;
97
+ const parent = path.dirname(cur);
98
+ if (parent === cur)
99
+ return null;
100
+ cur = parent;
101
+ }
102
+ }
103
+ const KIT_SUBPATHS = ['ui', 'node', 'jsx-runtime', 'jsx-dev-runtime'];
104
+ /**
105
+ * 项目源文件里的 `@joplo-app/kit` 怎样解析:
106
+ * - source:指向仓库里的 kit 源码(项目可在任意目录,不需要自己的 node_modules);`@/` 指向 web 源码。
107
+ * - dist:项目装了 @joplo-app/kit 就按项目依赖解析(与 `joplo check` 用的 tsx 一致);没装则退回 CLI 自带的 kit。
108
+ */
109
+ export function kitAliases(web, projectDir) {
110
+ if (web.mode === 'source')
111
+ return [
112
+ { find: /^@joplo-app\/kit$/, replacement: path.join(web.kitSrc, 'index.ts') },
113
+ { find: /^@joplo-app\/kit\/(ui|node|jsx-runtime|jsx-dev-runtime)$/, replacement: path.join(web.kitSrc, '$1.ts') },
114
+ { find: /^@\//, replacement: path.join(web.webDir, 'src') + '/' },
115
+ ];
116
+ if (findPackageJson(projectDir, '@joplo-app/kit'))
117
+ return [];
118
+ const main = fileURLToPath(import.meta.resolve('@joplo-app/kit'));
119
+ const sub = fileURLToPath(import.meta.resolve(`@joplo-app/kit/${KIT_SUBPATHS[0]}`));
120
+ if (path.dirname(main) !== path.dirname(sub))
121
+ throw new JoploError('NOT_FOUND', `@joplo-app/kit 的入口不在同一目录:${main}、${sub}`);
122
+ const ext = path.extname(main);
123
+ return [
124
+ { find: /^@joplo-app\/kit$/, replacement: main },
125
+ { find: /^@joplo-app\/kit\/(ui|node|jsx-runtime|jsx-dev-runtime)$/, replacement: path.join(path.dirname(main), `$1${ext}`) },
126
+ ];
127
+ }
package/package.json CHANGED
@@ -1,6 +1,56 @@
1
1
  {
2
2
  "name": "@joplo-app/server",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.1.1",
4
+ "description": "Joplo 本地服务:dev 看板、build 发布站、render 截图",
5
+ "license": "Apache-2.0",
6
+ "type": "module",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "default": "./dist/index.js"
11
+ },
12
+ "./package.json": "./package.json"
13
+ },
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "dependencies": {
18
+ "@joplo-app/core": "0.1.1",
19
+ "@joplo-app/kit": "0.1.1",
20
+ "@joplo-app/web": "0.1.1",
21
+ "@tailwindcss/vite": "4.3.4",
22
+ "tailwindcss": "4.3.4",
23
+ "vite": "8.3.5",
24
+ "zod": "4.6.7"
25
+ },
26
+ "peerDependencies": {
27
+ "playwright": "^1.64.1"
28
+ },
29
+ "peerDependenciesMeta": {
30
+ "playwright": {
31
+ "optional": true
32
+ }
33
+ },
34
+ "devDependencies": {
35
+ "@vitejs/plugin-react": "6.2.1",
36
+ "playwright": "1.64.1"
37
+ },
38
+ "engines": {
39
+ "node": ">=22"
40
+ },
41
+ "publishConfig": {
42
+ "access": "public"
43
+ },
44
+ "repository": {
45
+ "type": "git",
46
+ "url": "git+https://github.com/Joplo-app/Joplo.git",
47
+ "directory": "packages/server"
48
+ },
49
+ "homepage": "https://github.com/Joplo-app/Joplo#readme",
50
+ "bugs": {
51
+ "url": "https://github.com/Joplo-app/Joplo/issues"
52
+ },
53
+ "scripts": {
54
+ "build": "node ../../scripts/build-package.mjs"
55
+ }
6
56
  }
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.