yllaw 1.0.0 → 1.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 CHANGED
@@ -15,10 +15,15 @@ npm install -g yllaw
15
15
  ```bash
16
16
  # 安装所有包(Wally + Git)
17
17
  yllaw install
18
+ yllaw # install 是默认命令,可省略
18
19
 
19
20
  # 只安装 Git 包,跳过 Wally
20
- yllaw install --skip-wally
21
21
  yllaw install -s
22
+ yllaw -s # 等同于 yllaw install -s
23
+
24
+ # 开发模式:从同级目录安装(用于本地开发测试)
25
+ yllaw install -d
26
+ yllaw -d -s # 等同于 yllaw install -d -s
22
27
 
23
28
  # 强制更新所有 Git 包
24
29
  yllaw update
@@ -30,6 +35,32 @@ yllaw install --config my-wally.toml
30
35
  yllaw install --output Packages
31
36
  ```
32
37
 
38
+ > **提示**: `install` 是默认命令,直接使用 `yllaw -s -d` 等同于 `yllaw install -s -d`
39
+
40
+ ## 开发模式 (--dev)
41
+
42
+ 当你在本地开发一个包,想要在另一个项目中测试时,使用 `--dev` 模式:
43
+
44
+ ```
45
+ C:\Workspace\
46
+ ├── my-game/ ← 当前项目
47
+ │ └── wally.toml ← MMS = "...mms-matchmaking.git@1.1.0"
48
+ └── mms-matchmaking/ ← 同级目录
49
+ └── MMS/
50
+ ```
51
+
52
+ ```bash
53
+ cd my-game
54
+ yllaw install -d -s
55
+ ```
56
+
57
+ yllaw 会自动检测 `mms-matchmaking` 在同级目录存在,直接从本地复制,而不是从 Git 克隆。
58
+
59
+ 这样你可以:
60
+ 1. 修改 `mms-matchmaking` 中的代码
61
+ 2. 运行 `yllaw install -d -s` 快速同步到测试项目
62
+ 3. 无需提交、推送、更新版本号
63
+
33
64
  ## 配置
34
65
 
35
66
  在项目根目录创建 `wally.toml`:
@@ -113,6 +144,7 @@ yllaw **补充** Wally,而不是替代:
113
144
  | 选项 | 说明 | 默认值 |
114
145
  |------|------|--------|
115
146
  | `-s, --skip-wally` | 跳过 Wally | false |
147
+ | `-d, --dev` | 开发模式:优先从同级目录安装 | false |
116
148
  | `-c, --config <path>` | 配置文件路径 | `wally.toml` |
117
149
  | `-o, --output <dir>` | 输出目录 | `Packages` |
118
150
 
package/bin/yllaw.js CHANGED
@@ -13,6 +13,7 @@ program
13
13
  .command('install')
14
14
  .description('Install packages from wally.toml')
15
15
  .option('-s, --skip-wally', 'Skip wally install, only install git packages')
16
+ .option('-d, --dev', 'Dev mode: install from local sibling directories if available')
16
17
  .option('-c, --config <path>', 'Path to wally.toml', 'wally.toml')
17
18
  .option('-o, --output <dir>', 'Output directory for packages', 'Packages')
