Skip to content

Commit

Permalink
add changeset task (#399)
Browse files Browse the repository at this point in the history
  • Loading branch information
ryanatkn authored Sep 21, 2023
1 parent a712338 commit 3ae04c5
Show file tree
Hide file tree
Showing 9 changed files with 130 additions and 6 deletions.
5 changes: 5 additions & 0 deletions .changeset/sixty-carrots-drop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@feltjs/gro': patch
---

add `gro changeset` task
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,10 @@ gro # prints available tasks - defers to the project's locally installed version
Run a task: gro [name]
View help: gro [name] --help
14 tasks in ./src/lib:
15 tasks in ./src/lib:
build build the project
changeset call changeset with gro patterns
check check that everything is ready to commit
clean remove temporary dev and build files, and optionally prune git branches
commit commit and push to a new branch
Expand Down Expand Up @@ -193,7 +194,7 @@ gro deploy # build and push to the `deploy` branch
To publish: (also see [`src/lib/docs/publish.md`](/src/lib/docs/publish.md))

```bash
gro publish # flush changesets, bump version, publish to npm, and git push
gro publish # flush changeset to changelog, bump version, publish to npm, and git push
```

Etc:
Expand Down
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
},
"devDependencies": {
"@changesets/changelog-git": "^0.1.14",
"@changesets/types": "^5.2.1",
"@feltjs/eslint-config": "^0.4.0",
"@sveltejs/adapter-static": "^2.0.3",
"@sveltejs/kit": "^1.25.0",
Expand Down
110 changes: 110 additions & 0 deletions src/lib/changeset.task.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import {z} from 'zod';
import {spawn} from '@feltjs/util/process.js';
import {red, blue} from 'kleur/colors';
import type {WrittenConfig} from '@changesets/types';
import {readFile, writeFile} from 'node:fs/promises';

import type {Task} from './task/task.js';
import {exists} from './util/exists.js';
import {dirname} from 'node:path';

const RESTRICTED_ACCESS = 'restricted';
const PUBLIC_ACCESS = 'public';

const CHANGESET_CONFIG_PATH = './.changeset/config.json';

export const Args = z
.object({
_: z.array(z.string(), {description: 'the commands to pass to changeset'}).default([]),
path: z.string({description: 'changeset config file path'}).default(CHANGESET_CONFIG_PATH),
access: z
.union([z.literal(RESTRICTED_ACCESS), z.literal(PUBLIC_ACCESS)], {
description: 'changeset "access" config value, `AccessType`',
})
.default(PUBLIC_ACCESS),
changelog: z
.string({description: 'changeset "changelog" config value'})
.default('@changesets/changelog-git'),
install: z.boolean({description: 'dual of no-install'}).default(true),
'no-install': z
.boolean({description: 'opt out of npm installing the changelog package'})
.default(false),
})
.strict();
export type Args = z.infer<typeof Args>;

export const task: Task<Args> = {
summary: 'call changeset with gro patterns',
Args,
run: async (ctx): Promise<void> => {
const {
args: {_: changeset_args, path, access, changelog, install},
log,
} = ctx;

const inited = await exists(path);

if (!inited) {
await spawn('npx', ['changeset', 'init']);

const access_color = access === RESTRICTED_ACCESS ? blue : red;
log.info('initing changeset with ' + access_color(access) + ' access');
if (access !== RESTRICTED_ACCESS) {
await update_changeset_config(path, (config) => {
const updated = {...config};
updated.access = access;
updated.changelog = changelog;
return updated;
});
}

if (install) {
await spawn('npm', ['i', '-D', changelog]);
}
}

await spawn('changeset', changeset_args);

await spawn('git', ['add', dirname(CHANGESET_CONFIG_PATH)]);
},
};

export interface ChangesetCallback {
(config: WrittenConfig): WrittenConfig | Promise<WrittenConfig>;
}

export interface UpdateWrittenConfig {
(path: string, cb: ChangesetCallback): Promise<boolean>;
}

// TODO refactor all of this with zod and package_json helpers - util file helper? JSON parse pluggable

export const update_changeset_config: UpdateWrittenConfig = async (path, cb) => {
const config_contents = await load_changeset_config_contents(path);
const config = parse_changeset_config(config_contents);

const updated = await cb(config);

const serialized = serialize_changeset_config(updated);

if (serialized === config_contents) {
return false;
}

await write_changeset_config(serialized);
return true;
};

export const load_changeset_config = async (): Promise<WrittenConfig> =>
JSON.parse(await load_changeset_config_contents(CHANGESET_CONFIG_PATH));

export const load_changeset_config_contents = (path: string): Promise<string> =>
readFile(path, 'utf8');

export const write_changeset_config = (serialized: string): Promise<void> =>
writeFile(CHANGESET_CONFIG_PATH, serialized);

export const serialize_changeset_config = (config: WrittenConfig): string =>
JSON.stringify(config, null, 2) + '\n';

export const parse_changeset_config = (contents: string): WrittenConfig => JSON.parse(contents);
8 changes: 5 additions & 3 deletions src/lib/commit.task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ import type {Task} from './task/task.js';

export const Args = z
.object({
_: z.array(z.string(), {
description: 'the git commit message, the same as git commit -m or --message',
}),
_: z
.array(z.string(), {
description: 'the git commit message, the same as git commit -m or --message',
})
.default([]),
})
.strict();
export type Args = z.infer<typeof Args>;
Expand Down
4 changes: 3 additions & 1 deletion src/lib/config/gro.config.default.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type {GroConfigCreator} from './config.js';
import {base_path_to_source_id, LIB_DIR, LIB_DIRNAME} from '../util/paths.js';
import {exists} from '../util/exists.js';
import {load_package_json} from '$lib/util/package_json.js';

/**
* This is the default config that's passed to `gro.config.ts`
Expand Down Expand Up @@ -44,7 +45,8 @@ const config: GroConfigCreator = async () => {

export default config;

export const has_library = (): Promise<boolean> => exists(LIB_DIR);
export const has_library = async (): Promise<boolean> =>
(await exists(LIB_DIR)) && !!(await load_package_json()).exports;

export const has_server = (): Promise<boolean> => exists(SERVER_SOURCE_ID);
export const SERVER_SOURCE_ID = base_path_to_source_id(LIB_DIRNAME + '/server/server.ts');
Expand Down
1 change: 1 addition & 0 deletions src/lib/docs/tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ What is a `Task`? See [`task.md`](./task.md).
## all tasks

- [build](../build.task.ts) - build the project
- [changeset](../changeset.task.ts) - call changeset with gro patterns
- [check](../check.task.ts) - check that everything is ready to commit
- [clean](../clean.task.ts) - remove temporary dev and build files, and optionally prune git branches
- [commit](../commit.task.ts) - commit and push to a new branch
Expand Down
1 change: 1 addition & 0 deletions src/lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export type {GroConfig, GroConfigCreator} from './config/config.js';
export {type Gen, type GenContext} from './gen/gen.js';
export {type Task, type TaskContext, TaskError} from './task/task.js';

0 comments on commit 3ae04c5

Please sign in to comment.