@workfly/create-app 0.2.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/README.md +43 -0
- package/index.mjs +157 -0
- package/package.json +24 -0
- package/templates/react-ts/README.md +15 -0
- package/templates/react-ts/_gitignore +5 -0
- package/templates/react-ts/index.html +15 -0
- package/templates/react-ts/manifest.json +17 -0
- package/templates/react-ts/package.json +24 -0
- package/templates/react-ts/src/App.tsx +30 -0
- package/templates/react-ts/src/main.tsx +9 -0
- package/templates/react-ts/tsconfig.json +14 -0
- package/templates/react-ts/vite.config.ts +11 -0
- package/templates/vanilla/README.md +15 -0
- package/templates/vanilla/_gitignore +5 -0
- package/templates/vanilla/index.html +17 -0
- package/templates/vanilla/manifest.json +17 -0
- package/templates/vanilla/package.json +15 -0
- package/templates/vanilla/src/main.js +23 -0
- package/templates/vanilla/vite.config.js +10 -0
- package/templates/vanilla-ts/README.md +14 -0
- package/templates/vanilla-ts/_gitignore +5 -0
- package/templates/vanilla-ts/index.html +15 -0
- package/templates/vanilla-ts/manifest.json +17 -0
- package/templates/vanilla-ts/package.json +17 -0
- package/templates/vanilla-ts/src/main.ts +24 -0
- package/templates/vanilla-ts/tsconfig.json +13 -0
- package/templates/vanilla-ts/vite.config.ts +9 -0
- package/templates/vue-ts/README.md +15 -0
- package/templates/vue-ts/_gitignore +5 -0
- package/templates/vue-ts/index.html +15 -0
- package/templates/vue-ts/manifest.json +17 -0
- package/templates/vue-ts/package.json +22 -0
- package/templates/vue-ts/src/App.vue +27 -0
- package/templates/vue-ts/src/main.ts +4 -0
- package/templates/vue-ts/src/shims-vue.d.ts +6 -0
- package/templates/vue-ts/tsconfig.json +14 -0
- package/templates/vue-ts/vite.config.ts +11 -0
package/README.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# @workfly/create-app
|
|
2
|
+
|
|
3
|
+
脚手架一个 WorkFly 小程序——**零仓库起步**。基于 Vite,框架无关。
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm create @workfly/app my-app # 交互选模板;或 npx @workfly/create-app my-app
|
|
7
|
+
|
|
8
|
+
cd my-app
|
|
9
|
+
npm install
|
|
10
|
+
npm run dev # 本地调试:浏览器注入全局 wf + HMR
|
|
11
|
+
npm run build # 构建并打包成 my-app.wfapp.zip(拖进客户端安装 / 分发)
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## 预置模板
|
|
15
|
+
|
|
16
|
+
| 模板 | 说明 |
|
|
17
|
+
| ------------ | ------------------ |
|
|
18
|
+
| `vanilla` | 纯 JS,零框架 |
|
|
19
|
+
| `vanilla-ts` | TypeScript,零框架 |
|
|
20
|
+
| `react-ts` | React + TypeScript |
|
|
21
|
+
| `vue-ts` | Vue + TypeScript |
|
|
22
|
+
|
|
23
|
+
非交互直选:`npm create @workfly/app my-app -- --template react-ts --id com.acme.foo --name "Foo"`。
|
|
24
|
+
|
|
25
|
+
| flag | 说明 |
|
|
26
|
+
| --------------- | ------------------------------------ |
|
|
27
|
+
| `--template` | 模板名(见上表),缺省交互选择 |
|
|
28
|
+
| `--id <id>` | 应用 id,缺省 `com.example.<目录名>` |
|
|
29
|
+
| `--name <name>` | 展示名,缺省取目录名 |
|
|
30
|
+
|
|
31
|
+
## 它做了什么
|
|
32
|
+
|
|
33
|
+
产出一个普通 **Vite** 工程:
|
|
34
|
+
|
|
35
|
+
- 全部走 **sandbox 沙盒形态**(webview 进程级隔离)。`vite build` 经 [`@workfly/vite-plugin`](../vite-plugin) 校验 manifest、打包成带魔数的 `.wfapp.zip`。
|
|
36
|
+
- `dev` 时插件注入全局 `wf`(对标微信 `wx.*`)+ 网络/RSA 代理 + 权限门控,等价客户端内的 preload 注入。
|
|
37
|
+
- TS 模板通过 `@workfly/wf-types` 拿到全局 `wf` 类型。
|
|
38
|
+
- 框架由开发者自己的 `@vitejs/plugin-react|vue` 处理——本工具链只管 WorkFly 特有的注入与打包。
|
|
39
|
+
|
|
40
|
+
> 想要进程内高信任的 `bundle` 形态(深度集成、富交互)?需先与平台沟通授权,联系 <workfly@yonyou.com>。
|
|
41
|
+
> 只是「调接口 + 把结构化数据渲染成卡片」的零代码场景?直接把需求告诉客户端里的 AI 助手,生成即装,无需脚手架。
|
|
42
|
+
|
|
43
|
+
wf SDK 完整文档见发布站 `/api`,机器可读版 `/api/wf-api.json`。
|
package/index.mjs
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// @workfly/create-app —— 脚手架一个 WorkFly 小程序(Vite,框架无关)。
|
|
3
|
+
// npm create @workfly/app <dir> [--template vanilla|vanilla-ts|react-ts|vue-ts] [--id <id>] [--name <name>]
|
|
4
|
+
// npx @workfly/create-app <dir> ...
|
|
5
|
+
// 产出一个普通 Vite 工程(依赖 @workfly/vite-plugin + @workfly/wf-types):
|
|
6
|
+
// npm install → npm run dev(注入 wf + HMR)→ npm run build(出 .wfapp.zip)。
|
|
7
|
+
import { readFileSync, writeFileSync, statSync, readdirSync, existsSync, mkdirSync } from 'node:fs'
|
|
8
|
+
import { join, resolve, basename, dirname } from 'node:path'
|
|
9
|
+
import { fileURLToPath } from 'node:url'
|
|
10
|
+
import { createInterface } from 'node:readline/promises'
|
|
11
|
+
|
|
12
|
+
const here = dirname(fileURLToPath(import.meta.url))
|
|
13
|
+
const TEMPLATES_DIR = join(here, 'templates')
|
|
14
|
+
const TEMPLATES = [
|
|
15
|
+
{ key: 'vanilla', label: 'Vanilla · 纯 JS' },
|
|
16
|
+
{ key: 'vanilla-ts', label: 'Vanilla · TypeScript' },
|
|
17
|
+
{ key: 'react-ts', label: 'React · TypeScript' },
|
|
18
|
+
{ key: 'vue-ts', label: 'Vue · TypeScript' }
|
|
19
|
+
]
|
|
20
|
+
const DEFAULT_TEMPLATE = 'react-ts'
|
|
21
|
+
const TEXT_EXT = new Set([
|
|
22
|
+
'.json',
|
|
23
|
+
'.html',
|
|
24
|
+
'.js',
|
|
25
|
+
'.ts',
|
|
26
|
+
'.tsx',
|
|
27
|
+
'.mts',
|
|
28
|
+
'.cts',
|
|
29
|
+
'.vue',
|
|
30
|
+
'.md',
|
|
31
|
+
'.css',
|
|
32
|
+
'.less'
|
|
33
|
+
])
|
|
34
|
+
|
|
35
|
+
/** 解析 `--key value`、`--key=value` 与布尔 `--flag`。 */
|
|
36
|
+
function parseFlags(argv) {
|
|
37
|
+
const flags = {}
|
|
38
|
+
for (let i = 0; i < argv.length; i++) {
|
|
39
|
+
const a = argv[i]
|
|
40
|
+
if (!a.startsWith('--')) continue
|
|
41
|
+
const eq = a.indexOf('=')
|
|
42
|
+
if (eq >= 0) {
|
|
43
|
+
flags[a.slice(2, eq)] = a.slice(eq + 1)
|
|
44
|
+
continue
|
|
45
|
+
}
|
|
46
|
+
const next = argv[i + 1]
|
|
47
|
+
if (next === undefined || next.startsWith('--')) flags[a.slice(2)] = true
|
|
48
|
+
else flags[a.slice(2)] = argv[++i]
|
|
49
|
+
}
|
|
50
|
+
return flags
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** 把目录名规整成 slug:小写、非字母数字转连字符。 */
|
|
54
|
+
function slugify(s) {
|
|
55
|
+
return (
|
|
56
|
+
s
|
|
57
|
+
.toLowerCase()
|
|
58
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
59
|
+
.replace(/^-+|-+$/g, '') || 'app'
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** 递归复制模板目录,文本文件做 token 替换;_gitignore → .gitignore(绕开 npm 发布吞 .gitignore)。 */
|
|
64
|
+
function copyTemplate(srcDir, dstDir, tokens) {
|
|
65
|
+
mkdirSync(dstDir, { recursive: true })
|
|
66
|
+
for (const name of readdirSync(srcDir)) {
|
|
67
|
+
const src = join(srcDir, name)
|
|
68
|
+
const dst = join(dstDir, name === '_gitignore' ? '.gitignore' : name)
|
|
69
|
+
if (statSync(src).isDirectory()) {
|
|
70
|
+
copyTemplate(src, dst, tokens)
|
|
71
|
+
continue
|
|
72
|
+
}
|
|
73
|
+
const ext = name.slice(name.lastIndexOf('.'))
|
|
74
|
+
if (TEXT_EXT.has(ext) || name === '_gitignore') {
|
|
75
|
+
let text = readFileSync(src, 'utf8')
|
|
76
|
+
for (const [k, v] of Object.entries(tokens)) text = text.replaceAll(k, v)
|
|
77
|
+
writeFileSync(dst, text)
|
|
78
|
+
} else {
|
|
79
|
+
writeFileSync(dst, readFileSync(src))
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async function main() {
|
|
85
|
+
const argv = process.argv.slice(2)
|
|
86
|
+
const flags = parseFlags(argv)
|
|
87
|
+
const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY)
|
|
88
|
+
let rl
|
|
89
|
+
const ask = async (q) => {
|
|
90
|
+
rl ??= createInterface({ input: process.stdin, output: process.stdout })
|
|
91
|
+
return (await rl.question(q)).trim()
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// 目录
|
|
95
|
+
let dir = argv.find((a) => !a.startsWith('--'))
|
|
96
|
+
if (!dir) {
|
|
97
|
+
if (!interactive) {
|
|
98
|
+
console.error(
|
|
99
|
+
'用法: npm create @workfly/app <dir> [--template vanilla|vanilla-ts|react-ts|vue-ts] [--id <id>] [--name <name>]'
|
|
100
|
+
)
|
|
101
|
+
process.exit(1)
|
|
102
|
+
}
|
|
103
|
+
dir = (await ask('项目目录名: ')) || 'my-workfly-app'
|
|
104
|
+
}
|
|
105
|
+
const dst = resolve(dir)
|
|
106
|
+
if (existsSync(dst) && readdirSync(dst).length) {
|
|
107
|
+
console.error(`✗ 目标目录非空:${dst}`)
|
|
108
|
+
process.exit(1)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// 模板
|
|
112
|
+
let template = flags.template
|
|
113
|
+
if (template && !TEMPLATES.some((t) => t.key === template)) {
|
|
114
|
+
console.error(`✗ 未知模板 '${template}',可用:${TEMPLATES.map((t) => t.key).join(' / ')}`)
|
|
115
|
+
process.exit(1)
|
|
116
|
+
}
|
|
117
|
+
if (!template) {
|
|
118
|
+
if (!interactive) {
|
|
119
|
+
template = DEFAULT_TEMPLATE
|
|
120
|
+
} else {
|
|
121
|
+
console.log('\n选择模板:')
|
|
122
|
+
TEMPLATES.forEach((t, i) => console.log(` ${i + 1}) ${t.label}`))
|
|
123
|
+
const ans = await ask(
|
|
124
|
+
`输入序号 [${TEMPLATES.findIndex((t) => t.key === DEFAULT_TEMPLATE) + 1}]: `
|
|
125
|
+
)
|
|
126
|
+
template =
|
|
127
|
+
TEMPLATES[(Number(ans) || TEMPLATES.findIndex((t) => t.key === DEFAULT_TEMPLATE) + 1) - 1]
|
|
128
|
+
?.key ?? DEFAULT_TEMPLATE
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
rl?.close()
|
|
132
|
+
|
|
133
|
+
const base = basename(dst)
|
|
134
|
+
const name = flags.name ?? base
|
|
135
|
+
const slug = slugify(base)
|
|
136
|
+
const id = flags.id ?? `com.example.${slug}`
|
|
137
|
+
const initial = [...name][0]?.toUpperCase() ?? 'A'
|
|
138
|
+
copyTemplate(join(TEMPLATES_DIR, template), dst, {
|
|
139
|
+
'{{ID}}': id,
|
|
140
|
+
'{{NAME}}': name,
|
|
141
|
+
'{{INITIAL}}': initial,
|
|
142
|
+
'{{SLUG}}': slug
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
console.log(`\n✓ 已创建 ${template} 小程序:${dst}`)
|
|
146
|
+
console.log(` id=${id} · name=${name}\n`)
|
|
147
|
+
console.log('下一步:')
|
|
148
|
+
console.log(` cd ${dir}`)
|
|
149
|
+
console.log(' npm install')
|
|
150
|
+
console.log(' npm run dev # 本地调试(注入 wf + HMR)')
|
|
151
|
+
console.log(' npm run build # 构建并打包成 .wfapp.zip')
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
main().catch((e) => {
|
|
155
|
+
console.error(e)
|
|
156
|
+
process.exit(1)
|
|
157
|
+
})
|
package/package.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@workfly/create-app",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "脚手架一个 WorkFly 小程序(Vite,框架无关;预置 Vanilla / TS / React / Vue 模板),产出可独立开发并打包成 .wfapp.zip 的工程。",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"create-app": "index.mjs"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"index.mjs",
|
|
11
|
+
"templates"
|
|
12
|
+
],
|
|
13
|
+
"keywords": [
|
|
14
|
+
"workfly",
|
|
15
|
+
"miniapp",
|
|
16
|
+
"scaffold",
|
|
17
|
+
"create",
|
|
18
|
+
"vite"
|
|
19
|
+
],
|
|
20
|
+
"publishConfig": {
|
|
21
|
+
"access": "public"
|
|
22
|
+
},
|
|
23
|
+
"license": "MIT"
|
|
24
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# {{NAME}}
|
|
2
|
+
|
|
3
|
+
一个 WorkFly 小程序(React · TypeScript,sandbox 沙盒形态)。
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm install
|
|
7
|
+
npm run dev # 本地调试:浏览器注入全局 wf + HMR
|
|
8
|
+
npm run build # 类型检查 + 构建并打包成 {{SLUG}}.wfapp.zip
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
- 全局 `wf` 的类型来自 `@workfly/wf-types`(见 `tsconfig.json` 的 `types`),直接调用无需 import。
|
|
12
|
+
- `index.html` 含沙盒 CSP(页面不直连网络,全走 `wf.request`)。
|
|
13
|
+
- 框架由 `@vitejs/plugin-react` 处理;`@workfly/vite-plugin` 负责 wf 注入与打包。
|
|
14
|
+
|
|
15
|
+
wf SDK(对标微信 `wx.*`)完整文档见发布站 `/api`。
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="zh-CN">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta
|
|
6
|
+
http-equiv="Content-Security-Policy"
|
|
7
|
+
content="default-src 'self' workfly-app:; script-src 'self' workfly-app:; style-src 'unsafe-inline'; img-src 'self' workfly-app: data: https:; font-src 'self' workfly-app: data:; connect-src 'none'"
|
|
8
|
+
/>
|
|
9
|
+
<title>{{NAME}}</title>
|
|
10
|
+
</head>
|
|
11
|
+
<body>
|
|
12
|
+
<div id="app"></div>
|
|
13
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
14
|
+
</body>
|
|
15
|
+
</html>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "{{ID}}",
|
|
3
|
+
"name": "{{NAME}}",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"manifest": 1,
|
|
6
|
+
"wfSdk": "1.0.0",
|
|
7
|
+
"minClient": "0.10.0",
|
|
8
|
+
"description": "{{NAME}} · WorkFly 小程序",
|
|
9
|
+
"iconPlaceholder": { "initial": "{{INITIAL}}", "color": "#3461ff" },
|
|
10
|
+
"scope": "private",
|
|
11
|
+
"contains": ["ui"],
|
|
12
|
+
"ui": {
|
|
13
|
+
"standalone": { "enabled": true, "entry": "dist/index.html", "render": "sandbox", "layout": "full" }
|
|
14
|
+
},
|
|
15
|
+
"launchers": ["miniprogram_panel", "spotlight"],
|
|
16
|
+
"permissions": [{ "id": "net.fetch", "reason": "调用你填写的接口获取要展示的数据" }]
|
|
17
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{SLUG}}",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "vite",
|
|
8
|
+
"build": "tsc --noEmit && vite build",
|
|
9
|
+
"preview": "vite preview"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"react": "^19.2.0",
|
|
13
|
+
"react-dom": "^19.2.0"
|
|
14
|
+
},
|
|
15
|
+
"devDependencies": {
|
|
16
|
+
"@vitejs/plugin-react": "^6.0.0",
|
|
17
|
+
"@types/react": "^19.2.0",
|
|
18
|
+
"@types/react-dom": "^19.2.0",
|
|
19
|
+
"typescript": "^6.0.0",
|
|
20
|
+
"vite": "^8.0.0",
|
|
21
|
+
"@workfly/vite-plugin": "^0.1.0",
|
|
22
|
+
"@workfly/wf-types": "^0.1.0"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { useState } from 'react'
|
|
2
|
+
|
|
3
|
+
// 全局 wf 的类型来自 @workfly/wf-types(tsconfig types),直接调用、无需 import。
|
|
4
|
+
const APP_NAME = '{{NAME}}'
|
|
5
|
+
|
|
6
|
+
export default function App() {
|
|
7
|
+
const [out, setOut] = useState('就绪。')
|
|
8
|
+
const sys = wf.getSystemInfoSync()
|
|
9
|
+
|
|
10
|
+
async function go() {
|
|
11
|
+
try {
|
|
12
|
+
const res = await wf.request({ url: 'https://httpbin.org/get' })
|
|
13
|
+
setOut(JSON.stringify(res.data, null, 2).slice(0, 400))
|
|
14
|
+
await wf.showToast({ title: '已更新' })
|
|
15
|
+
} catch (e) {
|
|
16
|
+
setOut(String((e as Error)?.message ?? e))
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<main style={{ fontFamily: 'system-ui, sans-serif', padding: 24 }}>
|
|
22
|
+
<h1>{APP_NAME}</h1>
|
|
23
|
+
<p>
|
|
24
|
+
平台 {sys.platform} · 主题 {sys.theme}
|
|
25
|
+
</p>
|
|
26
|
+
<button onClick={go}>拉取数据</button>
|
|
27
|
+
<pre>{out}</pre>
|
|
28
|
+
</main>
|
|
29
|
+
)
|
|
30
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
5
|
+
"module": "ESNext",
|
|
6
|
+
"moduleResolution": "bundler",
|
|
7
|
+
"jsx": "react-jsx",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"noEmit": true,
|
|
11
|
+
"types": ["@workfly/wf-types"]
|
|
12
|
+
},
|
|
13
|
+
"include": ["src"]
|
|
14
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { defineConfig } from 'vite'
|
|
2
|
+
import react from '@vitejs/plugin-react'
|
|
3
|
+
import workfly from '@workfly/vite-plugin'
|
|
4
|
+
|
|
5
|
+
// base:'./' 与 modulePreload.polyfill:false 是沙盒小程序的硬约束(勿改)。
|
|
6
|
+
// 框架由 @vitejs/plugin-react 处理;workfly() 只管 wf 注入与打包。
|
|
7
|
+
export default defineConfig({
|
|
8
|
+
base: './',
|
|
9
|
+
plugins: [react(), workfly()],
|
|
10
|
+
build: { modulePreload: { polyfill: false } }
|
|
11
|
+
})
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# {{NAME}}
|
|
2
|
+
|
|
3
|
+
一个 WorkFly 小程序(Vanilla · 纯 JS,sandbox 沙盒形态)。
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm install
|
|
7
|
+
npm run dev # 本地调试:浏览器注入全局 wf + HMR
|
|
8
|
+
npm run build # 构建并打包成 {{SLUG}}.wfapp.zip(可拖进客户端安装 / 分发)
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
- `manifest.json` —— 小程序元信息与能力声明(`permissions` 声明用到的能力,如 `net.fetch`)。
|
|
12
|
+
- `index.html` —— 入口;含沙盒 CSP(页面不直连网络,全走 `wf.request`)。
|
|
13
|
+
- `src/main.js` —— 业务逻辑,直接用注入的全局 `wf`。
|
|
14
|
+
|
|
15
|
+
wf SDK(对标微信 `wx.*`)完整文档见发布站 `/api`。
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="zh-CN">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<!-- 沙盒形态:页面不直连网络,宿主能力全走注入的全局 wf;wf.request 经主进程代理。
|
|
6
|
+
CSP 不放开 script-src 'unsafe-inline'(Vite 内联脚本已由 build.modulePreload.polyfill:false 关闭)。 -->
|
|
7
|
+
<meta
|
|
8
|
+
http-equiv="Content-Security-Policy"
|
|
9
|
+
content="default-src 'self' workfly-app:; script-src 'self' workfly-app:; style-src 'unsafe-inline'; img-src 'self' workfly-app: data: https:; font-src 'self' workfly-app: data:; connect-src 'none'"
|
|
10
|
+
/>
|
|
11
|
+
<title>{{NAME}}</title>
|
|
12
|
+
</head>
|
|
13
|
+
<body>
|
|
14
|
+
<div id="app"></div>
|
|
15
|
+
<script type="module" src="/src/main.js"></script>
|
|
16
|
+
</body>
|
|
17
|
+
</html>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "{{ID}}",
|
|
3
|
+
"name": "{{NAME}}",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"manifest": 1,
|
|
6
|
+
"wfSdk": "1.0.0",
|
|
7
|
+
"minClient": "0.10.0",
|
|
8
|
+
"description": "{{NAME}} · WorkFly 小程序",
|
|
9
|
+
"iconPlaceholder": { "initial": "{{INITIAL}}", "color": "#3461ff" },
|
|
10
|
+
"scope": "private",
|
|
11
|
+
"contains": ["ui"],
|
|
12
|
+
"ui": {
|
|
13
|
+
"standalone": { "enabled": true, "entry": "dist/index.html", "render": "sandbox", "layout": "full" }
|
|
14
|
+
},
|
|
15
|
+
"launchers": ["miniprogram_panel", "spotlight"],
|
|
16
|
+
"permissions": [{ "id": "net.fetch", "reason": "调用你填写的接口获取要展示的数据" }]
|
|
17
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{SLUG}}",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "vite",
|
|
8
|
+
"build": "vite build",
|
|
9
|
+
"preview": "vite preview"
|
|
10
|
+
},
|
|
11
|
+
"devDependencies": {
|
|
12
|
+
"vite": "^8.0.0",
|
|
13
|
+
"@workfly/vite-plugin": "^0.1.0"
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// 通过宿主注入的全局 wf 调用平台能力(网络 / 存储 / UI / 系统…)。
|
|
2
|
+
// 完整 API 见发布站 /api,机器可读版 /api/wf-api.json。
|
|
3
|
+
const APP_NAME = '{{NAME}}'
|
|
4
|
+
const sys = wf.getSystemInfoSync()
|
|
5
|
+
|
|
6
|
+
document.getElementById('app').innerHTML = `
|
|
7
|
+
<main style="font-family: system-ui, sans-serif; padding: 24px">
|
|
8
|
+
<h1>${APP_NAME}</h1>
|
|
9
|
+
<p>平台 ${sys.platform} · 主题 ${sys.theme}</p>
|
|
10
|
+
<button id="go">拉取数据</button>
|
|
11
|
+
<pre id="out">就绪。</pre>
|
|
12
|
+
</main>`
|
|
13
|
+
|
|
14
|
+
document.getElementById('go').addEventListener('click', async () => {
|
|
15
|
+
const out = document.getElementById('out')
|
|
16
|
+
try {
|
|
17
|
+
const res = await wf.request({ url: 'https://httpbin.org/get' })
|
|
18
|
+
out.textContent = JSON.stringify(res.data, null, 2).slice(0, 400)
|
|
19
|
+
await wf.showToast({ title: '已更新' })
|
|
20
|
+
} catch (e) {
|
|
21
|
+
out.textContent = String(e?.message ?? e)
|
|
22
|
+
}
|
|
23
|
+
})
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import workfly from '@workfly/vite-plugin'
|
|
2
|
+
|
|
3
|
+
// base:'./' 与 modulePreload.polyfill:false 是沙盒小程序的硬约束(勿改):
|
|
4
|
+
// · 资源相对路径,适配宿主 workfly-app://<id>/ 加载
|
|
5
|
+
// · 关掉 Vite 内联 modulepreload polyfill,避免被沙盒 CSP(script-src 'self')拦截导致白屏
|
|
6
|
+
export default {
|
|
7
|
+
base: './',
|
|
8
|
+
plugins: [workfly()],
|
|
9
|
+
build: { modulePreload: { polyfill: false } }
|
|
10
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# {{NAME}}
|
|
2
|
+
|
|
3
|
+
一个 WorkFly 小程序(Vanilla · TypeScript,sandbox 沙盒形态)。
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm install
|
|
7
|
+
npm run dev # 本地调试:浏览器注入全局 wf + HMR
|
|
8
|
+
npm run build # 类型检查 + 构建并打包成 {{SLUG}}.wfapp.zip
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
- 全局 `wf` 的类型来自 `@workfly/wf-types`(见 `tsconfig.json` 的 `types`)。
|
|
12
|
+
- `index.html` 含沙盒 CSP(页面不直连网络,全走 `wf.request`)。
|
|
13
|
+
|
|
14
|
+
wf SDK(对标微信 `wx.*`)完整文档见发布站 `/api`。
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="zh-CN">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta
|
|
6
|
+
http-equiv="Content-Security-Policy"
|
|
7
|
+
content="default-src 'self' workfly-app:; script-src 'self' workfly-app:; style-src 'unsafe-inline'; img-src 'self' workfly-app: data: https:; font-src 'self' workfly-app: data:; connect-src 'none'"
|
|
8
|
+
/>
|
|
9
|
+
<title>{{NAME}}</title>
|
|
10
|
+
</head>
|
|
11
|
+
<body>
|
|
12
|
+
<div id="app"></div>
|
|
13
|
+
<script type="module" src="/src/main.ts"></script>
|
|
14
|
+
</body>
|
|
15
|
+
</html>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "{{ID}}",
|
|
3
|
+
"name": "{{NAME}}",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"manifest": 1,
|
|
6
|
+
"wfSdk": "1.0.0",
|
|
7
|
+
"minClient": "0.10.0",
|
|
8
|
+
"description": "{{NAME}} · WorkFly 小程序",
|
|
9
|
+
"iconPlaceholder": { "initial": "{{INITIAL}}", "color": "#3461ff" },
|
|
10
|
+
"scope": "private",
|
|
11
|
+
"contains": ["ui"],
|
|
12
|
+
"ui": {
|
|
13
|
+
"standalone": { "enabled": true, "entry": "dist/index.html", "render": "sandbox", "layout": "full" }
|
|
14
|
+
},
|
|
15
|
+
"launchers": ["miniprogram_panel", "spotlight"],
|
|
16
|
+
"permissions": [{ "id": "net.fetch", "reason": "调用你填写的接口获取要展示的数据" }]
|
|
17
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{SLUG}}",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "vite",
|
|
8
|
+
"build": "tsc --noEmit && vite build",
|
|
9
|
+
"preview": "vite preview"
|
|
10
|
+
},
|
|
11
|
+
"devDependencies": {
|
|
12
|
+
"typescript": "^6.0.0",
|
|
13
|
+
"vite": "^8.0.0",
|
|
14
|
+
"@workfly/vite-plugin": "^0.1.0",
|
|
15
|
+
"@workfly/wf-types": "^0.1.0"
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// 通过宿主注入的全局 wf 调用平台能力;类型来自 @workfly/wf-types(tsconfig types)。
|
|
2
|
+
// 完整 API 见发布站 /api,机器可读版 /api/wf-api.json。
|
|
3
|
+
const APP_NAME = '{{NAME}}'
|
|
4
|
+
const sys = wf.getSystemInfoSync()
|
|
5
|
+
|
|
6
|
+
const app = document.getElementById('app')!
|
|
7
|
+
app.innerHTML = `
|
|
8
|
+
<main style="font-family: system-ui, sans-serif; padding: 24px">
|
|
9
|
+
<h1>${APP_NAME}</h1>
|
|
10
|
+
<p>平台 ${sys.platform} · 主题 ${sys.theme}</p>
|
|
11
|
+
<button id="go">拉取数据</button>
|
|
12
|
+
<pre id="out">就绪。</pre>
|
|
13
|
+
</main>`
|
|
14
|
+
|
|
15
|
+
document.getElementById('go')!.addEventListener('click', async () => {
|
|
16
|
+
const out = document.getElementById('out')!
|
|
17
|
+
try {
|
|
18
|
+
const res = await wf.request({ url: 'https://httpbin.org/get' })
|
|
19
|
+
out.textContent = JSON.stringify(res.data, null, 2).slice(0, 400)
|
|
20
|
+
await wf.showToast({ title: '已更新' })
|
|
21
|
+
} catch (e) {
|
|
22
|
+
out.textContent = String((e as Error)?.message ?? e)
|
|
23
|
+
}
|
|
24
|
+
})
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
5
|
+
"module": "ESNext",
|
|
6
|
+
"moduleResolution": "bundler",
|
|
7
|
+
"strict": true,
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"noEmit": true,
|
|
10
|
+
"types": ["@workfly/wf-types"]
|
|
11
|
+
},
|
|
12
|
+
"include": ["src", "vite.config.ts"]
|
|
13
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { defineConfig } from 'vite'
|
|
2
|
+
import workfly from '@workfly/vite-plugin'
|
|
3
|
+
|
|
4
|
+
// base:'./' 与 modulePreload.polyfill:false 是沙盒小程序的硬约束(勿改)。
|
|
5
|
+
export default defineConfig({
|
|
6
|
+
base: './',
|
|
7
|
+
plugins: [workfly()],
|
|
8
|
+
build: { modulePreload: { polyfill: false } }
|
|
9
|
+
})
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# {{NAME}}
|
|
2
|
+
|
|
3
|
+
一个 WorkFly 小程序(Vue · TypeScript,sandbox 沙盒形态)。
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm install
|
|
7
|
+
npm run dev # 本地调试:浏览器注入全局 wf + HMR
|
|
8
|
+
npm run build # 类型检查 + 构建并打包成 {{SLUG}}.wfapp.zip
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
- 全局 `wf` 的类型来自 `@workfly/wf-types`(见 `tsconfig.json` 的 `types`),在 `<script setup>` 里直接调用。
|
|
12
|
+
- `index.html` 含沙盒 CSP(页面不直连网络,全走 `wf.request`)。
|
|
13
|
+
- 框架由 `@vitejs/plugin-vue` 处理;`@workfly/vite-plugin` 负责 wf 注入与打包。
|
|
14
|
+
|
|
15
|
+
wf SDK(对标微信 `wx.*`)完整文档见发布站 `/api`。
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="zh-CN">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta
|
|
6
|
+
http-equiv="Content-Security-Policy"
|
|
7
|
+
content="default-src 'self' workfly-app:; script-src 'self' workfly-app:; style-src 'unsafe-inline'; img-src 'self' workfly-app: data: https:; font-src 'self' workfly-app: data:; connect-src 'none'"
|
|
8
|
+
/>
|
|
9
|
+
<title>{{NAME}}</title>
|
|
10
|
+
</head>
|
|
11
|
+
<body>
|
|
12
|
+
<div id="app"></div>
|
|
13
|
+
<script type="module" src="/src/main.ts"></script>
|
|
14
|
+
</body>
|
|
15
|
+
</html>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "{{ID}}",
|
|
3
|
+
"name": "{{NAME}}",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"manifest": 1,
|
|
6
|
+
"wfSdk": "1.0.0",
|
|
7
|
+
"minClient": "0.10.0",
|
|
8
|
+
"description": "{{NAME}} · WorkFly 小程序",
|
|
9
|
+
"iconPlaceholder": { "initial": "{{INITIAL}}", "color": "#3461ff" },
|
|
10
|
+
"scope": "private",
|
|
11
|
+
"contains": ["ui"],
|
|
12
|
+
"ui": {
|
|
13
|
+
"standalone": { "enabled": true, "entry": "dist/index.html", "render": "sandbox", "layout": "full" }
|
|
14
|
+
},
|
|
15
|
+
"launchers": ["miniprogram_panel", "spotlight"],
|
|
16
|
+
"permissions": [{ "id": "net.fetch", "reason": "调用你填写的接口获取要展示的数据" }]
|
|
17
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{SLUG}}",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "vite",
|
|
8
|
+
"build": "vue-tsc --noEmit && vite build",
|
|
9
|
+
"preview": "vite preview"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"vue": "^3.5.0"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"@vitejs/plugin-vue": "^6.0.0",
|
|
16
|
+
"typescript": "^6.0.0",
|
|
17
|
+
"vue-tsc": "^3.0.0",
|
|
18
|
+
"vite": "^8.0.0",
|
|
19
|
+
"@workfly/vite-plugin": "^0.1.0",
|
|
20
|
+
"@workfly/wf-types": "^0.1.0"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// 全局 wf 的类型来自 @workfly/wf-types(tsconfig types),直接调用、无需 import。
|
|
3
|
+
import { ref } from 'vue'
|
|
4
|
+
|
|
5
|
+
const APP_NAME = '{{NAME}}'
|
|
6
|
+
const sys = wf.getSystemInfoSync()
|
|
7
|
+
const out = ref('就绪。')
|
|
8
|
+
|
|
9
|
+
async function go() {
|
|
10
|
+
try {
|
|
11
|
+
const res = await wf.request({ url: 'https://httpbin.org/get' })
|
|
12
|
+
out.value = JSON.stringify(res.data, null, 2).slice(0, 400)
|
|
13
|
+
await wf.showToast({ title: '已更新' })
|
|
14
|
+
} catch (e) {
|
|
15
|
+
out.value = String((e as Error)?.message ?? e)
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
<template>
|
|
21
|
+
<main style="font-family: system-ui, sans-serif; padding: 24px">
|
|
22
|
+
<h1>{{ APP_NAME }}</h1>
|
|
23
|
+
<p>平台 {{ sys.platform }} · 主题 {{ sys.theme }}</p>
|
|
24
|
+
<button @click="go">拉取数据</button>
|
|
25
|
+
<pre>{{ out }}</pre>
|
|
26
|
+
</main>
|
|
27
|
+
</template>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
5
|
+
"module": "ESNext",
|
|
6
|
+
"moduleResolution": "bundler",
|
|
7
|
+
"jsx": "preserve",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"noEmit": true,
|
|
11
|
+
"types": ["@workfly/wf-types"]
|
|
12
|
+
},
|
|
13
|
+
"include": ["src"]
|
|
14
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { defineConfig } from 'vite'
|
|
2
|
+
import vue from '@vitejs/plugin-vue'
|
|
3
|
+
import workfly from '@workfly/vite-plugin'
|
|
4
|
+
|
|
5
|
+
// base:'./' 与 modulePreload.polyfill:false 是沙盒小程序的硬约束(勿改)。
|
|
6
|
+
// 框架由 @vitejs/plugin-vue 处理;workfly() 只管 wf 注入与打包。
|
|
7
|
+
export default defineConfig({
|
|
8
|
+
base: './',
|
|
9
|
+
plugins: [vue(), workfly()],
|
|
10
|
+
build: { modulePreload: { polyfill: false } }
|
|
11
|
+
})
|