@ryanatkn/gro 0.129.4 → 0.129.5
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/dist/package.js +3 -3
- package/package.json +3 -2
- package/src/lib/args.test.ts +59 -0
- package/src/lib/args.ts +169 -0
- package/src/lib/build.task.ts +37 -0
- package/src/lib/changelog.test.ts +138 -0
- package/src/lib/changelog.ts +69 -0
- package/src/lib/changeset.task.ts +206 -0
- package/src/lib/changeset_helpers.ts +13 -0
- package/src/lib/check.task.ts +90 -0
- package/src/lib/clean.task.ts +46 -0
- package/src/lib/clean_fs.ts +54 -0
- package/src/lib/cli.ts +97 -0
- package/src/lib/commit.task.ts +33 -0
- package/src/lib/config.test.ts +71 -0
- package/src/lib/config.ts +161 -0
- package/src/lib/deploy.task.ts +243 -0
- package/src/lib/dev.task.ts +43 -0
- package/src/lib/docs/README.gen.md.ts +63 -0
- package/src/lib/docs/README.md +20 -0
- package/src/lib/docs/build.md +41 -0
- package/src/lib/docs/config.md +213 -0
- package/src/lib/docs/deploy.md +32 -0
- package/src/lib/docs/dev.md +40 -0
- package/src/lib/docs/gen.md +269 -0
- package/src/lib/docs/gro_plugin_sveltekit_app.md +113 -0
- package/src/lib/docs/package_json.md +33 -0
- package/src/lib/docs/plugin.md +50 -0
- package/src/lib/docs/publish.md +137 -0
- package/src/lib/docs/task.md +391 -0
- package/src/lib/docs/tasks.gen.md.ts +90 -0
- package/src/lib/docs/tasks.md +37 -0
- package/src/lib/docs/test.md +52 -0
- package/src/lib/env.ts +75 -0
- package/src/lib/esbuild_helpers.ts +50 -0
- package/src/lib/esbuild_plugin_external_worker.ts +92 -0
- package/src/lib/esbuild_plugin_svelte.test.ts +88 -0
- package/src/lib/esbuild_plugin_svelte.ts +108 -0
- package/src/lib/esbuild_plugin_sveltekit_local_imports.ts +31 -0
- package/src/lib/esbuild_plugin_sveltekit_shim_alias.ts +25 -0
- package/src/lib/esbuild_plugin_sveltekit_shim_app.ts +41 -0
- package/src/lib/esbuild_plugin_sveltekit_shim_env.ts +46 -0
- package/src/lib/format.task.ts +30 -0
- package/src/lib/format_directory.ts +55 -0
- package/src/lib/format_file.test.ts +20 -0
- package/src/lib/format_file.ts +49 -0
- package/src/lib/fs.ts +18 -0
- package/src/lib/gen.task.ts +134 -0
- package/src/lib/gen.test.ts +306 -0
- package/src/lib/gen.ts +360 -0
- package/src/lib/git.test.ts +34 -0
- package/src/lib/git.ts +297 -0
- package/src/lib/github.ts +46 -0
- package/src/lib/gro.config.default.ts +34 -0
- package/src/lib/gro.ts +25 -0
- package/src/lib/gro_helpers.ts +101 -0
- package/src/lib/gro_plugin_gen.ts +95 -0
- package/src/lib/gro_plugin_server.ts +288 -0
- package/src/lib/gro_plugin_sveltekit_app.ts +257 -0
- package/src/lib/gro_plugin_sveltekit_library.ts +74 -0
- package/src/lib/hash.test.ts +33 -0
- package/src/lib/hash.ts +19 -0
- package/src/lib/index.ts +4 -0
- package/src/lib/input_path.test.ts +230 -0
- package/src/lib/input_path.ts +255 -0
- package/src/lib/invoke.ts +27 -0
- package/src/lib/invoke_task.ts +116 -0
- package/src/lib/lint.task.ts +38 -0
- package/src/lib/loader.test.ts +49 -0
- package/src/lib/loader.ts +226 -0
- package/src/lib/module.test.ts +46 -0
- package/src/lib/module.ts +13 -0
- package/src/lib/modules.test.ts +63 -0
- package/src/lib/modules.ts +112 -0
- package/src/lib/package.gen.ts +33 -0
- package/src/lib/package.ts +998 -0
- package/src/lib/package_json.test.ts +101 -0
- package/src/lib/package_json.ts +330 -0
- package/src/lib/package_meta.ts +86 -0
- package/src/lib/path.ts +23 -0
- package/src/lib/path_constants.ts +30 -0
- package/src/lib/paths.test.ts +77 -0
- package/src/lib/paths.ts +101 -0
- package/src/lib/plugin.test.ts +57 -0
- package/src/lib/plugin.ts +113 -0
- package/src/lib/publish.task.ts +194 -0
- package/src/lib/register.ts +3 -0
- package/src/lib/reinstall.task.ts +42 -0
- package/src/lib/release.task.ts +21 -0
- package/src/lib/resolve.task.ts +43 -0
- package/src/lib/resolve_node_specifier.test.ts +31 -0
- package/src/lib/resolve_node_specifier.ts +55 -0
- package/src/lib/resolve_specifier.test.ts +76 -0
- package/src/lib/resolve_specifier.ts +61 -0
- package/src/lib/run.task.ts +41 -0
- package/src/lib/run_gen.test.ts +196 -0
- package/src/lib/run_gen.ts +95 -0
- package/src/lib/run_task.test.ts +86 -0
- package/src/lib/run_task.ts +75 -0
- package/src/lib/search_fs.test.ts +56 -0
- package/src/lib/search_fs.ts +93 -0
- package/src/lib/src_json.test.ts +49 -0
- package/src/lib/src_json.ts +153 -0
- package/src/lib/svelte_helpers.ts +2 -0
- package/src/lib/sveltekit_config.ts +101 -0
- package/src/lib/sveltekit_config_global.ts +6 -0
- package/src/lib/sveltekit_helpers.ts +132 -0
- package/src/lib/sveltekit_shim_app.ts +42 -0
- package/src/lib/sveltekit_shim_app_environment.ts +14 -0
- package/src/lib/sveltekit_shim_app_forms.ts +20 -0
- package/src/lib/sveltekit_shim_app_navigation.ts +23 -0
- package/src/lib/sveltekit_shim_app_paths.ts +16 -0
- package/src/lib/sveltekit_shim_app_stores.ts +25 -0
- package/src/lib/sveltekit_shim_env.ts +45 -0
- package/src/lib/sync.task.ts +47 -0
- package/src/lib/task.test.ts +84 -0
- package/src/lib/task.ts +235 -0
- package/src/lib/task_logging.ts +180 -0
- package/src/lib/test.task.ts +50 -0
- package/src/lib/throttle.test.ts +52 -0
- package/src/lib/throttle.ts +63 -0
- package/src/lib/typecheck.task.ts +57 -0
- package/src/lib/upgrade.task.ts +108 -0
- package/src/lib/watch_dir.ts +88 -0
package/src/lib/git.ts
ADDED
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import {spawn, spawn_out} from '@ryanatkn/belt/process.js';
|
|
2
|
+
import type {SpawnOptions} from 'node:child_process';
|
|
3
|
+
import {z} from 'zod';
|
|
4
|
+
import {existsSync} from 'node:fs';
|
|
5
|
+
|
|
6
|
+
import {to_file_path} from './path.js';
|
|
7
|
+
|
|
8
|
+
// TODO maybe extract to `util-git`
|
|
9
|
+
|
|
10
|
+
export const Git_Origin = z.string().brand('Git_Origin');
|
|
11
|
+
export type Git_Origin = z.infer<typeof Git_Origin>;
|
|
12
|
+
|
|
13
|
+
export const Git_Branch = z.string().brand('Git_Branch');
|
|
14
|
+
export type Git_Branch = z.infer<typeof Git_Branch>;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Returns the current git branch name or throws if something goes wrong.
|
|
18
|
+
*/
|
|
19
|
+
export const git_current_branch_name = async (options?: SpawnOptions): Promise<Git_Branch> => {
|
|
20
|
+
const {stdout} = await spawn_out('git', ['rev-parse', '--abbrev-ref', 'HEAD'], options);
|
|
21
|
+
if (!stdout) throw Error('git_current_branch_name failed');
|
|
22
|
+
const branch_name = stdout.toString().trim() as Git_Branch;
|
|
23
|
+
return branch_name;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @returns a boolean indicating if the remote git branch exists
|
|
28
|
+
*/
|
|
29
|
+
export const git_remote_branch_exists = async (
|
|
30
|
+
origin: Git_Origin = 'origin' as Git_Origin,
|
|
31
|
+
branch?: Git_Branch,
|
|
32
|
+
options?: SpawnOptions,
|
|
33
|
+
): Promise<boolean> => {
|
|
34
|
+
const final_branch = branch ?? (await git_current_branch_name(options));
|
|
35
|
+
if (options?.cwd && !existsSync(to_file_path(options.cwd))) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
const result = await spawn(
|
|
39
|
+
'git',
|
|
40
|
+
['ls-remote', '--exit-code', '--heads', origin, 'refs/heads/' + final_branch],
|
|
41
|
+
options,
|
|
42
|
+
);
|
|
43
|
+
if (result.ok) {
|
|
44
|
+
return true;
|
|
45
|
+
} else if (result.code === 2) {
|
|
46
|
+
return false;
|
|
47
|
+
} else {
|
|
48
|
+
throw Error(
|
|
49
|
+
`git_remote_branch_exists failed for origin '${origin}' and branch '${final_branch}' with code ${result.code}`,
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @returns a boolean indicating if the local git branch exists
|
|
56
|
+
*/
|
|
57
|
+
export const git_local_branch_exists = async (
|
|
58
|
+
branch: Git_Branch,
|
|
59
|
+
options?: SpawnOptions,
|
|
60
|
+
): Promise<boolean> => {
|
|
61
|
+
if (options?.cwd && !existsSync(to_file_path(options.cwd))) {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
const result = await spawn('git', ['show-ref', '--quiet', 'refs/heads/' + branch], options);
|
|
65
|
+
return result.ok;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* TODO make this return an enum and separate the text into a different function
|
|
70
|
+
* @returns an error message if the git workspace has any unstaged or uncommitted changes, or `null` if it's clean
|
|
71
|
+
*/
|
|
72
|
+
export const git_check_clean_workspace = async (options?: SpawnOptions): Promise<string | null> => {
|
|
73
|
+
const unstaged_result = await spawn('git', ['diff', '--exit-code', '--quiet'], options);
|
|
74
|
+
if (!unstaged_result.ok) {
|
|
75
|
+
return 'git has unstaged changes';
|
|
76
|
+
}
|
|
77
|
+
const staged_result = await spawn('git', ['diff', '--exit-code', '--cached', '--quiet'], options);
|
|
78
|
+
if (!staged_result.ok) {
|
|
79
|
+
return 'git has staged but uncommitted changes';
|
|
80
|
+
}
|
|
81
|
+
const status_result = await spawn_out('git', ['status', '--porcelain'], options);
|
|
82
|
+
if (status_result.stdout?.length) {
|
|
83
|
+
return 'git has untracked files';
|
|
84
|
+
}
|
|
85
|
+
return null;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* TODO make this return an enum and separate the text into a different function
|
|
90
|
+
* @returns an error message if the git workspace has any unstaged stages, or `null` if it's clean
|
|
91
|
+
*/
|
|
92
|
+
export const git_check_fully_staged_workspace = async (
|
|
93
|
+
options?: SpawnOptions,
|
|
94
|
+
): Promise<string | null> => {
|
|
95
|
+
const unstaged_result = await spawn('git', ['diff', '--exit-code', '--quiet'], options);
|
|
96
|
+
if (!unstaged_result.ok) {
|
|
97
|
+
return 'git has unstaged changes';
|
|
98
|
+
}
|
|
99
|
+
const status_result = await spawn_out('git', ['status', '--porcelain'], options);
|
|
100
|
+
if (status_result.stdout?.includes('??')) {
|
|
101
|
+
return 'git has untracked files';
|
|
102
|
+
}
|
|
103
|
+
return null;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Calls `git fetch` and throws if anything goes wrong.
|
|
108
|
+
*/
|
|
109
|
+
export const git_fetch = async (
|
|
110
|
+
origin: Git_Origin = 'origin' as Git_Origin,
|
|
111
|
+
branch?: Git_Branch,
|
|
112
|
+
options?: SpawnOptions,
|
|
113
|
+
): Promise<void> => {
|
|
114
|
+
const args = ['fetch', origin];
|
|
115
|
+
if (branch) args.push(branch);
|
|
116
|
+
const result = await spawn('git', args, options);
|
|
117
|
+
if (!result.ok) {
|
|
118
|
+
throw Error(
|
|
119
|
+
`git_fetch failed for origin '${origin}' and branch '${branch}' with code ${result.code}`,
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Calls `git checkout` and throws if anything goes wrong.
|
|
126
|
+
* @returns the previous branch name, if it changed
|
|
127
|
+
*/
|
|
128
|
+
export const git_checkout = async (
|
|
129
|
+
branch: Git_Branch,
|
|
130
|
+
options?: SpawnOptions,
|
|
131
|
+
): Promise<Git_Branch | null> => {
|
|
132
|
+
const current_branch = await git_current_branch_name(options);
|
|
133
|
+
if (branch === current_branch) {
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
const result = await spawn('git', ['checkout', branch], options);
|
|
137
|
+
if (!result.ok) {
|
|
138
|
+
throw Error(`git_checkout failed for branch '${branch}' with code ${result.code}`);
|
|
139
|
+
}
|
|
140
|
+
return current_branch;
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Calls `git pull` and throws if anything goes wrong.
|
|
145
|
+
*/
|
|
146
|
+
export const git_pull = async (
|
|
147
|
+
origin: Git_Origin = 'origin' as Git_Origin,
|
|
148
|
+
branch?: Git_Branch,
|
|
149
|
+
options?: SpawnOptions,
|
|
150
|
+
): Promise<void> => {
|
|
151
|
+
const args = ['pull', origin];
|
|
152
|
+
if (branch) args.push(branch);
|
|
153
|
+
const result = await spawn('git', args, options);
|
|
154
|
+
if (!result.ok) {
|
|
155
|
+
throw Error(`git_pull failed for branch '${branch}' with code ${result.code}`);
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Calls `git push` and throws if anything goes wrong.
|
|
161
|
+
*/
|
|
162
|
+
export const git_push = async (
|
|
163
|
+
origin: Git_Origin,
|
|
164
|
+
branch?: Git_Branch,
|
|
165
|
+
options?: SpawnOptions,
|
|
166
|
+
set_upstream = false,
|
|
167
|
+
): Promise<void> => {
|
|
168
|
+
const final_branch = branch ?? (await git_current_branch_name(options));
|
|
169
|
+
const args = ['push', origin, final_branch];
|
|
170
|
+
if (set_upstream) args.push('-u');
|
|
171
|
+
const result = await spawn('git', args, options);
|
|
172
|
+
if (!result.ok) {
|
|
173
|
+
throw Error(`git_push failed for branch '${final_branch}' with code ${result.code}`);
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Calls `git push` and throws if anything goes wrong.
|
|
179
|
+
*/
|
|
180
|
+
export const git_push_to_create = async (
|
|
181
|
+
origin: Git_Origin = 'origin' as Git_Origin,
|
|
182
|
+
branch?: Git_Branch,
|
|
183
|
+
options?: SpawnOptions,
|
|
184
|
+
): Promise<void> => {
|
|
185
|
+
const final_branch = branch ?? (await git_current_branch_name(options));
|
|
186
|
+
const push_args = ['push'];
|
|
187
|
+
if (await git_remote_branch_exists(origin, final_branch, options)) {
|
|
188
|
+
push_args.push(origin);
|
|
189
|
+
} else {
|
|
190
|
+
push_args.push('-u', origin);
|
|
191
|
+
}
|
|
192
|
+
push_args.push(final_branch);
|
|
193
|
+
const result = await spawn('git', push_args, options);
|
|
194
|
+
if (!result.ok) {
|
|
195
|
+
throw Error(`git_push failed for branch '${final_branch}' with code ${result.code}`);
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Deletes a branch locally and throws if anything goes wrong.
|
|
201
|
+
*/
|
|
202
|
+
export const git_delete_local_branch = async (
|
|
203
|
+
branch: Git_Branch,
|
|
204
|
+
options?: SpawnOptions,
|
|
205
|
+
): Promise<void> => {
|
|
206
|
+
const result = await spawn('git', ['branch', '-D', branch], options);
|
|
207
|
+
if (!result.ok) {
|
|
208
|
+
throw Error(`git_delete_local_branch failed for branch '${branch}' with code ${result.code}`);
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Deletes a branch remotely and throws if anything goes wrong.
|
|
214
|
+
*/
|
|
215
|
+
export const git_delete_remote_branch = async (
|
|
216
|
+
origin: Git_Origin,
|
|
217
|
+
branch: Git_Branch,
|
|
218
|
+
options?: SpawnOptions,
|
|
219
|
+
): Promise<void> => {
|
|
220
|
+
const result = await spawn('git', ['push', origin, ':' + branch], options);
|
|
221
|
+
if (!result.ok) {
|
|
222
|
+
throw Error(`git_delete_remote_branch failed for branch '${branch}' with code ${result.code}`);
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Resets the `target` branch back to its first commit both locally and remotely.
|
|
228
|
+
*/
|
|
229
|
+
export const git_reset_branch_to_first_commit = async (
|
|
230
|
+
origin: Git_Origin,
|
|
231
|
+
branch: Git_Branch,
|
|
232
|
+
options?: SpawnOptions,
|
|
233
|
+
): Promise<void> => {
|
|
234
|
+
const previous_branch = await git_checkout(branch, options);
|
|
235
|
+
const first_commit_hash = await git_current_branch_first_commit_hash(options);
|
|
236
|
+
await spawn('git', ['reset', '--hard', first_commit_hash], options);
|
|
237
|
+
await spawn('git', ['push', origin, branch, '--force'], options);
|
|
238
|
+
if (previous_branch) {
|
|
239
|
+
await git_checkout(previous_branch, options);
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Returns the branch's latest commit hash or throws if something goes wrong.
|
|
245
|
+
*/
|
|
246
|
+
export const git_current_commit_hash = async (
|
|
247
|
+
branch?: string,
|
|
248
|
+
options?: SpawnOptions,
|
|
249
|
+
): Promise<string | null> => {
|
|
250
|
+
const final_branch = branch ?? (await git_current_branch_name(options));
|
|
251
|
+
const {stdout} = await spawn_out('git', ['show-ref', '-s', final_branch], options);
|
|
252
|
+
if (!stdout) return null; // TODO hack for CI
|
|
253
|
+
return stdout.toString().split('\n')[0].trim();
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Returns the hash of the current branch's first commit or throws if something goes wrong.
|
|
258
|
+
*/
|
|
259
|
+
export const git_current_branch_first_commit_hash = async (
|
|
260
|
+
options?: SpawnOptions,
|
|
261
|
+
): Promise<string> => {
|
|
262
|
+
const {stdout} = await spawn_out(
|
|
263
|
+
'git',
|
|
264
|
+
['rev-list', '--max-parents=0', '--abbrev-commit', 'HEAD'],
|
|
265
|
+
options,
|
|
266
|
+
);
|
|
267
|
+
if (!stdout) throw Error('git_current_branch_first_commit_hash failed');
|
|
268
|
+
return stdout.toString().trim();
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Returns the global git config setting for `pull.rebase`.
|
|
273
|
+
* Gro is currently written to expect `true`,
|
|
274
|
+
* but the restriction could be loosened with additional work.
|
|
275
|
+
*/
|
|
276
|
+
export const git_check_setting_pull_rebase = async (options?: SpawnOptions): Promise<boolean> => {
|
|
277
|
+
const value = await spawn_out('git', ['config', '--global', 'pull.rebase'], options);
|
|
278
|
+
return value.stdout?.trim() === 'true';
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Clones a branch locally to another directory and updates the origin to match the source.
|
|
283
|
+
*/
|
|
284
|
+
export const git_clone_locally = async (
|
|
285
|
+
origin: Git_Origin,
|
|
286
|
+
branch: Git_Branch,
|
|
287
|
+
source_dir: string,
|
|
288
|
+
target_dir: string,
|
|
289
|
+
options?: SpawnOptions,
|
|
290
|
+
): Promise<void> => {
|
|
291
|
+
await spawn('git', ['clone', '-b', branch, '--single-branch', source_dir, target_dir], options);
|
|
292
|
+
const origin_url = (
|
|
293
|
+
await spawn_out('git', ['remote', 'get-url', origin], {...options, cwd: source_dir})
|
|
294
|
+
).stdout?.trim();
|
|
295
|
+
if (!origin_url) throw Error('Failed to get the origin url with git in ' + source_dir);
|
|
296
|
+
await spawn('git', ['remote', 'set-url', origin, origin_url], {...options, cwd: target_dir});
|
|
297
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// TODO if this grows at all, use `@octokit/request`,
|
|
2
|
+
// for now it's just calling a single endpoint so we do it manually
|
|
3
|
+
// and we specify just the types we need
|
|
4
|
+
|
|
5
|
+
import {Fetch_Value_Cache, fetch_value} from '@ryanatkn/belt/fetch.js';
|
|
6
|
+
import type {Logger} from '@ryanatkn/belt/log.js';
|
|
7
|
+
import {z} from 'zod';
|
|
8
|
+
|
|
9
|
+
export const GITHUB_REPO_MATCHER = /.+github.com\/(.+)\/(.+)/u;
|
|
10
|
+
|
|
11
|
+
export const Github_Pull_Request = z.object({
|
|
12
|
+
url: z.string(),
|
|
13
|
+
id: z.number(),
|
|
14
|
+
html_url: z.string(),
|
|
15
|
+
number: z.number(),
|
|
16
|
+
user: z.object({
|
|
17
|
+
login: z.string(),
|
|
18
|
+
}),
|
|
19
|
+
});
|
|
20
|
+
export type Github_Pull_Request = z.infer<typeof Github_Pull_Request>;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @see https://docs.github.com/en/rest/commits/commits?apiVersion=2022-11-28#list-pull-requests-associated-with-a-commit
|
|
24
|
+
*/
|
|
25
|
+
export const github_fetch_commit_prs = async (
|
|
26
|
+
owner: string,
|
|
27
|
+
repo: string,
|
|
28
|
+
commit_sha: string,
|
|
29
|
+
token?: string,
|
|
30
|
+
log?: Logger,
|
|
31
|
+
cache?: Fetch_Value_Cache,
|
|
32
|
+
api_version?: string,
|
|
33
|
+
): Promise<Github_Pull_Request[] | null> => {
|
|
34
|
+
const headers = api_version ? new Headers({'x-github-api-version': api_version}) : undefined;
|
|
35
|
+
const url = `https://api.github.com/repos/${owner}/${repo}/commits/${commit_sha}/pulls`;
|
|
36
|
+
const fetched = await fetch_value(url, {
|
|
37
|
+
request: {headers},
|
|
38
|
+
parse: (v: any[]) => v.map((p) => Github_Pull_Request.parse(p)),
|
|
39
|
+
token,
|
|
40
|
+
cache,
|
|
41
|
+
return_early_from_cache: true,
|
|
42
|
+
log,
|
|
43
|
+
});
|
|
44
|
+
if (!fetched.ok) return null;
|
|
45
|
+
return fetched.value;
|
|
46
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type {Create_Gro_Config} from './config.js';
|
|
2
|
+
import {gro_plugin_sveltekit_library} from './gro_plugin_sveltekit_library.js';
|
|
3
|
+
import {has_server, gro_plugin_server} from './gro_plugin_server.js';
|
|
4
|
+
import {gro_plugin_sveltekit_app} from './gro_plugin_sveltekit_app.js';
|
|
5
|
+
import {has_sveltekit_app, has_sveltekit_library} from './sveltekit_helpers.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* This is the default config that's passed to `gro.config.ts`
|
|
9
|
+
* if it exists in the current project, and if not, this is the final config.
|
|
10
|
+
* It looks at the project and tries to do the right thing:
|
|
11
|
+
*
|
|
12
|
+
* - if `src/routes`, assumes a SvelteKit frontend
|
|
13
|
+
* - if `src/lib`, assumes a Node library
|
|
14
|
+
* - if `src/lib/server/server.ts`, assumes a Node server
|
|
15
|
+
*/
|
|
16
|
+
const config: Create_Gro_Config = async (cfg) => {
|
|
17
|
+
const [has_sveltekit_library_result, has_server_result, has_sveltekit_app_result] =
|
|
18
|
+
await Promise.all([has_sveltekit_library(), has_server(), has_sveltekit_app()]);
|
|
19
|
+
|
|
20
|
+
cfg.plugins = () => [
|
|
21
|
+
has_sveltekit_library_result.ok ? gro_plugin_sveltekit_library() : null,
|
|
22
|
+
has_server_result.ok ? gro_plugin_server() : null,
|
|
23
|
+
has_sveltekit_app_result.ok
|
|
24
|
+
? gro_plugin_sveltekit_app({host_target: has_server_result.ok ? 'node' : 'github_pages'})
|
|
25
|
+
: null,
|
|
26
|
+
// TODO replace with an esbuild plugin, see the module for more
|
|
27
|
+
// import {gro_plugin_gen} from './gro_plugin_gen.js';
|
|
28
|
+
// gro_plugin_gen(),
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
return cfg;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export default config;
|
package/src/lib/gro.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import {join} from 'node:path';
|
|
4
|
+
|
|
5
|
+
import {resolve_gro_module_path, spawn_with_loader} from './gro_helpers.js';
|
|
6
|
+
|
|
7
|
+
/*
|
|
8
|
+
|
|
9
|
+
This file is a loader for the Gro CLI.
|
|
10
|
+
Its only purpose is to import the `invoke.js` script in the correct directory.
|
|
11
|
+
By using `resolve_gro_module_path` it lets the global Gro CLI defer
|
|
12
|
+
to a local installation of Gro if one is available,
|
|
13
|
+
and it also provides special handling for the case
|
|
14
|
+
where we're running Gro inside Gro's own repo for development.
|
|
15
|
+
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const invoke_path = resolve_gro_module_path('invoke.js');
|
|
19
|
+
|
|
20
|
+
const loader_path = join(invoke_path, '../loader.js');
|
|
21
|
+
|
|
22
|
+
const spawned = await spawn_with_loader(loader_path, invoke_path, process.argv.slice(2));
|
|
23
|
+
if (!spawned.ok) {
|
|
24
|
+
process.exit(spawned.code || 1); // eslint-disable-line @typescript-eslint/prefer-nullish-coalescing
|
|
25
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import {realpathSync, existsSync} from 'node:fs';
|
|
2
|
+
import {join, resolve} from 'node:path';
|
|
3
|
+
import {fileURLToPath} from 'node:url';
|
|
4
|
+
import {spawn, type Spawn_Result} from '@ryanatkn/belt/process.js';
|
|
5
|
+
|
|
6
|
+
import {NODE_MODULES_DIRNAME, SVELTEKIT_DIST_DIRNAME} from './path_constants.js';
|
|
7
|
+
|
|
8
|
+
/*
|
|
9
|
+
|
|
10
|
+
This module is intended to have minimal dependencies to avoid over-imports in the CLI.
|
|
11
|
+
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Resolves a path to an internal Gro file.
|
|
16
|
+
* Prefers any local installation of Gro and falls back to the current CLI context.
|
|
17
|
+
*
|
|
18
|
+
* Uses heuristics to find `path`, so may fail in some rare corner cases.
|
|
19
|
+
* Currently looks for `gro.js` as a sibling to the `path` arg for detection.
|
|
20
|
+
* If this fails for your usecases, rename `gro.js` or open an issue/PR!
|
|
21
|
+
*
|
|
22
|
+
* Used by the CLI and `gro run`.
|
|
23
|
+
*
|
|
24
|
+
* case 1:
|
|
25
|
+
*
|
|
26
|
+
* We're in a directory that has a local installation of Gro at `node_modules/.bin/gro`.
|
|
27
|
+
* Use this local version instead of the global.
|
|
28
|
+
*
|
|
29
|
+
* case 2:
|
|
30
|
+
*
|
|
31
|
+
* We're running Gro inside the Gro repo itself.
|
|
32
|
+
*
|
|
33
|
+
* In this case, we use the build directory instead of dist.
|
|
34
|
+
* There's a paradox here for using Gro inside itself -
|
|
35
|
+
* ideally we use the dist directory because that's what's shipped,
|
|
36
|
+
* but the build directory has all of the tests,
|
|
37
|
+
* and loading two instances of its modules causes problems
|
|
38
|
+
* like `instanceof` checks failing.
|
|
39
|
+
* For now we'll just run from build and see if it causes any problems.
|
|
40
|
+
* There's probably a better design in here somewhere.
|
|
41
|
+
*
|
|
42
|
+
* case 3:
|
|
43
|
+
*
|
|
44
|
+
* Fall back to invoking Gro from wherever the CLI is being executed.
|
|
45
|
+
* When using the global CLI, this uses the global Gro installation.
|
|
46
|
+
*
|
|
47
|
+
*/
|
|
48
|
+
export const resolve_gro_module_path = (path = ''): string => {
|
|
49
|
+
const gro_bin_path = resolve(NODE_MODULES_DIRNAME, '.bin/gro');
|
|
50
|
+
// case 1
|
|
51
|
+
// Prefer any locally installed version of Gro.
|
|
52
|
+
if (existsSync(gro_bin_path)) {
|
|
53
|
+
return join(realpathSync(gro_bin_path), '..', path);
|
|
54
|
+
}
|
|
55
|
+
// case 2
|
|
56
|
+
// If running Gro inside its own repo, require the local dist.
|
|
57
|
+
// If the local dist is not yet built it will fall back to the global.
|
|
58
|
+
if (
|
|
59
|
+
existsSync(join(SVELTEKIT_DIST_DIRNAME, 'gro.js')) &&
|
|
60
|
+
existsSync(join(SVELTEKIT_DIST_DIRNAME, path))
|
|
61
|
+
) {
|
|
62
|
+
return resolve(SVELTEKIT_DIST_DIRNAME, path);
|
|
63
|
+
}
|
|
64
|
+
// case 3
|
|
65
|
+
// Fall back to the version associated with the running CLI.
|
|
66
|
+
const file_path = fileURLToPath(import.meta.url);
|
|
67
|
+
return join(file_path, '..', path);
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Runs a file using the Gro loader.
|
|
72
|
+
*
|
|
73
|
+
* Uses conditional exports to correctly set up `esm-env` as development by default,
|
|
74
|
+
* so if you want production set `NODE_ENV=production`.
|
|
75
|
+
*
|
|
76
|
+
* @see https://nodejs.org/api/packages.html#conditional-exports
|
|
77
|
+
*
|
|
78
|
+
* @param loader_path path to loader
|
|
79
|
+
* @param invoke_path path to file to spawn with `node`
|
|
80
|
+
*/
|
|
81
|
+
export const spawn_with_loader = (
|
|
82
|
+
loader_path: string,
|
|
83
|
+
invoke_path: string,
|
|
84
|
+
argv: string[],
|
|
85
|
+
): Promise<Spawn_Result> => {
|
|
86
|
+
const args = [
|
|
87
|
+
'--import',
|
|
88
|
+
// This does the same as `$lib/register.ts` but without the cost of importing another file.
|
|
89
|
+
`data:text/javascript,
|
|
90
|
+
import {register} from "node:module";
|
|
91
|
+
import {pathToFileURL} from "node:url";
|
|
92
|
+
register("${loader_path}", pathToFileURL("./"));`,
|
|
93
|
+
'--enable-source-maps', // because TypeScript
|
|
94
|
+
];
|
|
95
|
+
// In almost all cases we want the exports condition to be `"development"`.
|
|
96
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
97
|
+
args.push('-C', 'development'); // same as `--conditions`
|
|
98
|
+
}
|
|
99
|
+
args.push(invoke_path, ...argv);
|
|
100
|
+
return spawn('node', args);
|
|
101
|
+
};
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// TODO this became unused with https://github.com/ryanatkn/gro/pull/382
|
|
2
|
+
// because we no longer have a normal system build - replace with an esbuild plugin
|
|
3
|
+
// @ts-nocheck
|
|
4
|
+
|
|
5
|
+
import type {Plugin, Plugin_Context} from './plugin.js';
|
|
6
|
+
import type {Args} from './args.js';
|
|
7
|
+
import {path_id_to_base_path} from './paths.js';
|
|
8
|
+
import {find_genfiles, is_gen_path} from './gen.js';
|
|
9
|
+
import {filter_dependents} from './build/source_file.js';
|
|
10
|
+
import {throttle} from './throttle.js';
|
|
11
|
+
|
|
12
|
+
const FLUSH_DEBOUNCE_DELAY = 500;
|
|
13
|
+
|
|
14
|
+
export interface Task_Args extends Args {
|
|
15
|
+
watch?: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const plugin = (): Plugin<Plugin_Context<Task_Args>> => {
|
|
19
|
+
let generating = false;
|
|
20
|
+
let regen = false;
|
|
21
|
+
let on_filer_build: ((e: Filer_Events['build']) => void) | undefined;
|
|
22
|
+
const queued_files: Set<string> = new Set();
|
|
23
|
+
const queue_gen = (gen_file_name: string) => {
|
|
24
|
+
queued_files.add(gen_file_name);
|
|
25
|
+
void flush_gen_queue();
|
|
26
|
+
};
|
|
27
|
+
const flush_gen_queue = throttle(async () => {
|
|
28
|
+
// hacky way to avoid concurrent `gro gen` calls
|
|
29
|
+
if (generating) {
|
|
30
|
+
regen = true;
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
generating = true;
|
|
34
|
+
const files = Array.from(queued_files);
|
|
35
|
+
queued_files.clear();
|
|
36
|
+
await gen(files);
|
|
37
|
+
generating = false;
|
|
38
|
+
if (regen) {
|
|
39
|
+
regen = false;
|
|
40
|
+
void flush_gen_queue();
|
|
41
|
+
}
|
|
42
|
+
}, FLUSH_DEBOUNCE_DELAY);
|
|
43
|
+
const gen = (files: string[] = []) => spawn_cli('gro', ['gen', ...files]);
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
name: 'gro_plugin_gen',
|
|
47
|
+
setup: async ({args: {watch}, dev, log, config}) => {
|
|
48
|
+
// For production builds, we assume `gen` is already fresh,
|
|
49
|
+
// which should be checked by CI via `gro check` which calls `gro gen --check`.
|
|
50
|
+
if (!dev) return;
|
|
51
|
+
|
|
52
|
+
// Run `gen`, first checking if there are any modules to avoid a console error.
|
|
53
|
+
// Some parts of the build may have already happened,
|
|
54
|
+
// making us miss `build` events for gen dependencies,
|
|
55
|
+
// so we run `gen` here even if it's usually wasteful.
|
|
56
|
+
const found = find_genfiles([paths.source], root_dirs, config);
|
|
57
|
+
if (found.ok && found.value.resolved_input_files.size > 0) {
|
|
58
|
+
await gen();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Do we need to just generate everything once and exit?
|
|
62
|
+
// TODO could we have an esbuild context here? problem is watching the right files, maybe a plugin that tracks deps
|
|
63
|
+
if (!filer || !watch) {
|
|
64
|
+
log.info('generating and exiting early');
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// When a file builds, check it and its tree of dependents
|
|
69
|
+
// for any `.gen.` files that need to run.
|
|
70
|
+
on_filer_build = ({source_file, build_config}) => {
|
|
71
|
+
// TODO how to handle this now? the loader traces deps for us with `parentPath`,
|
|
72
|
+
// but we probably want to make this an esbuild plugin instead
|
|
73
|
+
// if (build_config.name !== 'system') return;
|
|
74
|
+
if (is_gen_path(source_file.id)) {
|
|
75
|
+
queue_gen(path_id_to_base_path(source_file.id));
|
|
76
|
+
}
|
|
77
|
+
const dependent_gen_file_ids = filter_dependents(
|
|
78
|
+
source_file,
|
|
79
|
+
build_config,
|
|
80
|
+
filer.find_by_id as any, // cast because we can assume they're all `SourceFile`s
|
|
81
|
+
is_gen_path,
|
|
82
|
+
);
|
|
83
|
+
for (const dependent_gen_file_id of dependent_gen_file_ids) {
|
|
84
|
+
queue_gen(path_id_to_base_path(dependent_gen_file_id));
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
filer.on('build', on_filer_build);
|
|
88
|
+
},
|
|
89
|
+
teardown: ({filer}) => {
|
|
90
|
+
if (on_filer_build && filer) {
|
|
91
|
+
filer.off('build', on_filer_build);
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
};
|