Skip to content

Commit 3d6aad3

Browse files
committed
port language directory upgrades to plugin directory
1 parent 2f7672d commit 3d6aad3

File tree

11 files changed

+526
-77
lines changed

11 files changed

+526
-77
lines changed

.github/workflows/build-deploy.yml

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
name: Build and deploy directory
2+
on:
3+
push:
4+
branches:
5+
- main
6+
7+
jobs:
8+
build:
9+
name: Build directory
10+
11+
runs-on: ubuntu-latest
12+
13+
steps:
14+
- name: Checkout repository
15+
uses: actions/checkout@v4
16+
17+
- name: Enable corepack
18+
run: corepack enable
19+
20+
- name: Setup node
21+
uses: actions/setup-node@v4
22+
with:
23+
node-version: 22
24+
cache: yarn
25+
26+
- name: yarn install
27+
run: yarn install --immutable
28+
29+
- name: yarn build
30+
run: yarn build
31+
32+
- name: Upload artifacts
33+
id: deployment
34+
uses: actions/upload-pages-artifact@v3
35+
with:
36+
path: dist/
37+
38+
deploy:
39+
needs: build
40+
41+
name: Deploy directory
42+
43+
permissions:
44+
pages: write
45+
id-token: write
46+
47+
environment:
48+
name: github-pages
49+
url: ${{ steps.deployment.outputs.page_url }}
50+
51+
runs-on: ubuntu-latest
52+
53+
steps:
54+
- name: Deploy to GitHub Pages
55+
id: deployment
56+
uses: actions/deploy-pages@v4

README.md

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,49 @@
33
This repository acts as a directory for Conductor plugins officially supported
44
by Source Academy.
55

