yllaw 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/README.md ADDED
@@ -0,0 +1,121 @@
1
+ # yllaw
2
+
3
+ Roblox 包管理器 - 支持 Wally 包和私有 Git 仓库。
4
+
5
+ > yllaw = wally 倒过来 🙃
6
+
7
+ ## 安装
8
+
9
+ ```bash
10
+ npm install -g yllaw
11
+ ```
12
+
13
+ ## 使用
14
+
15
+ ```bash
16
+ # 安装所有包(Wally + Git)
17
+ yllaw install
18
+
19
+ # 只安装 Git 包,跳过 Wally
20
+ yllaw install --skip-wally
21
+ yllaw install -s
22
+
23
+ # 强制更新所有 Git 包
24
+ yllaw update
25
+
26
+ # 指定配置文件
27
+ yllaw install --config my-wally.toml
28
+
29
+ # 指定输出目录
30
+ yllaw install --output Packages
31
+ ```
32
+
33
+ ## 配置
34
+
35
+ 在项目根目录创建 `wally.toml`:
36
+
37
+ ```toml
38
+ [package]
39
+ name = "your-scope/your-project"
40
+ version = "0.1.0"
41
+ realm = "shared"
42
+
43
+ [dependencies]
44
+ # Wally 公共包
45
+
46
+ [server-dependencies]
47
+ # Wally 服务端包
48
+
49
+ [git-dependencies]
50
+ # 私有 Git 包(共享)
51
+ CommonLib = "https://gitlab.example.com/group/common-lib.git@1.0.0"
52
+
53
+ [server-git-dependencies]
54
+ # 私有 Git 包(服务端)
55
+ MMS = "https://gitlab.example.com/group/mms.git@1.1.0"
56
+ ```
57
+
58
+ ## Git 包格式
59
+
60
+ ```
61
+ 包名 = "仓库地址.git@版本"
62
+ ```
63
+
64
+ | 部分 | 说明 | 示例 |
65
+ |------|------|------|
66
+ | 仓库地址 | HTTPS Git URL | `https://gitlab.example.com/group/repo.git` |
67
+ | 版本 | Git tag / 分支 / commit | `1.0.0`, `main`, `abc1234` |
68
+
69
+ ## 创建私有包
70
+
71
+ 你的 Git 仓库结构:
72
+
73
+ ```
74
+ your-package/
75
+ ├── README.md
76
+ ├── wally.toml
77
+ └── YourModule/
78
+ ├── init.luau ← 必须有入口文件
79
+ ├── Foo.luau
80
+ └── Bar.luau
81
+ ```
82
+
83
+ yllaw 会自动查找包含 `init.luau` 或 `init.lua` 的目录。
84
+
85
+ ### 发布版本
86
+
87
+ ```bash
88
+ git tag 1.0.0
89
+ git push origin 1.0.0
90
+ ```
91
+
92
+ ## 与 Wally 的关系
93
+
94
+ yllaw **补充** Wally,而不是替代:
95
+
96
+ - Wally 负责公共包和私有 Registry
97
+ - yllaw 负责私有 Git 仓库的包
98
+
99
+ 运行 `yllaw install` 时:
100
+ 1. 先运行 `wally install`
101
+ 2. 再安装 `[git-dependencies]` 和 `[server-git-dependencies]`
102
+
103
+ ## 命令
104
+
105
+ | 命令 | 说明 |
106
+ |------|------|
107
+ | `yllaw install` | 安装所有包 |
108
+ | `yllaw install -s` | 跳过 Wally,只安装 Git 包 |
109
+ | `yllaw update` | 强制重新安装所有 Git 包 |
110
+
111
+ ## 选项
112
+
113
+ | 选项 | 说明 | 默认值 |
114
+ |------|------|--------|
115
+ | `-s, --skip-wally` | 跳过 Wally | false |
116
+ | `-c, --config <path>` | 配置文件路径 | `wally.toml` |
117
+ | `-o, --output <dir>` | 输出目录 | `Packages` |
118
+
119
+ ## License
120
+
121
+ MIT
package/bin/yllaw.js ADDED
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { program } = require('commander');
4
+ const { version } = require('../package.json');
5
+ const { install } = require('../src/installer');
6
+
7
+ program
8
+ .name('yllaw')
9
+ .description('Package manager for Roblox - supports Wally and private Git repositories')
10
+ .version(version);
11
+
12
+ program
13
+ .command('install')
14
+ .description('Install packages from wally.toml')
15
+ .option('-s, --skip-wally', 'Skip wally install, only install git packages')
16
+ .option('-c, --config <path>', 'Path to wally.toml', 'wally.toml')
17
+ .option('-o, --output <dir>', 'Output directory for packages', 'Packages')
18
+ .action(async (options) => {
19
+ try {
20
+ await install(options);
21
+ } catch (err) {
22
+ console.error('Error:', err.message);
23
+ process.exit(1);
24
+ }
25
+ });
26
+
27
+ program
28
+ .command('update')
29
+ .description('Update all git packages to latest version (re-clone)')
30
+ .option('-s, --skip-wally', 'Skip wally install')
31
+ .option('-c, --config <path>', 'Path to wally.toml', 'wally.toml')
32
+ .option('-o, --output <dir>', 'Output directory for packages', 'Packages')
33
+ .action(async (options) => {
34
+ try {
35
+ await install({ ...options, force: true });
36
+ } catch (err) {
37
+ console.error('Error:', err.message);
38
+ process.exit(1);
39
+ }
40
+ });
41
+
42
+ // Default command: install
43
+ if (process.argv.length === 2) {
44
+ process.argv.push('install');
45
+ }
46
+
47
+ program.parse();
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "yllaw",
3
+ "version": "1.0.0",
4
+ "description": "Package manager for Roblox - supports Wally packages and private Git repositories",
5
+ "main": "src/index.js",
6
+ "bin": {
7
+ "yllaw": "./bin/yllaw.js"
8
+ },
9
+ "scripts": {
10
+ "test": "node bin/yllaw.js install"
11
+ },
12
+ "keywords": [
13
+ "roblox",
14
+ "wally",
15
+ "package-manager",
16
+ "luau",
17
+ "lua"
18
+ ],
19
+ "author": "Hauru",
20
+ "license": "MIT",
21
+ "dependencies": {
22
+ "commander": "^11.1.0",
23
+ "simple-git": "^3.22.0",
24
+ "toml": "^3.0.0",
25
+ "chalk": "^4.1.2",
26
+ "fs-extra": "^11.2.0"
27
+ },
28
+ "engines": {
29
+ "node": ">=14.0.0"
30
+ },
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "https://gitlab.showhand.win/game_develop/yllaw.git"
34
+ }
35
+ }
@@ -0,0 +1,206 @@
1
+ const fs = require('fs-extra');
2
+ const path = require('path');
3
+ const toml = require('toml');
4
+ const simpleGit = require('simple-git');
5
+ const { spawn } = require('child_process');
6
+ const chalk = require('chalk');
7
+
8
+ const log = {
9
+ info: (msg) => console.log(chalk.blue('[INFO]'), msg),
10
+ ok: (msg) => console.log(chalk.green('[OK]'), msg),
11
+ warn: (msg) => console.log(chalk.yellow('[WARN]'), msg),
12
+ error: (msg) => console.log(chalk.red('[ERROR]'), msg),
13
+ };
14
+
15
+ /**
16
+ * Run wally install
17
+ */
18
+ async function runWally() {
19
+ return new Promise((resolve) => {
20
+ const wally = spawn('wally', ['install'], {
21
+ stdio: 'inherit',
22
+ shell: true,
23
+ });
24
+
25
+ wally.on('close', (code) => {
26
+ resolve(code === 0);
27
+ });
28
+
29
+ wally.on('error', () => {
30
+ resolve(false);
31
+ });
32
+ });
33
+ }
34
+
35
+ /**
36
+ * Parse git spec: "https://example.com/repo.git@version"
37
+ * @returns {{ url: string, ref: string }}
38
+ */
39
+ function parseGitSpec(spec) {
40
+ const gitAtIndex = spec.lastIndexOf('.git@');
41
+ if (gitAtIndex > 0) {
42
+ return {
43
+ url: spec.substring(0, gitAtIndex + 4),
44
+ ref: spec.substring(gitAtIndex + 5),
45
+ };
46
+ }
47
+ return {
48
+ url: spec,
49
+ ref: 'main',
50
+ };
51
+ }
52
+
53
+ /**
54
+ * Find module directory (containing init.luau or init.lua)
55
+ */
56
+ async function findModuleDir(searchDir) {
57
+ // Check root
58
+ if (await fs.pathExists(path.join(searchDir, 'init.luau')) ||
59
+ await fs.pathExists(path.join(searchDir, 'init.lua'))) {
60
+ return searchDir;
61
+ }
62
+
63
+ // Check immediate subdirectories
64
+ const entries = await fs.readdir(searchDir, { withFileTypes: true });
65
+ for (const entry of entries) {
66
+ if (entry.isDirectory()) {
67
+ const subDir = path.join(searchDir, entry.name);
68
+ if (await fs.pathExists(path.join(subDir, 'init.luau')) ||
69
+ await fs.pathExists(path.join(subDir, 'init.lua'))) {
70
+ return subDir;
71
+ }
72
+ }
73
+ }
74
+
75
+ // Deep search (fallback)
76
+ for (const entry of entries) {
77
+ if (entry.isDirectory() && !entry.name.startsWith('.')) {
78
+ const found = await findModuleDir(path.join(searchDir, entry.name));
79
+ if (found) return found;
80
+ }
81
+ }
82
+
83
+ return null;
84
+ }
85
+
86
+ /**
87
+ * Install a single git package
88
+ */
89
+ async function installGitPackage(name, spec, outputDir, tempDir) {
90
+ const { url, ref } = parseGitSpec(spec);
91
+ const target = path.join(outputDir, name);
92
+ const repoDir = path.join(tempDir, 'repo');
93
+
94
+ log.info(`Installing ${name} @ ${ref}`);
95
+
96
+ // Clean temp dir
97
+ await fs.remove(tempDir);
98
+ await fs.ensureDir(tempDir);
99
+
100
+ // Clone
101
+ const git = simpleGit();
102
+ try {
103
+ await git.clone(url, repoDir, ['--depth', '1', '--branch', ref]);
104
+ } catch (err) {
105
+ // Fallback: clone default branch and checkout
106
+ try {
107
+ await git.clone(url, repoDir, ['--depth', '1']);
108
+ const repoGit = simpleGit(repoDir);
109
+ await repoGit.fetch(['origin', ref, '--depth', '1']);
110
+ await repoGit.checkout('FETCH_HEAD');
111
+ } catch (err2) {
112
+ log.error(`Clone failed: ${name} - ${err2.message}`);
113
+ return false;
114
+ }
115
+ }
116
+
117
+ // Find module directory
118
+ const moduleDir = await findModuleDir(repoDir);
119
+ if (!moduleDir) {
120
+ log.error(`No init.luau found in ${name}`);
121
+ return false;
122
+ }
123
+
124
+ // Copy to output
125
+ await fs.remove(target);
126
+ await fs.copy(moduleDir, target);
127
+
128
+ // Clean up .git if copied
129
+ await fs.remove(path.join(target, '.git'));
130
+
131
+ log.ok(`${name} -> ${target}`);
132
+ return true;
133
+ }
134
+
135
+ /**
136
+ * Main install function
137
+ */
138
+ async function install(options) {
139
+ const { config, output, skipWally, force } = options;
140
+ const configPath = path.resolve(config);
141
+ const outputDir = path.resolve(output);
142
+ const tempDir = path.join(process.cwd(), '.yllaw_temp');
143
+
144
+ // Check config exists
145
+ if (!await fs.pathExists(configPath)) {
146
+ throw new Error(`Config not found: ${configPath}`);
147
+ }
148
+
149
+ log.info(`Config: ${configPath}`);
150
+ await fs.ensureDir(outputDir);
151
+
152
+ // Step 1: Wally install
153
+ if (skipWally) {
154
+ log.info('Skipping wally (--skip-wally)');
155
+ } else {
156
+ log.info('Running wally install...');
157
+ const wallyOk = await runWally();
158
+ if (wallyOk) {
159
+ log.ok('Wally done');
160
+ } else {
161
+ log.warn('Wally skipped or not found');
162
+ }
163
+ }
164
+
165
+ // Step 2: Parse config
166
+ const configContent = await fs.readFile(configPath, 'utf-8');
167
+ let parsed;
168
+ try {
169
+ parsed = toml.parse(configContent);
170
+ } catch (err) {
171
+ throw new Error(`Failed to parse ${config}: ${err.message}`);
172
+ }
173
+
174
+ // Step 3: Install git dependencies
175
+ const gitDeps = {
176
+ ...parsed['git-dependencies'],
177
+ ...parsed['server-git-dependencies'],
178
+ };
179
+
180
+ const packages = Object.entries(gitDeps);
181
+ if (packages.length === 0) {
182
+ log.info('No git dependencies found');
183
+ } else {
184
+ log.info(`Found ${packages.length} git package(s)`);
185
+
186
+ for (const [name, spec] of packages) {
187
+ const target = path.join(outputDir, name);
188
+
189
+ // Skip if exists and not forcing update
190
+ if (!force && await fs.pathExists(target)) {
191
+ log.info(`${name} already exists, skipping (use 'yllaw update' to force)`);
192
+ continue;
193
+ }
194
+
195
+ await installGitPackage(name, spec, outputDir, tempDir);
196
+ }
197
+ }
198
+
199
+ // Cleanup
200
+ await fs.remove(tempDir);
201
+
202
+ console.log('');
203
+ log.ok(`Done! Packages installed to ${outputDir}`);
204
+ }
205
+
206
+ module.exports = { install };