@ryanatkn/gro 0.129.4 → 0.129.6

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.
Files changed (124) hide show
  1. package/dist/package.js +3 -3
  2. package/package.json +3 -2
  3. package/src/lib/args.test.ts +59 -0
  4. package/src/lib/args.ts +169 -0
  5. package/src/lib/build.task.ts +37 -0
  6. package/src/lib/changelog.test.ts +138 -0
  7. package/src/lib/changelog.ts +69 -0
  8. package/src/lib/changeset.task.ts +206 -0
  9. package/src/lib/changeset_helpers.ts +13 -0
  10. package/src/lib/check.task.ts +90 -0
  11. package/src/lib/clean.task.ts +46 -0
  12. package/src/lib/clean_fs.ts +54 -0
  13. package/src/lib/cli.ts +97 -0
  14. package/src/lib/commit.task.ts +33 -0
  15. package/src/lib/config.test.ts +71 -0
  16. package/src/lib/config.ts +161 -0
  17. package/src/lib/deploy.task.ts +243 -0
  18. package/src/lib/dev.task.ts +43 -0
  19. package/src/lib/docs/README.gen.md.ts +63 -0
  20. package/src/lib/docs/README.md +20 -0
  21. package/src/lib/docs/build.md +41 -0
  22. package/src/lib/docs/config.md +213 -0
  23. package/src/lib/docs/deploy.md +32 -0
  24. package/src/lib/docs/dev.md +40 -0
  25. package/src/lib/docs/gen.md +269 -0
  26. package/src/lib/docs/gro_plugin_sveltekit_app.md +113 -0
  27. package/src/lib/docs/package_json.md +33 -0
  28. package/src/lib/docs/plugin.md +50 -0
  29. package/src/lib/docs/publish.md +137 -0
  30. package/src/lib/docs/task.md +391 -0
  31. package/src/lib/docs/tasks.gen.md.ts +90 -0
  32. package/src/lib/docs/tasks.md +37 -0
  33. package/src/lib/docs/test.md +52 -0
  34. package/src/lib/env.ts +75 -0
  35. package/src/lib/esbuild_helpers.ts +50 -0
  36. package/src/lib/esbuild_plugin_external_worker.ts +92 -0
  37. package/src/lib/esbuild_plugin_svelte.test.ts +88 -0
  38. package/src/lib/esbuild_plugin_svelte.ts +108 -0
  39. package/src/lib/esbuild_plugin_sveltekit_local_imports.ts +31 -0
  40. package/src/lib/esbuild_plugin_sveltekit_shim_alias.ts +25 -0
  41. package/src/lib/esbuild_plugin_sveltekit_shim_app.ts +41 -0
  42. package/src/lib/esbuild_plugin_sveltekit_shim_env.ts +46 -0
  43. package/src/lib/format.task.ts +30 -0
  44. package/src/lib/format_directory.ts +55 -0
  45. package/src/lib/format_file.test.ts +20 -0
  46. package/src/lib/format_file.ts +49 -0
  47. package/src/lib/fs.ts +18 -0
  48. package/src/lib/gen.task.ts +134 -0
  49. package/src/lib/gen.test.ts +306 -0
  50. package/src/lib/gen.ts +360 -0
  51. package/src/lib/git.test.ts +34 -0
  52. package/src/lib/git.ts +297 -0
  53. package/src/lib/github.ts +46 -0
  54. package/src/lib/gro.config.default.ts +34 -0
  55. package/src/lib/gro.ts +25 -0
  56. package/src/lib/gro_helpers.ts +101 -0
  57. package/src/lib/gro_plugin_gen.ts +95 -0
  58. package/src/lib/gro_plugin_server.ts +288 -0
  59. package/src/lib/gro_plugin_sveltekit_app.ts +257 -0
  60. package/src/lib/gro_plugin_sveltekit_library.ts +74 -0
  61. package/src/lib/hash.test.ts +33 -0
  62. package/src/lib/hash.ts +19 -0
  63. package/src/lib/index.ts +4 -0
  64. package/src/lib/input_path.test.ts +230 -0
  65. package/src/lib/input_path.ts +255 -0
  66. package/src/lib/invoke.ts +27 -0
  67. package/src/lib/invoke_task.ts +116 -0
  68. package/src/lib/lint.task.ts +38 -0
  69. package/src/lib/loader.test.ts +49 -0
  70. package/src/lib/loader.ts +226 -0
  71. package/src/lib/module.test.ts +46 -0
  72. package/src/lib/module.ts +13 -0
  73. package/src/lib/modules.test.ts +63 -0
  74. package/src/lib/modules.ts +112 -0
  75. package/src/lib/package.gen.ts +33 -0
  76. package/src/lib/package.ts +998 -0
  77. package/src/lib/package_json.test.ts +101 -0
  78. package/src/lib/package_json.ts +330 -0
  79. package/src/lib/package_meta.ts +86 -0
  80. package/src/lib/path.ts +23 -0
  81. package/src/lib/path_constants.ts +30 -0
  82. package/src/lib/paths.test.ts +77 -0
  83. package/src/lib/paths.ts +101 -0
  84. package/src/lib/plugin.test.ts +57 -0
  85. package/src/lib/plugin.ts +113 -0
  86. package/src/lib/publish.task.ts +194 -0
  87. package/src/lib/register.ts +3 -0
  88. package/src/lib/reinstall.task.ts +42 -0
  89. package/src/lib/release.task.ts +21 -0
  90. package/src/lib/resolve.task.ts +43 -0
  91. package/src/lib/resolve_node_specifier.test.ts +31 -0
  92. package/src/lib/resolve_node_specifier.ts +55 -0
  93. package/src/lib/resolve_specifier.test.ts +76 -0
  94. package/src/lib/resolve_specifier.ts +61 -0
  95. package/src/lib/run.task.ts +41 -0
  96. package/src/lib/run_gen.test.ts +196 -0
  97. package/src/lib/run_gen.ts +95 -0
  98. package/src/lib/run_task.test.ts +86 -0
  99. package/src/lib/run_task.ts +75 -0
  100. package/src/lib/search_fs.test.ts +56 -0
  101. package/src/lib/search_fs.ts +93 -0
  102. package/src/lib/src_json.test.ts +49 -0
  103. package/src/lib/src_json.ts +153 -0
  104. package/src/lib/svelte_helpers.ts +2 -0
  105. package/src/lib/sveltekit_config.ts +101 -0
  106. package/src/lib/sveltekit_config_global.ts +6 -0
  107. package/src/lib/sveltekit_helpers.ts +132 -0
  108. package/src/lib/sveltekit_shim_app.ts +42 -0
  109. package/src/lib/sveltekit_shim_app_environment.ts +14 -0
  110. package/src/lib/sveltekit_shim_app_forms.ts +20 -0
  111. package/src/lib/sveltekit_shim_app_navigation.ts +23 -0
  112. package/src/lib/sveltekit_shim_app_paths.ts +16 -0
  113. package/src/lib/sveltekit_shim_app_stores.ts +25 -0
  114. package/src/lib/sveltekit_shim_env.ts +45 -0
  115. package/src/lib/sync.task.ts +47 -0
  116. package/src/lib/task.test.ts +84 -0
  117. package/src/lib/task.ts +235 -0
  118. package/src/lib/task_logging.ts +180 -0
  119. package/src/lib/test.task.ts +50 -0
  120. package/src/lib/throttle.test.ts +52 -0
  121. package/src/lib/throttle.ts +63 -0
  122. package/src/lib/typecheck.task.ts +57 -0
  123. package/src/lib/upgrade.task.ts +108 -0
  124. package/src/lib/watch_dir.ts +88 -0