6+
## Using this directory
7+
8+
### As a static asset
9+
10+
You may import the `plugins` array directly after installing this repository
11+
(as `import { plugins } from 'plugin-directory'`). This will cause the array
12+
to be bundled together at build time.
13+
14+
Caution: when including this repository as a dependency in your `package.json`,
15+
**be sure to specify a specific commit hash or tag**.
16+
Failure to do this may cause problems with CI/CD due to lockfile issues when this
17+
directory is updated.
18+
19+
### Retrieved dynamically
20+
21+
The `plugins` array is available dynamically at
22+
[`https://source-academy.github.io/plugin-directory/directory.json`](https://source-academy.github.io/plugin-directory/directory.json).
23+
24+
In this case, you probably do not want to bundle the array at build time. You may
25+
bundle only the utility functions by importing from `'plugin-directory/dist/util'` instead.
26+
27+
You can reconstruct the `pluginMap` using the `generatePluginMap` utility function.
28+
629
## Plugin definitions
730

831
Plugin definitions should follow the `IPluginDefinition` interface.
932

10-
Plugins consist of a name, an optional description, and a record mapping
33+
Plugins consist of an ID, a name, an optional description, and a record mapping
1134
execution environment to a URL to the plugin's entrypoint file.
1235

13-
## Adding a new Plugin
14-
1536
To add a new plugin to the plugins directory, define your plugin according to
16-
the interface, and add the definition to the `plugins` array in `src/index.ts`.
17-
18-
Please export your plugin definitions `as const`.
37+
the interface, and add the definition to the `plugins` array in `src/plugins.ts`.
1938

2039
For plugins meant to use Conductor's Module API, create an entry for
2140
`PluginType.MODULE`, and do not create one for `PluginType.RUNNER`.
2241

2342
For plugins meant to use the Source Academy Frontend's API, create an entry for
2443
`PluginType.WEB`.
44+
45+
## Local testing
46+
47+
You may test any changes (e.g. adding your new language) locally by running `yarn start`.
48+
This starts a development server and compiles the directory in watch mode (recompiling on any changes).
49+
50+
The directory will be available at [`http://localhost:8126/directory.json`](http://localhost:8126/directory.json).
51+
On the Source Academy frontend, set the feature flag `plugindir.url` to point to this location.

package.json

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
{
22
"name": "plugin-directory",
33
"packageManager": "[email protected]",
4-
"version": "0.0.1",
4+
"version": "0.0.2",
55
"description": "This repository acts as a directory for Conductor plugins officially supported by Source Academy.",
66
"type": "module",
77
"main": "dist/index.js",
88
"scripts": {
9-
"build": "rollup -c",
9+
"build": "rollup -c && yarn make-json",
10+
"start": "rollup -cw --watch.onEnd='yarn make-json' & http-server ./dist -p8126 -c-1 --cors &",
11+
"make-json": "node ./scripts/make-json.js > dist/directory.json",
1012
"prepack": "yarn build"
1113
},
1214
"repository": {
@@ -23,9 +25,9 @@
2325
},
2426
"homepage": "https://github.com/source-academy/plugin-directory#readme",
2527
"devDependencies": {
26-
"@rollup/plugin-node-resolve": "^16.0.0",
2728
"@rollup/plugin-terser": "^0.4.4",
2829
"@rollup/plugin-typescript": "^12.1.2",
30+
"http-server": "^14.1.1",
2931
"rollup": "^4.34.1",
3032
"tslib": "^2.8.1",
3133
"typescript": "^5.5.3"

rollup.config.js

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
import nodeResolve from "@rollup/plugin-node-resolve";
21
import terser from "@rollup/plugin-terser";
32
import typescript from "@rollup/plugin-typescript";
43

5-
export default [{
6-
plugins: [nodeResolve(), typescript()],
7-
input: "src/index.ts",
4+
export default {
5+
plugins: [typescript()],
6+
input: ["src/index.ts", "src/plugins.ts", "src/util.ts"],
87
output: {
98
plugins: [terser({
109
module: true,
@@ -17,4 +16,4 @@ export default [{
1716
format: "es",
1817
sourcemap: true,
1918
}
20-
}];
19+
};

scripts/make-json.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { plugins } from "../dist/plugins.js";
2+
3+
console.log(JSON.stringify(plugins));

src/index.ts

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,3 @@
1-
import { soundsPlugin } from "./plugins/sounds";
2-
import type { IPluginDefinition } from "./types/IPluginDefinition";
3-
4-
function toRecord<const P extends IPluginDefinition[]>(plugins: P) {
5-
const pluginRecord: Record<P[number]["name"], IPluginDefinition> = {} as any; // to be populated
6-
for (const plugin of plugins) {
7-
// @ts-expect-error
8-
pluginRecord[plugin.name] = plugin;
9-
}
10-
return pluginRecord;
11-
}
12-
13-
export const plugins = /*#__PURE__*/ toRecord([
14-
soundsPlugin
15-
]);
16-
17-
export { PluginType } from "./types";
1+
export * from "./types";
2+
export * from "./plugins";
3+
export * from "./util";

src/plugins.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { IPluginDefinition } from "./types";
2+
import { generatePluginMap } from "./util";
3+
4+
export const plugins: IPluginDefinition[] = [
5+
];
6+
7+
export const pluginMap: Map<string, IPluginDefinition> = /*#__PURE__*/ generatePluginMap(plugins);
8+
9+
if (plugins.length !== pluginMap.size) {
10+
console.warn("Non-unique plugin ID in plugin directory");
11+
}
12+
13+
export default plugins;

src/plugins/sounds.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import { IPluginDefinition } from "../types";
22

3-
export const soundsPlugin = {
4-
name: "sounds",
3+
export const soundsPlugin: IPluginDefinition = {
4+
id: "sounds",
5+
name: "Sounds",
56
resolutions: {
67
// TODO: add sounds plugin resolutions
78
}
8-
} as const satisfies IPluginDefinition;
9+
};

src/types/IPluginDefinition.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
11
import type { PluginType } from "./PluginType";
22

33
export interface IPluginDefinition {
4-
name: string;
5-
description?: string;
6-
resolutions: Partial<Record<PluginType, string>>;
4+
/** The plugin's identifier. */
5+
readonly id: string;
6+
7+
/** The name of the plugin. */
8+
readonly name: string;
9+
10+
/** An optional description of the plugin. */
11+
readonly description?: string;
12+
13+
/** The paths to the plugin's script file, by target. */
14+
readonly resolutions: Partial<Record<PluginType, string>>;
715
}

src/util.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { IPluginDefinition } from "./types";
2+
3+
export function generatePluginMap(plugins: IPluginDefinition[]): Map<string, IPluginDefinition> {
4+
return new Map(plugins.map(plugin => [plugin.id, plugin]));
5+
}
6+
7+
/**
8+
* Retrieve by ID a definition for a plugin from the directory.
9+
* @param pluginMap The plugin map of the directory.
10+
* @param pluginId The ID of the plugin to get the definition of.
11+
* @returns The retrieved plugin definition, or undefined if not found.
12+
*/
13+
export function getPluginDefinition(pluginMap: Map<string, IPluginDefinition>, pluginId: string) {
14+
return pluginMap.get(pluginId);
15+
}

0 commit comments

Comments
 (0)