@aebzx/slang-tool 1.0.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.mjs +16 -0
- package/command.ts +254 -0
- package/dist/cli.js +15785 -0
- package/entry.ts +90 -0
- package/model.ts +90 -0
- package/package.json +34 -0
- package/test/entry.test.ts +102 -0
- package/test/utils.test.ts +72 -0
- package/tsconfig.json +15 -0
- package/utils/ajax.ts +15 -0
- package/utils/index.ts +4 -0
- package/utils/net.ts +75 -0
- package/utils/utils.ts +58 -0
package/build.mjs
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { rolldown } from 'rolldown'
|
|
3
|
+
import * as path from 'node:path'
|
|
4
|
+
import { fileURLToPath } from 'node:url'
|
|
5
|
+
const root = path.dirname(fileURLToPath(import.meta.url))
|
|
6
|
+
const bundle = await rolldown({
|
|
7
|
+
input: path.join(root, 'entry.ts'),
|
|
8
|
+
platform: 'node',
|
|
9
|
+
external: [/^node:/, 'commander','@inquirer/prompts','tar','lzma-native'],
|
|
10
|
+
})
|
|
11
|
+
await bundle.write({
|
|
12
|
+
dir: path.join(root, 'dist'),
|
|
13
|
+
entryFileNames: 'cli.js',
|
|
14
|
+
format: 'esm',
|
|
15
|
+
banner: '#!/usr/bin/env node',
|
|
16
|
+
})
|
package/command.ts
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import {ProjectConfig} from './model.ts'
|
|
2
|
+
import {
|
|
3
|
+
verify,
|
|
4
|
+
global_config,
|
|
5
|
+
net,
|
|
6
|
+
project_config,
|
|
7
|
+
decompress,
|
|
8
|
+
compress,
|
|
9
|
+
write_project_config,
|
|
10
|
+
write_global_config
|
|
11
|
+
} from './utils'
|
|
12
|
+
import {input, select} from '@inquirer/prompts'
|
|
13
|
+
import {existsSync, mkdirSync, readFileSync, rmdirSync, rmSync, write, writeFileSync} from 'node:fs'
|
|
14
|
+
import path from 'node:path'
|
|
15
|
+
import os from 'node:os'
|
|
16
|
+
import {createHash} from 'node:crypto'
|
|
17
|
+
import {spawnSync} from 'node:child_process'
|
|
18
|
+
async function init(){
|
|
19
|
+
//默认为当前目录名
|
|
20
|
+
const name=await input({message:'项目名',default:process.cwd().split('/').pop()||'$'})
|
|
21
|
+
const version=await input({message:'版本号',default:'1.0.0'})
|
|
22
|
+
const author=await input({message:'作者',default:global_config().username})
|
|
23
|
+
const license=await input({message:'协议',default:'MIT'})
|
|
24
|
+
const lib=await input({message:'库目录',default:'lib'})
|
|
25
|
+
const optimize=await select({message:'优化级别',choices:[
|
|
26
|
+
{
|
|
27
|
+
name:'不优化',
|
|
28
|
+
value:0
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name:'普通优化',
|
|
32
|
+
value:1
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name:'激进优化',
|
|
36
|
+
value:2
|
|
37
|
+
}
|
|
38
|
+
],default:0})
|
|
39
|
+
const output=await input({message:'输出文件(.sbin)',default:name})+'.sbin'
|
|
40
|
+
const venv=await input({message:'虚拟环境目录',default:'venv'})
|
|
41
|
+
let compiler_menu=(await net.list.compiler()).filter(i=>i.child.length!=0)
|
|
42
|
+
let compiler_large_version=compiler_menu.map((item)=>item.version)
|
|
43
|
+
const compiler=await select({message:'选择Slang版本',choices:compiler_large_version})
|
|
44
|
+
//服务端 CompilerChild 无 date 字段,按列表顺序(服务端 push 顺序,新版本在后)
|
|
45
|
+
let compiler_small_version=compiler_menu.find((item)=>item.version==compiler).child
|
|
46
|
+
.map((item)=>item.version)
|
|
47
|
+
const small=await select({message:'选择Slang小版本',choices:compiler_small_version})
|
|
48
|
+
let vm_menu=await net.list.vm()
|
|
49
|
+
const vm=await select({message:'选择虚拟机版本',choices:vm_menu.map((item)=>{
|
|
50
|
+
return {name:`${item.version}(基于${item.isa}指令集)`,value:item.version}
|
|
51
|
+
})})
|
|
52
|
+
let _compiler=await net.download.compiler(compiler,small)
|
|
53
|
+
let _vm=await net.download.vm(vm)
|
|
54
|
+
if(createHash('sha256').update(_compiler).digest('hex')!=
|
|
55
|
+
compiler_menu.find(i=>i.version==compiler).child
|
|
56
|
+
.find(i=>i.version==small).hex)
|
|
57
|
+
throw new Error('文件校验失败')
|
|
58
|
+
if(createHash('sha256').update(_vm).digest('hex')!=
|
|
59
|
+
vm_menu.find(i=>i.version==vm).hex)
|
|
60
|
+
throw new Error('文件校验失败')
|
|
61
|
+
mkdirSync(path.join(process.cwd(),venv))
|
|
62
|
+
writeFileSync(path.join(process.cwd(),venv,'compiler.js'),_compiler,'utf-8')
|
|
63
|
+
//+ 优先级高于 ==,必须加括号;Windows 下补 .exe 后缀
|
|
64
|
+
writeFileSync(path.join(process.cwd(),venv,'vm'+(os.type()=='Windows_NT'?'.exe':'')),_vm)
|
|
65
|
+
const write:ProjectConfig={
|
|
66
|
+
name,
|
|
67
|
+
version,
|
|
68
|
+
author,
|
|
69
|
+
license,
|
|
70
|
+
ignore:[lib],
|
|
71
|
+
dependency:[],
|
|
72
|
+
optimize,
|
|
73
|
+
output,
|
|
74
|
+
venv:{
|
|
75
|
+
dir:venv,
|
|
76
|
+
compiler,
|
|
77
|
+
vm,
|
|
78
|
+
compiler_version:small,
|
|
79
|
+
vm_version:vm
|
|
80
|
+
},
|
|
81
|
+
lib:{
|
|
82
|
+
local:lib,
|
|
83
|
+
data:[]
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
writeFileSync(path.join(process.cwd(),'slang.json'),JSON.stringify(write,null,4),'utf-8')
|
|
87
|
+
}
|
|
88
|
+
async function install(name:string,version:string,d:boolean=true){
|
|
89
|
+
verify(process.cwd())
|
|
90
|
+
let config=project_config(process.cwd())
|
|
91
|
+
let list=await net.list.module()
|
|
92
|
+
if(!(list.find((item)=>item.name==name&&
|
|
93
|
+
item.version.map(i=>i.version==version).includes(true))))
|
|
94
|
+
throw new Error(`不存在${name}@${version}`)
|
|
95
|
+
if(config.lib.data.find((item)=>item.name==name&&item.version==version))
|
|
96
|
+
throw new Error(`${name}已安装`)
|
|
97
|
+
let module=
|
|
98
|
+
list.find((item)=>item.name==name)
|
|
99
|
+
.version.find((item)=>item.version==version)
|
|
100
|
+
if(!module)throw new Error(`不存在${name}@${version}`)
|
|
101
|
+
//检查依赖是否冲突,以及需要安装的包
|
|
102
|
+
let dependencies:{name:string,version:string}[]=[]
|
|
103
|
+
for(let i of module.dependencies){
|
|
104
|
+
if(config.lib.data.find((item)=>item.name==i.name&&item.version!=i.version))
|
|
105
|
+
throw new Error(`依赖冲突${i.name}@${i.version}`)
|
|
106
|
+
if(!config.lib.data.find((item)=>item.name==i.name))
|
|
107
|
+
dependencies.push(i)
|
|
108
|
+
}
|
|
109
|
+
console.log(`install ${name}@${version}`)
|
|
110
|
+
config.lib.data.push({
|
|
111
|
+
name,
|
|
112
|
+
version
|
|
113
|
+
})
|
|
114
|
+
config.lock.push({
|
|
115
|
+
name,
|
|
116
|
+
dependencies:dependencies
|
|
117
|
+
})
|
|
118
|
+
if(d)config.dependency.push({name, version})
|
|
119
|
+
for(let i of dependencies)
|
|
120
|
+
await install(i.name,i.version,false)
|
|
121
|
+
await decompress(name, await net.download.module(name, version), path.join(process.cwd(), config.lib.local),
|
|
122
|
+
module.hex)
|
|
123
|
+
write_project_config(process.cwd(), config)
|
|
124
|
+
}
|
|
125
|
+
async function uninstall(name:string){
|
|
126
|
+
verify(process.cwd())
|
|
127
|
+
let config=project_config(process.cwd())
|
|
128
|
+
//寻找是否存在
|
|
129
|
+
if(!config.dependency.find((item)=>item.name==name))
|
|
130
|
+
throw new Error(`${name}未安装`)
|
|
131
|
+
console.log(`uninstall ${name}`)
|
|
132
|
+
//找到需要连带删除的(lock 可能不含该条目,如旧项目,需判空)
|
|
133
|
+
let lock=config.lock?.find(i=>i.name==name)
|
|
134
|
+
let dependencies:string[]=(lock?lock.dependencies:[]).map(i=>i.name).filter(i=>!config.dependency.find(j=>j.name==i))
|
|
135
|
+
.filter(i=>!config.lock.map(j=>j.dependencies
|
|
136
|
+
.find(k=>k.name==i)!=null).includes(true))
|
|
137
|
+
for(let i of dependencies)
|
|
138
|
+
await uninstall(i)
|
|
139
|
+
config.dependency=config.dependency.filter((item)=>item.name!=name)
|
|
140
|
+
config.lib.data=config.lib.data.filter((item)=>item.name!=name)
|
|
141
|
+
config.lock=config.lock.filter((item)=>item.name!=name)
|
|
142
|
+
rmdirSync(path.join(process.cwd(), config.lib.local, name))
|
|
143
|
+
write_project_config(process.cwd(), config)
|
|
144
|
+
}
|
|
145
|
+
async function publish_module(){
|
|
146
|
+
verify(process.cwd())
|
|
147
|
+
let config=project_config(process.cwd())
|
|
148
|
+
let global=global_config()
|
|
149
|
+
let list=await net.list.module()
|
|
150
|
+
if(list.find((item)=>item.name==config.name&&
|
|
151
|
+
item.version.map(i=>i.version==config.version).includes(true)))
|
|
152
|
+
throw new Error(`${config.name}@${config.version}已经存在`)
|
|
153
|
+
console.log(`publish ${config.name}`)
|
|
154
|
+
let data=await compress(config.ignore, process.cwd())
|
|
155
|
+
//token 用全局配置里的 token(注册后由服务器发到邮箱,用户 config set token 配置)
|
|
156
|
+
await net.publish.module(global.username,global.token,{
|
|
157
|
+
dependencies:config.dependency, hex: createHash('sha256').update(data).digest('hex'),
|
|
158
|
+
source: '', version: config.version
|
|
159
|
+
},data)
|
|
160
|
+
}
|
|
161
|
+
async function publish_vm(local:string,version:string,isa:string,license:string){
|
|
162
|
+
let config=global_config()
|
|
163
|
+
let list=await net.list.vm()
|
|
164
|
+
if(!existsSync(local))
|
|
165
|
+
throw new Error(`${local}不存在`)
|
|
166
|
+
if(list.find((item)=>item.version==version))
|
|
167
|
+
throw new Error(`vm-${version}已经存在`)
|
|
168
|
+
console.log(`publish ${local}`)
|
|
169
|
+
let data:Buffer=readFileSync(local)
|
|
170
|
+
await net.publish.vm(config.username,config.token,{
|
|
171
|
+
version, isa, author: config.username, license,source:'',
|
|
172
|
+
hex: createHash('sha256').update(data).digest('hex')
|
|
173
|
+
},data)
|
|
174
|
+
}
|
|
175
|
+
async function create_compiler(license:string,version:string){
|
|
176
|
+
let config=global_config()
|
|
177
|
+
let list=await net.list.compiler()
|
|
178
|
+
if(list.find((item)=>item.version==version))
|
|
179
|
+
throw new Error(`compiler-${version}已经存在`)
|
|
180
|
+
console.log(`create compiler ${version}`)
|
|
181
|
+
await net.publish.compiler_create(config.username,config.token,license,version)
|
|
182
|
+
}
|
|
183
|
+
async function publish_compiler(local:string,large_version:string,small_version:string){
|
|
184
|
+
let config=global_config()
|
|
185
|
+
let list=await net.list.compiler()
|
|
186
|
+
if(!existsSync(local))
|
|
187
|
+
throw new Error(`${local}不存在`)
|
|
188
|
+
if(!list.find((item)=>item.version==large_version))
|
|
189
|
+
throw new Error(`compiler-${large_version}不存在`)
|
|
190
|
+
if(list.find(item=>item.version==large_version).child.find(i=>i.version==small_version))
|
|
191
|
+
throw new Error(`compiler-${large_version}.${small_version}已经存在`)
|
|
192
|
+
console.log(`publish compiler ${large_version}.${small_version}`)
|
|
193
|
+
let data=readFileSync(local,'utf-8')
|
|
194
|
+
await net.publish.compiler(config.username,config.token,large_version,{
|
|
195
|
+
version: small_version, hex: createHash('sha256').update(data).digest('hex'),
|
|
196
|
+
source:''
|
|
197
|
+
},data)
|
|
198
|
+
}
|
|
199
|
+
async function config_set(key:string,value:string){
|
|
200
|
+
if(!['server','username','password','token'].includes(key))
|
|
201
|
+
throw new Error('不存在的配置项')
|
|
202
|
+
let config=global_config()
|
|
203
|
+
config[key]=value
|
|
204
|
+
write_global_config(config)
|
|
205
|
+
}
|
|
206
|
+
async function config_verify(){
|
|
207
|
+
let config=global_config()
|
|
208
|
+
let data=await net.user.verify(config.username,config.token)
|
|
209
|
+
if(data!='')throw new Error(data)
|
|
210
|
+
}
|
|
211
|
+
async function register(username:string,email:string){
|
|
212
|
+
let config=global_config()
|
|
213
|
+
await net.user.register(username,email)
|
|
214
|
+
console.log('注册成功,token已经发送到邮箱')
|
|
215
|
+
}
|
|
216
|
+
function compiler(){
|
|
217
|
+
//读取虚拟环境运行:编译器是 init 时下载到 venv/compiler.js 的 JS 文件,用 node 执行
|
|
218
|
+
let config=project_config(process.cwd())
|
|
219
|
+
let dir=config.venv.dir||'venv'
|
|
220
|
+
spawnSync('node',[path.join(process.cwd(),dir,'compiler.js')
|
|
221
|
+
,'--dir',process.cwd(),'--ignore',config.ignore.map(i=>path.join(process.cwd(),i)).join(';')
|
|
222
|
+
,'--output',path.join(process.cwd(),config.output),'--optimize',config.optimize+''],{
|
|
223
|
+
stdio: 'inherit',
|
|
224
|
+
shell: true
|
|
225
|
+
})
|
|
226
|
+
}
|
|
227
|
+
function run(){
|
|
228
|
+
let config=project_config(process.cwd())
|
|
229
|
+
let dir=config.venv.dir||'venv'
|
|
230
|
+
//VM 是 init 时下载到 venv/vm(.exe) 的二进制,版本号字段不是路径
|
|
231
|
+
spawnSync(path.join(process.cwd(),dir,'vm'+(os.type()=='Windows_NT'?'.exe':'')),['run',path.join(process.cwd(),config.output)],{
|
|
232
|
+
stdio: 'inherit',
|
|
233
|
+
shell: true
|
|
234
|
+
})
|
|
235
|
+
}
|
|
236
|
+
function go(){
|
|
237
|
+
compiler()
|
|
238
|
+
run()
|
|
239
|
+
}
|
|
240
|
+
export default {
|
|
241
|
+
init,
|
|
242
|
+
install,
|
|
243
|
+
uninstall,
|
|
244
|
+
publish_module,
|
|
245
|
+
publish_vm,
|
|
246
|
+
create_compiler,
|
|
247
|
+
publish_compiler,
|
|
248
|
+
config_set,
|
|
249
|
+
config_verify,
|
|
250
|
+
register,
|
|
251
|
+
compiler,
|
|
252
|
+
run,
|
|
253
|
+
go
|
|
254
|
+
}
|