package/dist/package.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // generated by src/lib/package.gen.ts
2
2
  export const package_json = {
3
3
  name: '@ryanatkn/gro',
4
- version: '0.129.4',
4
+ version: '0.129.6',
5
5
  description: 'task runner and toolkit extending SvelteKit',
6
6
  motto: 'generate, run, optimize',
7
7
  glyph: '🌰',
@@ -82,7 +82,7 @@ export const package_json = {
82
82
  overrides: [{ files: 'package.json', options: { useTabs: false } }],
83
83
  },
84
84
  sideEffects: ['**/*.css'],
85
- files: ['dist'],
85
+ files: ['dist', 'src/lib'],
86
86
  exports: {
87
87
  '.': { types: './dist/index.d.ts', default: './dist/index.js' },
88
88
  './package.json': './package.json',
@@ -266,7 +266,7 @@ export const package_json = {
266
266
  };
267
267
  export const src_json = {
268
268
  name: '@ryanatkn/gro',
269
- version: '0.129.4',
269
+ version: '0.129.6',
270
270
  modules: {
271
271
  '.': {
272
272
  path: 'index.ts',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ryanatkn/gro",
3
- "version": "0.129.4",
3
+ "version": "0.129.6",
4
4
  "description": "task runner and toolkit extending SvelteKit",
5
5
  "motto": "generate, run, optimize",
6
6
  "glyph": "🌰",
@@ -107,7 +107,8 @@
107
107
  "**/*.css"
108
108
  ],
109
109
  "files": [
110
- "dist"
110
+ "dist",
111
+ "src/lib"
111
112
  ],
112
113
  "exports": {
113
114
  ".": {
@@ -0,0 +1,59 @@
1
+ import mri from 'mri';
2
+ import {suite} from 'uvu';
3
+ import * as assert from 'uvu/assert';
4
+
5
+ import {
6
+ serialize_args,
7
+ to_forwarded_args,
8
+ to_forwarded_args_by_command,
9
+ to_raw_rest_args,
10
+ } from './args.js';
11
+
12
+ /* test__serialize_args */
13
+ const test__serialize_args = suite('serialize_args');
14
+
15
+ test__serialize_args('basic behavior', () => {
16
+ const raw = ['a', '-i', '1', 'b', 'c', '-i', '-i', 'three'];
17
+ const parsed = mri(raw);
18
+ assert.equal(parsed, {_: ['a', 'b', 'c'], i: [1, true, 'three']});
19
+ const serialized = serialize_args(parsed);
20
+ assert.equal(serialized, ['a', 'b', 'c', '-i', '1', '-i', '-i', 'three']); // sorted
21
+ });
22
+
23
+ test__serialize_args.run();
24
+ /* test__serialize_args */
25
+
26
+ /* test__to_forwarded_args_by_command */
27
+ const test__to_forwarded_args_by_command = suite('to_forwarded_args_by_command');
28
+
29
+ test__to_forwarded_args_by_command('basic behavior', () => {
30
+ const raw_rest_args = to_raw_rest_args(
31
+ (
32
+ 'gro taskname a b c --d -e 1 -- -- ' +
33
+ 'eslint a --b c -- ' +
34
+ 'gro a --a -- ' +
35
+ 'tsc -b -- ' +
36
+ 'gro b -t2 t2a --t2 t2b --t222 2 -- -- -- ' +
37
+ 'groc --m --n nn -- ' +
38
+ 'gro d -b a --c 4 -- ' +
39
+ 'gro d -b a --c 5 -- '
40
+ ).split(' '),
41
+ );
42
+ assert.equal(to_forwarded_args_by_command(raw_rest_args), {
43
+ eslint: {_: ['a'], b: 'c'},
44
+ 'gro a': {a: true},
45
+ tsc: {b: true},
46
+ 'gro b': {'2': 't2a', t: true, t2: 't2b', t222: 2},
47
+ groc: {m: true, n: 'nn'},
48
+ 'gro d': {b: 'a', c: 5},
49
+ });
50
+ assert.equal(to_forwarded_args('gro b', raw_rest_args), {
51
+ '2': 't2a',
52
+ t: true,
53
+ t2: 't2b',
54
+ t222: 2,
55
+ });
56
+ });
57
+
58
+ test__to_forwarded_args_by_command.run();
59
+ /* test__to_forwarded_args_by_command */
@@ -0,0 +1,169 @@
1
+ import {gray, magenta} from '@ryanatkn/belt/styletext.js';
2
+ import mri from 'mri';
3
+ import type {z} from 'zod';
4
+
5
+ /**
6
+ * These extend the CLI args for tasks.
7
+ * Anything can be assigned to a task's `args`. It's just a mutable POJO dictionary.
8
+ * Downstream tasks will see args that upstream events mutate,
9
+ * unless `invoke_task` is called with modified args.
10
+ * Upstream tasks can use listeners to respond to downstream events and values.
11
+ * It's a beautiful mutable spaghetti mess. cant get enough
12
+ * The raw CLI ares are handled by `mri` - https://github.com/lukeed/mri
13
+ */
14
+ export interface Args {
15
+ _?: string[];
16
+ help?: boolean;
17
+ [key: string]: Arg_Value;
18
+ }
19
+
20
+ export type Arg_Value = string | number | boolean | undefined | Array<string | number | boolean>;
21
+
22
+ export interface Arg_Schema {
23
+ type: string;
24
+ default: Arg_Value;
25
+ description: string;
26
+ }
27
+
28
+ /**
29
+ * Parses user input args with a Zod schema.
30
+ * Sets the correct source of truth for `no-` versions of args,
31
+ * to the opposite of the unprefixed versions when not included in `unparsed_args`.
32
+ * This is needed because CLI args don't have a normal way of setting falsy values,
33
+ * so instead the args parser `mri` will pass through the truthy versions of args
34
+ * without the `no-` prefix.
35
+ * When we declare task args schemas,
36
+ * we need include both versions with their defaults to get correct `--help` output.
37
+ * Parsing like this also ensures data consistency for both versions because `mri` only creates one.
38
+ * A simpler implementation could replace `mri`, but it handles some finicky details well.
39
+ */
40
+ export const parse_args = <
41
+ TOutput extends Record<string, Arg_Value> = Args,
42
+ TInput extends Record<string, Arg_Value> = Args,
43
+ >(
44
+ unparsed_args: TInput,
45
+ schema: z.ZodType<TOutput, z.ZodTypeDef, TInput>,
46
+ ): z.SafeParseReturnType<TInput, TOutput> => {
47
+ const parsed = schema.safeParse(unparsed_args);
48
+ if (parsed.success) {
49
+ // mutate `data` with the correct source of truth for `no-` prefixed args
50
+ const {data} = parsed;
51
+ for (const key in parsed.data) {
52
+ if (key.startsWith('no-')) {
53
+ const base_key = key.substring(3);
54
+ if (!(key in unparsed_args)) {
55
+ (data as any)[key] = !data[base_key];
56
+ } else if (!(base_key in unparsed_args)) {
57
+ (data as any)[base_key] = !data[key];
58
+ }
59
+ }
60
+ }
61
+ }
62
+ return parsed;
63
+ };
64
+
65
+ /**
66
+ * Serializes parsed `Args` for CLI commands.
67
+ */
68
+ export const serialize_args = (args: Args): string[] => {
69
+ const result: string[] = [];
70
+ const add_value = (name: string, value: string | number | boolean | undefined): void => {
71
+ if (value === undefined) return;
72
+ result.push(name);
73
+ if (typeof value !== 'boolean') {
74
+ result.push(value + '');
75
+ }
76
+ };
77
+ let _: string[] | null = null;
78
+ for (const [key, value] of Object.entries(args)) {
79
+ if (key === '_') {
80
+ _ = value ? (value as any[]).map((v) => (v === undefined ? '' : v + '')) : [];
81
+ } else {
82
+ const name = `${key.length === 1 ? '-' : '--'}${key}`;
83
+ if (Array.isArray(value)) {
84
+ for (const v of value) add_value(name, v);
85
+ } else {
86
+ add_value(name, value);
87
+ }
88
+ }
89
+ }
90
+ return _ ? [..._, ...result] : result;
91
+ };
92
+
93
+ /**
94
+ * Parses `task_name` and `args` from `process.argv` using `mri`,
95
+ * ignoring anything after any `--`.
96
+ */
97
+ export const to_task_args = (argv = process.argv): {task_name: string; args: Args} => {
98
+ const forwarded_index = argv.indexOf('--');
99
+ const args = mri(forwarded_index === -1 ? argv.slice(2) : argv.slice(2, forwarded_index));
100
+ const task_name = args._.shift() ?? '';
101
+ if (!args._.length) delete (args as Args)._; // enable schema defaults
102
+ return {task_name, args};
103
+ };
104
+
105
+ /**
106
+ * Gets the array of raw string args starting with the first `--`, if any.
107
+ */
108
+ export const to_raw_rest_args = (argv = process.argv): string[] => {
109
+ const forwarded_index = argv.indexOf('--');
110
+ return forwarded_index === -1 ? [] : argv.slice(forwarded_index);
111
+ };
112
+
113
+ /**
114
+ * Parses `process.argv` for the specified `command`, so given
115
+ * `gro taskname arg1 --arg2 -- eslint eslintarg1 --eslintarg2 -- tsc --tscarg1 --tscarg2`
116
+ * the `command` `'eslint'` returns `eslintarg1 --eslintarg2`
117
+ * and `'tsc'` returns `--tscarg1` and `--tscarg2`.
118
+ */
119
+ export const to_forwarded_args = (
120
+ command: string,
121
+ raw_rest_args?: string[],
122
+ cache = to_forwarded_args_by_command(raw_rest_args),
123
+ ): Args => cache[command] ?? {};
124
+
125
+ export const to_forwarded_args_by_command = (
126
+ raw_rest_args = to_raw_rest_args(),
127
+ ): Record<string, Args | undefined> => {
128
+ // Parse each segment of `argv` separated by `--`.
129
+ const argvs: string[][] = [];
130
+ let arr: string[] | undefined;
131
+ for (const arg of raw_rest_args) {
132
+ if (arg === '--') {
133
+ if (arr?.length) argvs.push(arr);
134
+ arr = [];
135
+ } else if (!arr) {
136
+ continue;
137
+ } else if (arg) {
138
+ arr.push(arg);
139
+ }
140
+ }
141
+ if (arr?.length) argvs.push(arr);
142
+ // Add each segment of parsed `argv` keyed by the first rest arg,
143
+ // which is assumed to be the CLI command that gets forwarded the args.
144
+ const forwarded_args_by_command: Record<string, Args> = {};
145
+ for (const argv of argvs) {
146
+ const args = mri(argv);
147
+ let command = args._.shift();
148
+ if (!command) {
149
+ throw Error(
150
+ `Malformed args following a \`--\`. Expected a rest arg command: \`${argv.join(' ')}\``,
151
+ );
152
+ }
153
+ // Gro commands get combined with their task name.
154
+ if (command === 'gro') {
155
+ if (!args._.length) {
156
+ throw Error(
157
+ `Malformed args following a \`--\`. Expected gro taskname: \`${argv.join(' ')}\``,
158
+ );
159
+ }
160
+ command += ' ' + args._.shift();
161
+ }
162
+ if (!args._.length) delete (args as Args)._;
163
+ forwarded_args_by_command[command] = args;
164
+ }
165
+ return forwarded_args_by_command;
166
+ };
167
+
168
+ export const print_command_args = (serialized_args: string[]): string =>
169
+ gray('[') + magenta('running command') + gray(']') + ' ' + serialized_args.join(' ');
@@ -0,0 +1,37 @@
1
+ import {z} from 'zod';
2
+
3
+ import type {Task} from './task.js';
4
+ import {Plugins} from './plugin.js';
5
+ import {clean_fs} from './clean_fs.js';
6
+
7
+ export const Args = z
8
+ .object({
9
+ install: z.boolean({description: 'dual of no-install'}).default(true),
10
+ 'no-install': z
11
+ .boolean({description: 'opt out of npm installing before building'})
12
+ .default(false),
13
+ })
14
+ .strict();
15
+ export type Args = z.infer<typeof Args>;
16
+
17
+ export const task: Task<Args> = {
18
+ summary: 'build the project',
19
+ Args,
20
+ run: async (ctx): Promise<void> => {
21
+ const {args, invoke_task} = ctx;
22
+ const {install} = args;
23
+
24
+ // By default `gro build` installs, opposite of `gro sync`, so that arg needs special handling.
25
+ await invoke_task('sync', {install});
26
+
27
+ // TODO possibly detect if the git workspace is clean, and ask for confirmation if not,
28
+ // because we're not doing things like `gro gen` here because that's a dev/CI concern
29
+
30
+ await clean_fs({build_dist: true});
31
+
32
+ const plugins = await Plugins.create({...ctx, dev: false, watch: false});
33
+ await plugins.setup();
34
+ await plugins.adapt();
35
+ await plugins.teardown();
36
+ },
37
+ };
@@ -0,0 +1,138 @@
1
+ import {test} from 'uvu';
2
+ import * as assert from 'uvu/assert';
3
+ import {Logger} from '@ryanatkn/belt/log.js';
4
+ import {readFile, writeFile} from 'node:fs/promises';
5
+ import type {Fetch_Value_Cache} from '@ryanatkn/belt/fetch.js';
6
+
7
+ import {update_changelog} from './changelog.js';
8
+ import {load_from_env} from './env.js';
9
+
10
+ const log = new Logger();
11
+
12
+ const token = load_from_env('GITHUB_TOKEN_SECRET');
13
+ if (!token) {
14
+ log.warn('the env var GITHUB_TOKEN_SECRET was not found, so API calls with be unauthorized');
15
+ }
16
+
17
+ const fixture_path = 'src/fixtures/changelog_example.md';
18
+
19
+ // TODO ideally this is just a ts file, but there's a problem where building outputs a `.d.ts` file
20
+ // when importing from src/fixtures (fix in SvelteKit/Vite/tsconfig?) and I want to keep it in src/fixtures
21
+ const changelog_cache_fixture: Fetch_Value_Cache = new Map(
22
+ JSON.parse(await readFile('src/fixtures/changelog_cache.json', 'utf8')),
23
+ );
24
+
25
+ test('update_changelog', async () => {
26
+ const original = await readFile(fixture_path, 'utf8');
27
+ const result = await update_changelog(
28
+ 'ryanatkn',
29
+ 'gro',
30
+ fixture_path,
31
+ token,
32
+ log,
33
+ changelog_cache_fixture,
34
+ );
35
+ const updated = await readFile(fixture_path, 'utf8');
36
+ await writeFile(fixture_path, original, 'utf8');
37
+ assert.ok(result);
38
+ assert.is(
39
+ updated,
40
+ `# @ryanatkn/gro
41
+
42
+ ## 0.6.0
43
+
44
+ ### Minor Changes
45
+
46
+ - duplicate 1 ([#429](https://github.com/ryanatkn/gro/pull/429))
47
+ - duplicate 2 ([#429](https://github.com/ryanatkn/gro/pull/429))
48
+ - abc ([#437](https://github.com/ryanatkn/gro/pull/437))
49
+
50
+ - 123
51
+ - 123
52
+ - 123
53
+
54
+ ### Patch Changes
55
+
56
+ - abc ([5e94cd4](https://github.com/ryanatkn/gro/commit/5e94cd4))
57
+
58
+ ## 0.5.2
59
+
60
+ ### Patch Changes
61
+
62
+ - abc ([e345eaa](https://github.com/ryanatkn/gro/commit/e345eaa))
63
+
64
+ ## 0.5.1
65
+
66
+ ### Patch Changes
67
+
68
+ - abc ([094279d](https://github.com/ryanatkn/gro/commit/094279d))
69
+
70
+ ## 0.5.0
71
+
72
+ ### Minor Changes
73
+
74
+ - abc ([f6133f7](https://github.com/ryanatkn/gro/commit/f6133f7))
75
+
76
+ ## 0.4.3
77
+
78
+ ### Patch Changes
79
+
80
+ - abc ([54b65ec](https://github.com/ryanatkn/gro/commit/54b65ec))
81
+
82
+ ## 0.4.2
83
+
84
+ ### Patch Changes
85
+
86
+ - abc ([80365d0](https://github.com/ryanatkn/gro/commit/80365d0))
87
+
88
+ ## 0.4.1
89
+
90
+ ### Patch Changes
91
+
92
+ - abc ([3d84dfd](https://github.com/ryanatkn/gro/commit/3d84dfd))
93
+ - abc ([fc64b77](https://github.com/ryanatkn/gro/commit/fc64b77))
94
+
95
+ ## 0.4.0
96
+
97
+ ### Minor Changes
98
+
99
+ - abc ([#434](https://github.com/ryanatkn/gro/pull/434))
100
+ - 123
101
+ - 123
102
+
103
+ ### Patch Changes
104
+
105
+ - abc ([#434](https://github.com/ryanatkn/gro/pull/434))
106
+
107
+ ## 0.3.1
108
+
109
+ - e
110
+
111
+ ## 0.3.0
112
+
113
+ - b2
114
+ - c2
115
+ - d2
116
+
117
+ ## 0.2.0
118
+
119
+ - a2
120
+
121
+ ## 0.1.2
122
+
123
+ - e
124
+
125
+ ## 0.1.1
126
+
127
+ - b
128
+ - c
129
+ - d
130
+
131
+ ## 0.1.0
132
+
133
+ - a
134
+ `,
135
+ );
136
+ });
137
+
138
+ test.run();
@@ -0,0 +1,69 @@
1
+ import {readFile, writeFile} from 'node:fs/promises';
2
+ import {z} from 'zod';
3
+ import type {Logger} from '@ryanatkn/belt/log.js';
4
+ import type {Fetch_Value_Cache} from '@ryanatkn/belt/fetch.js';
5
+
6
+ import {github_fetch_commit_prs} from './github.js';
7
+
8
+ /**
9
+ * Updates a changelog produced by `@changesets/changelog-git` with better links and formatting.
10
+ * It's similar to `@changesets/changelog-github` but doesn't require a token for light usage.
11
+ * This may be better implemented as a standalone dependency
12
+ * as an alternative to `@changesets/changelog-git`.
13
+ * @returns boolean indicating if the changelog changed
14
+ */
15
+ export const update_changelog = async (
16
+ owner: string,
17
+ repo: string,
18
+ path = 'CHANGELOG.md',
19
+ token?: string,
20
+ log?: Logger,
21
+ cache: Fetch_Value_Cache = new Map(), // include a default cache to efficiently handle multiple changesets per commit
22
+ ): Promise<boolean> => {
23
+ const contents = await readFile(path, 'utf8');
24
+ const parsed = parse_changelog(contents);
25
+ const mapped = await map_changelog(parsed, owner, repo, token, log, cache);
26
+ const updated = serialize_changelog(mapped);
27
+ if (contents === updated) {
28
+ return false;
29
+ }
30
+ await writeFile(path, updated, 'utf8');
31
+ return true;
32
+ };
33
+
34
+ // keeping this really simple for now, no need to parse further for our current usecases
35
+ const Parsed_Changelog = z.array(z.string());
36
+ type Parsed_Changelog = z.infer<typeof Parsed_Changelog>;
37
+ const parse_changelog = (contents: string): Parsed_Changelog => contents.split('\n');
38
+ const serialize_changelog = (parsed: Parsed_Changelog): string => parsed.join('\n');
39
+
40
+ const LINE_WITH_SHA_MATCHER = /^- ([a-z0-9]{7,8}): /u;
41
+
42
+ const map_changelog = async (
43
+ parsed: Parsed_Changelog,
44
+ owner: string,
45
+ repo: string,
46
+ token?: string,
47
+ log?: Logger,
48
+ cache?: Fetch_Value_Cache,
49
+ ): Promise<Parsed_Changelog> => {
50
+ const mapped: Parsed_Changelog = [];
51
+ for (const line of parsed) {
52
+ const matches = LINE_WITH_SHA_MATCHER.exec(line);
53
+ if (matches) {
54
+ const commit_sha = matches[1];
55
+ const l = '- ' + line.substring(commit_sha.length + 4);
56
+ const prs = await github_fetch_commit_prs(owner, repo, commit_sha, token, log, cache); // eslint-disable-line no-await-in-loop
57
+ if (prs?.length) {
58
+ mapped.push(`${l} (${prs.map((p) => `[#${p.number}](${p.html_url})`).join(', ')})`);
59
+ } else {
60
+ mapped.push(
61
+ `${l} ([${commit_sha}](https://github.com/${owner}/${repo}/commit/${commit_sha}))`,
62
+ );
63
+ }
64
+ } else {
65
+ mapped.push(line);
66
+ }
67
+ }
68
+ return mapped;
69
+ };