18
19
  .action(async (options) => {
@@ -28,6 +29,7 @@ program
28
29
  .command('update')
29
30
  .description('Update all git packages to latest version (re-clone)')
30
31
  .option('-s, --skip-wally', 'Skip wally install')
32
+ .option('-d, --dev', 'Dev mode: install from local sibling directories if available')
31
33
  .option('-c, --config <path>', 'Path to wally.toml', 'wally.toml')
32
34
  .option('-o, --output <dir>', 'Output directory for packages', 'Packages')
33
35
  .action(async (options) => {
@@ -40,8 +42,14 @@ program
40
42
  });
41
43
 
42
44
  // Default command: install
43
- if (process.argv.length === 2) {
44
- process.argv.push('install');
45
+ // If no known command is provided, insert 'install' as the default
46
+ const knownCommands = ['install', 'update', 'help', '-h', '--help', '-V', '--version'];
47
+ const args = process.argv.slice(2);
48
+ const firstNonOption = args.find(arg => !arg.startsWith('-'));
49
+
50
+ if (!firstNonOption || !knownCommands.includes(firstNonOption)) {
51
+ // Insert 'install' after 'node' and 'yllaw'
52
+ process.argv.splice(2, 0, 'install');
45
53
  }
46
54
 
47
55
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yllaw",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "description": "Package manager for Roblox - supports Wally packages and private Git repositories",
5
5
  "main": "src/index.js",
6
6
  "bin": {
package/src/installer.js CHANGED
@@ -34,20 +34,26 @@ async function runWally() {
34
34
 
35
35
  /**
36
36
  * Parse git spec: "https://example.com/repo.git@version"
37
- * @returns {{ url: string, ref: string }}
37
+ * @returns {{ url: string, ref: string, repoName: string }}
38
38
  */
39
39
  function parseGitSpec(spec) {
40
40
  const gitAtIndex = spec.lastIndexOf('.git@');
41
+ let url, ref;
42
+
41
43
  if (gitAtIndex > 0) {
42
- return {
43
- url: spec.substring(0, gitAtIndex + 4),
44
- ref: spec.substring(gitAtIndex + 5),
45
- };
44
+ url = spec.substring(0, gitAtIndex + 4);
45
+ ref = spec.substring(gitAtIndex + 5);
46
+ } else {
47
+ url = spec;
48
+ ref = 'main';
46
49
  }
47
- return {
48
- url: spec,
49
- ref: 'main',
50
- };
50
+
51
+ // Extract repo name from URL
52
+ // e.g., "https://gitlab.example.com/group/mms-matchmaking.git" -> "mms-matchmaking"
53
+ const repoMatch = url.match(/\/([^\/]+)\.git$/);
54
+ const repoName = repoMatch ? repoMatch[1] : null;
55
+
56
+ return { url, ref, repoName };
51
57
  }
52
58
 
53
59
  /**
@@ -83,6 +89,47 @@ async function findModuleDir(searchDir) {
83
89
  return null;
84
90
  }
85
91
 
92
+ /**
93
+ * Try to install from local sibling directory (dev mode)
94
+ * @returns {boolean} true if installed from local
95
+ */
96
+ async function tryInstallFromLocal(name, spec, outputDir, parentDir) {
97
+ const { repoName } = parseGitSpec(spec);
98
+
99
+ if (!repoName) {
100
+ return false;
101
+ }
102
+
103
+ // Check if sibling directory exists
104
+ const localRepoDir = path.join(parentDir, repoName);
105
+
106
+ if (!await fs.pathExists(localRepoDir)) {
107
+ return false;
108
+ }
109
+
110
+ log.info(`[DEV] Found local: ${localRepoDir}`);
111
+
112
+ // Find module directory in local repo
113
+ const moduleDir = await findModuleDir(localRepoDir);
114
+
115
+ if (!moduleDir) {
116
+ log.warn(`[DEV] No init.luau found in ${localRepoDir}, falling back to git`);
117
+ return false;
118
+ }
119
+
120
+ const target = path.join(outputDir, name);
121
+
122
+ // Copy to output
123
+ await fs.remove(target);
124
+ await fs.copy(moduleDir, target);
125
+
126
+ // Clean up .git if copied
127
+ await fs.remove(path.join(target, '.git'));
128
+
129
+ log.ok(`[DEV] ${name} <- ${moduleDir}`);
130
+ return true;
131
+ }
132
+
86
133
  /**
87
134
  * Install a single git package
88
135
  */
@@ -136,10 +183,11 @@ async function installGitPackage(name, spec, outputDir, tempDir) {
136
183
  * Main install function
137
184
  */
138
185
  async function install(options) {
139
- const { config, output, skipWally, force } = options;
186
+ const { config, output, skipWally, force, dev } = options;
140
187
  const configPath = path.resolve(config);
141
188
  const outputDir = path.resolve(output);
142
189
  const tempDir = path.join(process.cwd(), '.yllaw_temp');
190
+ const parentDir = path.dirname(process.cwd()); // Parent directory for dev mode
143
191
 
144
192
  // Check config exists
145
193
  if (!await fs.pathExists(configPath)) {
@@ -181,17 +229,29 @@ async function install(options) {
181
229
  if (packages.length === 0) {
182
230
  log.info('No git dependencies found');
183
231
  } else {
232
+ if (dev) {
233
+ log.info(`[DEV MODE] Looking for packages in: ${parentDir}`);
234
+ }
184
235
  log.info(`Found ${packages.length} git package(s)`);
185
236
 
186
237
  for (const [name, spec] of packages) {
187
238
  const target = path.join(outputDir, name);
188
239
 
189
- // Skip if exists and not forcing update
190
- if (!force && await fs.pathExists(target)) {
240
+ // Skip if exists and not forcing update (unless dev mode, always update in dev)
241
+ if (!force && !dev && await fs.pathExists(target)) {
191
242
  log.info(`${name} already exists, skipping (use 'yllaw update' to force)`);
192
243
  continue;
193
244
  }
194
245
 
246
+ // Dev mode: try local first
247
+ if (dev) {
248
+ const installedFromLocal = await tryInstallFromLocal(name, spec, outputDir, parentDir);
249
+ if (installedFromLocal) {
250
+ continue;
251
+ }
252
+ log.info(`[DEV] ${name} not found locally, using git`);
253
+ }
254
+
195
255
  await installGitPackage(name, spec, outputDir, tempDir);
196
256
  }
197
257
  }