create-conformal 0.1.3
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/.eslintrc.cjs +9 -0
- package/.prettierignore +1 -0
- package/package.json +21 -0
- package/package.json.bak +21 -0
- package/src/config.ts +97 -0
- package/src/index.ts +16 -0
- package/src/stamp.ts +46 -0
- package/src/template.test.ts +69 -0
- package/src/uuid.ts +4 -0
- package/template/.gitattributes +2 -0
- package/template/.github/actions/bootstrap/action.yml +11 -0
- package/template/.github/workflows/ci.yml +17 -0
- package/template/.github/workflows/release.yml +68 -0
- package/template/Cargo.toml +6 -0
- package/template/package.json +47 -0
- package/template/prettier.config.mjs +1 -0
- package/template/rust/{{plug_slug}}/component/Cargo.toml +9 -0
- package/template/rust/{{plug_slug}}/component/src/lib.rs +82 -0
- package/template/rust/{{plug_slug}}/vst/Cargo.toml +13 -0
- package/template/rust/{{plug_slug}}/vst/about.hbs +13 -0
- package/template/rust/{{plug_slug}}/vst/about.toml +10 -0
- package/template/rust/{{plug_slug}}/vst/src/lib.rs +49 -0
- package/template/rustfmt.toml +0 -0
- package/template/web/eslint-config-custom/config.cjs +45 -0
- package/template/web/eslint-config-custom/package.json +5 -0
- package/template/web/tsconfig/package.json +4 -0
- package/template/web/tsconfig/tsconfig.json +20 -0
- package/template/web/{{plug_slug}}/.eslintrc.cjs +8 -0
- package/template/web/{{plug_slug}}/bundle.json +7 -0
- package/template/web/{{plug_slug}}/index.html +10 -0
- package/template/web/{{plug_slug}}/package.json +15 -0
- package/template/web/{{plug_slug}}/src/App.tsx +11 -0
- package/template/web/{{plug_slug}}/src/Layout.tsx +29 -0
- package/template/web/{{plug_slug}}/src/index.css +0 -0
- package/template/web/{{plug_slug}}/src/main.tsx +29 -0
- package/template/web/{{plug_slug}}/src/mock_infos.ts +24 -0
- package/template/web/{{plug_slug}}/tsconfig.json +7 -0
- package/template/web/{{plug_slug}}/vite.config.ts +33 -0
- package/tsconfig.json +4 -0
package/.eslintrc.cjs
ADDED
package/.prettierignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
template
|
package/package.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-conformal",
|
|
3
|
+
"version": "0.1.3",
|
|
4
|
+
"description": "Project generator script for conformal projects",
|
|
5
|
+
"homepage": "https://github.com/russellmcc/conformal",
|
|
6
|
+
"bugs": "https://github.com/russellmcc/conformal/issues",
|
|
7
|
+
"license": "ISC",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"lint": "tsc && eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
|
10
|
+
"check-format": "prettier -c .",
|
|
11
|
+
"format": "prettier --write ."
|
|
12
|
+
},
|
|
13
|
+
"bin": "./src/index.ts",
|
|
14
|
+
"type": "module",
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"@commander-js/extra-typings": "^12.1.0",
|
|
17
|
+
"@inquirer/prompts": "^5.3.8",
|
|
18
|
+
"commander": "^12.1.0",
|
|
19
|
+
"handlebars": "^4.7.8"
|
|
20
|
+
}
|
|
21
|
+
}
|
package/package.json.bak
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-conformal",
|
|
3
|
+
"version": "0.1.3",
|
|
4
|
+
"description": "Project generator script for conformal projects",
|
|
5
|
+
"homepage": "https://github.com/russellmcc/conformal",
|
|
6
|
+
"bugs": "https://github.com/russellmcc/conformal/issues",
|
|
7
|
+
"license": "ISC",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"lint": "tsc && eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
|
10
|
+
"check-format": "prettier -c .",
|
|
11
|
+
"format": "prettier --write ."
|
|
12
|
+
},
|
|
13
|
+
"bin": "./src/index.ts",
|
|
14
|
+
"type": "module",
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"@commander-js/extra-typings": "^12.1.0",
|
|
17
|
+
"@inquirer/prompts": "^5.3.8",
|
|
18
|
+
"commander": "^12.1.0",
|
|
19
|
+
"handlebars": "^4.7.8"
|
|
20
|
+
}
|
|
21
|
+
}
|
package/src/config.ts
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { Command } from "@commander-js/extra-typings";
|
|
2
|
+
import { input } from "@inquirer/prompts";
|
|
3
|
+
import uuidHex from "./uuid";
|
|
4
|
+
|
|
5
|
+
export type Config = {
|
|
6
|
+
proj_slug: string;
|
|
7
|
+
plug_slug: string;
|
|
8
|
+
plug_name: string;
|
|
9
|
+
vendor_name: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
type ConfigMetadata = {
|
|
13
|
+
key: keyof Config;
|
|
14
|
+
prompt: string;
|
|
15
|
+
description: string;
|
|
16
|
+
default?: string;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const toEnv = (
|
|
20
|
+
config: Config,
|
|
21
|
+
{ skipTodo } = { skipTodo: false },
|
|
22
|
+
): Record<string, string> => ({
|
|
23
|
+
...config,
|
|
24
|
+
class_id: uuidHex(),
|
|
25
|
+
edit_class_id: uuidHex(),
|
|
26
|
+
task_marker: skipTodo ? "DONE" : "TOD" + "O",
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const metadatas: ConfigMetadata[] = [
|
|
30
|
+
{
|
|
31
|
+
key: "proj_slug",
|
|
32
|
+
prompt: "Project slug (lower snake_case, e.g. `my_project`)",
|
|
33
|
+
description: "Slug for the project in lower snake_case, e.g. `my_project`",
|
|
34
|
+
default: "my_project",
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
key: "plug_slug",
|
|
38
|
+
prompt: "Plug-in slug (lower snake_case, e.g. `my_plugin`)",
|
|
39
|
+
description:
|
|
40
|
+
"The name of the first plug-in in lower snake_case, e.g. `my_plugin`",
|
|
41
|
+
default: "my_plugin",
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
key: "vendor_name",
|
|
45
|
+
prompt:
|
|
46
|
+
'Human-readable vendor name (DAWs often present plug-ins grouped by vendor). e.g., "My Project"?',
|
|
47
|
+
description:
|
|
48
|
+
"Human-readable vendor name, e.g. `My Project`. DAWs often present plug-ins grouped by vendor",
|
|
49
|
+
default: "My Project",
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
key: "plug_name",
|
|
53
|
+
prompt: "Human-readable plug-in name (e.g. `My Plug-in`)?",
|
|
54
|
+
description: "Human-readable vendor name, e.g. `My Plug-in`",
|
|
55
|
+
default: "My Plug-in",
|
|
56
|
+
},
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
const fromCli = async (argv?: readonly string[]): Promise<Partial<Config>> => {
|
|
60
|
+
const command = new Command();
|
|
61
|
+
for (const { key, description } of metadatas) {
|
|
62
|
+
if (key !== "proj_slug") {
|
|
63
|
+
command.option(`--${key} <${key}>`, description);
|
|
64
|
+
} else {
|
|
65
|
+
// Name is a positional argument
|
|
66
|
+
command.argument("<proj_slug>", description);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const processed = await command.parseAsync(argv);
|
|
70
|
+
|
|
71
|
+
const ret = Object.fromEntries(
|
|
72
|
+
Object.entries(processed.opts()).filter(([_, v]) => v !== undefined),
|
|
73
|
+
);
|
|
74
|
+
if (processed.args.length > 0) {
|
|
75
|
+
ret.proj_slug = processed.args[0];
|
|
76
|
+
}
|
|
77
|
+
return ret;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const doPrompt = async (metadata: ConfigMetadata): Promise<string> =>
|
|
81
|
+
input({ message: metadata.prompt, default: metadata.default });
|
|
82
|
+
|
|
83
|
+
const promptRemainder = async (config: Partial<Config>): Promise<Config> => {
|
|
84
|
+
const ret: Partial<Config> = { ...config };
|
|
85
|
+
for (const metadata of metadatas) {
|
|
86
|
+
if (metadata.key in ret) {
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
ret[metadata.key] = await doPrompt(metadata);
|
|
90
|
+
}
|
|
91
|
+
// Note that we've filled in all of config here, since metadatas must contain all configs!
|
|
92
|
+
// It would be cool to check this in ts but I don't know how
|
|
93
|
+
return ret as Config;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
export const getConfig = async (argv?: readonly string[]): Promise<Config> =>
|
|
97
|
+
await promptRemainder(await fromCli(argv));
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
import { getConfig, toEnv } from "./config";
|
|
4
|
+
import { stampTemplate } from "./stamp";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { $ } from "bun";
|
|
7
|
+
|
|
8
|
+
const config = await getConfig();
|
|
9
|
+
const dest = config.proj_slug;
|
|
10
|
+
await stampTemplate(
|
|
11
|
+
dest,
|
|
12
|
+
path.join(path.dirname(import.meta.path), "..", "template"),
|
|
13
|
+
toEnv(config),
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
await $`git init`.cwd(dest);
|
package/src/stamp.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { promises as fs } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { compile } from "handlebars";
|
|
4
|
+
|
|
5
|
+
const dirExists = async (dir: string) => {
|
|
6
|
+
try {
|
|
7
|
+
await fs.readdir(dir);
|
|
8
|
+
return true;
|
|
9
|
+
} catch {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const stampTemplate = async (
|
|
15
|
+
dest: string,
|
|
16
|
+
templateDir: string,
|
|
17
|
+
env: Record<string, string>,
|
|
18
|
+
) => {
|
|
19
|
+
if (await dirExists(dest)) {
|
|
20
|
+
throw new Error(`Directory already exists: ${dest}`);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const files = await fs.readdir(templateDir, {
|
|
24
|
+
recursive: true,
|
|
25
|
+
withFileTypes: true,
|
|
26
|
+
});
|
|
27
|
+
for (const file of files) {
|
|
28
|
+
const srcPath = path.join(file.path, file.name);
|
|
29
|
+
const destPath = path.join(
|
|
30
|
+
dest,
|
|
31
|
+
compile(path.relative(templateDir, srcPath))(env),
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
if (file.isDirectory()) {
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (await Bun.file(destPath).exists()) {
|
|
39
|
+
throw new Error(`File already exists: ${destPath}`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
await fs.mkdir(path.dirname(destPath), { recursive: true });
|
|
43
|
+
const srcContent = await Bun.file(srcPath).text();
|
|
44
|
+
await Bun.write(destPath, compile(srcContent)(env));
|
|
45
|
+
}
|
|
46
|
+
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { describe, test, expect } from "bun:test";
|
|
2
|
+
import { withDir } from "tmp-promise";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { $ } from "bun";
|
|
5
|
+
import { Config, toEnv } from "./config";
|
|
6
|
+
import { stampTemplate } from "./stamp";
|
|
7
|
+
|
|
8
|
+
const TEST_CONFIG: Config = {
|
|
9
|
+
proj_slug: "test",
|
|
10
|
+
plug_slug: "test_plug",
|
|
11
|
+
plug_name: "Test Plug",
|
|
12
|
+
vendor_name: "Test Project",
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const MINUTE = 60_000;
|
|
16
|
+
|
|
17
|
+
describe("create-conformal template", () => {
|
|
18
|
+
test(
|
|
19
|
+
"passes CI",
|
|
20
|
+
async () => {
|
|
21
|
+
await withDir(
|
|
22
|
+
async ({ path: tmpDir }) => {
|
|
23
|
+
const workspacePath = path.join(
|
|
24
|
+
import.meta.path,
|
|
25
|
+
"..",
|
|
26
|
+
"..",
|
|
27
|
+
"..",
|
|
28
|
+
"..",
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
// Note that bun skips dependencies when installing packages from local paths :'(,
|
|
32
|
+
// so instead, we use `npm pack` to create tarballs.
|
|
33
|
+
|
|
34
|
+
const localDependencies = ["scripts", "plugin"];
|
|
35
|
+
for (const dep of localDependencies) {
|
|
36
|
+
await $`npm pack --pack-destination=${tmpDir}`.cwd(
|
|
37
|
+
path.join(workspacePath, "web", dep),
|
|
38
|
+
);
|
|
39
|
+
expect(
|
|
40
|
+
Bun.file(
|
|
41
|
+
path.join(tmpDir, `conformal-${dep}-0.0.0.tgz`),
|
|
42
|
+
).exists(),
|
|
43
|
+
).resolves.toBe(true);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// stamp the template
|
|
47
|
+
const dest = path.join(tmpDir, TEST_CONFIG.proj_slug);
|
|
48
|
+
await stampTemplate(
|
|
49
|
+
dest,
|
|
50
|
+
path.join(workspacePath, "web", "create", "template"),
|
|
51
|
+
toEnv(TEST_CONFIG, { skipTodo: true }),
|
|
52
|
+
);
|
|
53
|
+
await $`git init`.cwd(dest);
|
|
54
|
+
|
|
55
|
+
// Note we use perl as a sed replacement because of https://github.com/oven-sh/bun/issues/13197,
|
|
56
|
+
// which makes sed unusable on macOS.
|
|
57
|
+
const perl_command = `s/"\\@conformal\\/([^"]+)": "workspace:\\*"/"\\@conformal\\/$1": "file:..\\/conformal-$1-0.0.0.tgz"/`;
|
|
58
|
+
await $`perl -pi -e ${perl_command} package.json`.cwd(dest);
|
|
59
|
+
|
|
60
|
+
// Make sure CI would pass.
|
|
61
|
+
await $`bun install`.cwd(dest);
|
|
62
|
+
await $`bun run ci`.cwd(dest);
|
|
63
|
+
},
|
|
64
|
+
{ unsafeCleanup: true },
|
|
65
|
+
);
|
|
66
|
+
},
|
|
67
|
+
3 * MINUTE,
|
|
68
|
+
);
|
|
69
|
+
});
|
package/src/uuid.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
run-name: Release
|
|
3
|
+
|
|
4
|
+
on:
|
|
5
|
+
push:
|
|
6
|
+
tags:
|
|
7
|
+
- "v*.*.*"
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
release:
|
|
11
|
+
permissions:
|
|
12
|
+
contents: write
|
|
13
|
+
runs-on: macos-14
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
with:
|
|
17
|
+
lfs: "true"
|
|
18
|
+
# This is from https://docs.github.com/en/actions/use-cases-and-examples/deploying/installing-an-apple-certificate-on-macos-runners-for-xcode-development
|
|
19
|
+
- name: Install the Apple certificate and provisioning profile
|
|
20
|
+
env:
|
|
21
|
+
APPLICATION_P12_BASE64: $\{{ secrets.APPLICATION_P12_BASE64 }}
|
|
22
|
+
APPLICATION_P12_PASSWORD: $\{{ secrets.APPLICATION_P12_PASSWORD }}
|
|
23
|
+
INSTALLER_P12_BASE64: $\{{ secrets.INSTALLER_P12_BASE64 }}
|
|
24
|
+
INSTALLER_P12_PASSWORD: $\{{ secrets.INSTALLER_P12_PASSWORD }}
|
|
25
|
+
KEYCHAIN_PASSWORD: $\{{ secrets.KEYCHAIN_PASSWORD }}
|
|
26
|
+
run: |
|
|
27
|
+
# create variables
|
|
28
|
+
APPLICATION_PATH=$RUNNER_TEMP/application.p12
|
|
29
|
+
INSTALLER_PATH=$RUNNER_TEMP/installer.p12
|
|
30
|
+
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
|
|
31
|
+
|
|
32
|
+
# import certificate and provisioning profile from secrets
|
|
33
|
+
echo -n "$APPLICATION_P12_BASE64" | base64 --decode -o $APPLICATION_PATH
|
|
34
|
+
echo -n "$INSTALLER_P12_BASE64" | base64 --decode -o $INSTALLER_PATH
|
|
35
|
+
|
|
36
|
+
# create temporary keychain
|
|
37
|
+
security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
|
|
38
|
+
security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
|
|
39
|
+
security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
|
|
40
|
+
|
|
41
|
+
# import certificate to keychain
|
|
42
|
+
security import $APPLICATION_PATH -P "$APPLICATION_P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
|
|
43
|
+
security import $INSTALLER_PATH -P "$INSTALLER_P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
|
|
44
|
+
security set-key-partition-list -S apple-tool:,apple: -k "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
|
|
45
|
+
security list-keychain -d user -s $KEYCHAIN_PATH > /dev/null
|
|
46
|
+
- name: Log in to notarytool
|
|
47
|
+
env:
|
|
48
|
+
NOTARYTOOL_CREDENTIALS_KEYCHAIN_ITEM: $\{{ secrets.NOTARYTOOL_CREDENTIALS_KEYCHAIN_ITEM }}
|
|
49
|
+
NOTARYTOOL_APPLE_ID: $\{{ secrets.NOTARYTOOL_APPLE_ID }}
|
|
50
|
+
NOTARYTOOL_DEVELOPER_TEAM_ID: $\{{ secrets.NOTARYTOOL_DEVELOPER_TEAM_ID }}
|
|
51
|
+
NOTARYTOOL_PASSWORD: $\{{ secrets.NOTARYTOOL_PASSWORD }}
|
|
52
|
+
run: |
|
|
53
|
+
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
|
|
54
|
+
xcrun notarytool store-credentials "$NOTARYTOOL_CREDENTIALS_KEYCHAIN_ITEM" --apple-id "$NOTARYTOOL_APPLE_ID" --team-id "$NOTARYTOOL_DEVELOPER_TEAM_ID" --password "$NOTARYTOOL_PASSWORD" --keychain "$KEYCHAIN_PATH"
|
|
55
|
+
- uses: ./.github/actions/bootstrap
|
|
56
|
+
- run: bun run ci
|
|
57
|
+
- run: NOTARYTOOL_KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db bun run package {{plug_slug}} --dist
|
|
58
|
+
env:
|
|
59
|
+
DEVELOPER_ID_APPLICATION: $\{{ secrets.DEVELOPER_ID_APPLICATION }}
|
|
60
|
+
DEVELOPER_ID_INSTALLER: $\{{ secrets.DEVELOPER_INSTALLER_ID }}
|
|
61
|
+
NOTARYTOOL_CREDENTIALS_KEYCHAIN_ITEM: $\{{ secrets.NOTARYTOOL_CREDENTIALS_KEYCHAIN_ITEM }}
|
|
62
|
+
- name: Release
|
|
63
|
+
uses: softprops/action-gh-release@v2
|
|
64
|
+
with:
|
|
65
|
+
prerelease: $\{{ contains(github.ref, '-') }}
|
|
66
|
+
body: "Release $\{{ github.ref_name }}" # Prevent auto generation of release notes
|
|
67
|
+
files: |
|
|
68
|
+
target/release/{{plug_name}}.dmg
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{proj_slug}}",
|
|
3
|
+
"private": true,
|
|
4
|
+
"workspaces": [
|
|
5
|
+
"web/*"
|
|
6
|
+
],
|
|
7
|
+
"scripts": {
|
|
8
|
+
"bootstrap": "conformal-scripts bootstrap",
|
|
9
|
+
"ci": "conformal-scripts ci",
|
|
10
|
+
"check-lfs": "conformal-scripts check-lfs",
|
|
11
|
+
"check-todo": "conformal-scripts check-todo",
|
|
12
|
+
"check-format": "conformal-scripts check-format",
|
|
13
|
+
"format": "conformal-scripts format",
|
|
14
|
+
"web-dev": "conformal-scripts web-script -s dev",
|
|
15
|
+
"web-build": "conformal-scripts web-script -s build",
|
|
16
|
+
"web-lint": "conformal-scripts web-script -s lint",
|
|
17
|
+
"web-test": "bun test",
|
|
18
|
+
"rust-miri": "PROPTEST_DISABLE_FAILURE_PERSISTENCE=true MIRIFLAGS='-Zmiri-env-forward=PROPTEST_DISABLE_FAILURE_PERSISTENCE' conformal-scripts cargo +nightly miri test",
|
|
19
|
+
"rust-build": "conformal-scripts cargo build",
|
|
20
|
+
"rust-lint": "conformal-scripts cargo clippy",
|
|
21
|
+
"rust-test": "conformal-scripts cargo test",
|
|
22
|
+
"rust-bench": "conformal-scripts cargo bench",
|
|
23
|
+
"package": "conformal-scripts web-script -s package",
|
|
24
|
+
"validate": "conformal-scripts web-script -s validate"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@types/bun": "1.1.0",
|
|
28
|
+
"@typescript-eslint/eslint-plugin": "^7.7.0",
|
|
29
|
+
"@typescript-eslint/parser": "^7.7.0",
|
|
30
|
+
"@vitejs/plugin-react-swc": "^3.6.0",
|
|
31
|
+
"@types/react": "^18.2.79",
|
|
32
|
+
"@types/react-dom": "^18.2.25",
|
|
33
|
+
"eslint": "^8.57.0",
|
|
34
|
+
"eslint-plugin-prefer-arrow-functions": "^3.3.2",
|
|
35
|
+
"eslint-plugin-react": "^7.34.1",
|
|
36
|
+
"eslint-plugin-react-hooks": "^4.6.0",
|
|
37
|
+
"eslint-plugin-react-refresh": "^0.4.6",
|
|
38
|
+
"prettier": "^3.2.5",
|
|
39
|
+
"react": "^18.2.0",
|
|
40
|
+
"react-dom": "^18.2.0",
|
|
41
|
+
"typescript": "^5.4.5",
|
|
42
|
+
"vite": "^5.2.9",
|
|
43
|
+
"@conformal/scripts": "^0.1.3",
|
|
44
|
+
"@conformal/plugin": "^0.1.3",
|
|
45
|
+
"rollup-plugin-license": "^3.4.0"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default {};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
#![warn(
|
|
2
|
+
nonstandard_style,
|
|
3
|
+
rust_2018_idioms,
|
|
4
|
+
future_incompatible,
|
|
5
|
+
clippy::pedantic,
|
|
6
|
+
clippy::todo
|
|
7
|
+
)]
|
|
8
|
+
#![allow(
|
|
9
|
+
clippy::type_complexity,
|
|
10
|
+
clippy::cast_sign_loss,
|
|
11
|
+
clippy::cast_possible_wrap,
|
|
12
|
+
clippy::default_trait_access
|
|
13
|
+
)]
|
|
14
|
+
|
|
15
|
+
use conformal_component::audio::{channels, channels_mut, Buffer, BufferMut};
|
|
16
|
+
use conformal_component::effect::Effect as EffectTrait;
|
|
17
|
+
use conformal_component::parameters::{self, BufferStates, Flags, InfoRef, TypeSpecificInfoRef};
|
|
18
|
+
use conformal_component::pzip;
|
|
19
|
+
use conformal_component::{Component as ComponentTrait, ProcessingEnvironment, Processor};
|
|
20
|
+
|
|
21
|
+
const PARAMETERS: [InfoRef<'static, &'static str>; 2] = [
|
|
22
|
+
InfoRef {
|
|
23
|
+
title: "Bypass",
|
|
24
|
+
short_title: "Bypass",
|
|
25
|
+
unique_id: "bypass",
|
|
26
|
+
flags: Flags { automatable: true },
|
|
27
|
+
type_specific: TypeSpecificInfoRef::Switch { default: false },
|
|
28
|
+
},
|
|
29
|
+
InfoRef {
|
|
30
|
+
title: "Gain",
|
|
31
|
+
short_title: "Gain",
|
|
32
|
+
unique_id: "gain",
|
|
33
|
+
flags: Flags { automatable: true },
|
|
34
|
+
type_specific: TypeSpecificInfoRef::Numeric {
|
|
35
|
+
default: 100.,
|
|
36
|
+
valid_range: 0f32..=100.,
|
|
37
|
+
units: "%",
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
#[derive(Clone, Debug, Default)]
|
|
43
|
+
pub struct Component {}
|
|
44
|
+
|
|
45
|
+
#[derive(Clone, Debug, Default)]
|
|
46
|
+
pub struct Effect {}
|
|
47
|
+
|
|
48
|
+
impl Processor for Effect {
|
|
49
|
+
fn set_processing(&mut self, _processing: bool) {}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
impl EffectTrait for Effect {
|
|
53
|
+
fn handle_parameters<P: parameters::States>(&mut self, _: P) {}
|
|
54
|
+
fn process<P: BufferStates, I: Buffer, O: BufferMut>(
|
|
55
|
+
&mut self,
|
|
56
|
+
parameters: P,
|
|
57
|
+
input: &I,
|
|
58
|
+
output: &mut O,
|
|
59
|
+
) {
|
|
60
|
+
for (input_channel, output_channel) in channels(input).zip(channels_mut(output)) {
|
|
61
|
+
for ((input_sample, output_sample), (gain, bypass)) in input_channel
|
|
62
|
+
.iter()
|
|
63
|
+
.zip(output_channel.iter_mut())
|
|
64
|
+
.zip(pzip!(parameters[numeric "gain", switch "bypass"]))
|
|
65
|
+
{
|
|
66
|
+
*output_sample = *input_sample * (if bypass { 1.0 } else { gain / 100.0 });
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
impl ComponentTrait for Component {
|
|
73
|
+
type Processor = Effect;
|
|
74
|
+
|
|
75
|
+
fn parameter_infos(&self) -> Vec<parameters::Info> {
|
|
76
|
+
parameters::to_infos(&PARAMETERS)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
fn create_processor(&self, _env: &ProcessingEnvironment) -> Self::Processor {
|
|
80
|
+
Default::default()
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "{{plug_slug}}_vst"
|
|
3
|
+
edition = "2021"
|
|
4
|
+
rust-version = "1.79.0"
|
|
5
|
+
publish = false
|
|
6
|
+
|
|
7
|
+
[lib]
|
|
8
|
+
crate-type = ["cdylib"]
|
|
9
|
+
|
|
10
|
+
[dependencies]
|
|
11
|
+
conformal_vst_wrapper = "0.1.0"
|
|
12
|
+
vst3 = "0.1.2"
|
|
13
|
+
{{plug_slug}}_component = { path = "../component" }
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{{task_marker}}: Add end-user license information here! This file will be shown
|
|
2
|
+
in the installer. This program was created using a variety of open source
|
|
3
|
+
software, whose licenses are listed below. All distributions of this software
|
|
4
|
+
must contain these license notices.
|
|
5
|
+
|
|
6
|
+
\{{#each licenses}}
|
|
7
|
+
\{{#each used_by}} -----
|
|
8
|
+
\{{crate.name}}
|
|
9
|
+
\{{crate.version}} (\{{{../name}}})
|
|
10
|
+
|
|
11
|
+
\{{{../text}}}
|
|
12
|
+
\{{/each}}
|
|
13
|
+
\{{/each}}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#![warn(
|
|
2
|
+
nonstandard_style,
|
|
3
|
+
rust_2018_idioms,
|
|
4
|
+
future_incompatible,
|
|
5
|
+
clippy::pedantic,
|
|
6
|
+
clippy::todo
|
|
7
|
+
)]
|
|
8
|
+
#![allow(
|
|
9
|
+
clippy::type_complexity,
|
|
10
|
+
clippy::cast_sign_loss,
|
|
11
|
+
clippy::cast_possible_wrap,
|
|
12
|
+
clippy::default_trait_access
|
|
13
|
+
)]
|
|
14
|
+
|
|
15
|
+
use {{plug_slug}}_component::Component;
|
|
16
|
+
|
|
17
|
+
use conformal_vst_wrapper::{ClassID, ClassInfo, EffectClass, HostInfo, Info};
|
|
18
|
+
|
|
19
|
+
const CID: ClassID = [
|
|
20
|
+
{{class_id}}
|
|
21
|
+
];
|
|
22
|
+
const EDIT_CONTROLLER_CID: ClassID = [
|
|
23
|
+
{{edit_class_id}}
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
conformal_vst_wrapper::wrap_factory!(
|
|
27
|
+
&const {
|
|
28
|
+
[&EffectClass {
|
|
29
|
+
info: ClassInfo {
|
|
30
|
+
name: "{{plug_name}}",
|
|
31
|
+
cid: CID,
|
|
32
|
+
edit_controller_cid: EDIT_CONTROLLER_CID,
|
|
33
|
+
ui_initial_size: conformal_vst_wrapper::UiSize {
|
|
34
|
+
width: 400,
|
|
35
|
+
height: 400,
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
factory: |_: &HostInfo| -> Component { Default::default() },
|
|
39
|
+
category: "Fx",
|
|
40
|
+
bypass_id: "bypass",
|
|
41
|
+
}]
|
|
42
|
+
},
|
|
43
|
+
Info {
|
|
44
|
+
vendor: "{{vendor_name}}",
|
|
45
|
+
url: "{{task_marker}} add URL",
|
|
46
|
+
email: "test@example.com",
|
|
47
|
+
version: "1.0.0",
|
|
48
|
+
}
|
|
49
|
+
);
|
|
File without changes
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
root: true,
|
|
3
|
+
env: { browser: true, es2020: true },
|
|
4
|
+
extends: [
|
|
5
|
+
"eslint:recommended",
|
|
6
|
+
"plugin:@typescript-eslint/recommended-type-checked",
|
|
7
|
+
"plugin:@typescript-eslint/stylistic-type-checked",
|
|
8
|
+
"plugin:react-hooks/recommended",
|
|
9
|
+
"plugin:react/recommended",
|
|
10
|
+
"plugin:react/jsx-runtime",
|
|
11
|
+
],
|
|
12
|
+
ignorePatterns: ["dist", ".eslintrc.cjs"],
|
|
13
|
+
parser: "@typescript-eslint/parser",
|
|
14
|
+
parserOptions: {
|
|
15
|
+
ecmaVersion: "latest",
|
|
16
|
+
sourceType: "module",
|
|
17
|
+
},
|
|
18
|
+
plugins: ["react-refresh", "prefer-arrow-functions"],
|
|
19
|
+
rules: {
|
|
20
|
+
"@typescript-eslint/no-unused-vars": [
|
|
21
|
+
"error",
|
|
22
|
+
{
|
|
23
|
+
argsIgnorePattern: "^_",
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
"react-refresh/only-export-components": [
|
|
27
|
+
"warn",
|
|
28
|
+
{ allowConstantExport: true },
|
|
29
|
+
],
|
|
30
|
+
"react-hooks/exhaustive-deps": "error",
|
|
31
|
+
"prefer-arrow-functions/prefer-arrow-functions": [
|
|
32
|
+
"error",
|
|
33
|
+
{
|
|
34
|
+
returnStyle: "implicit",
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
"@typescript-eslint/consistent-type-definitions": ["error", "type"],
|
|
38
|
+
"no-warning-comments": "error",
|
|
39
|
+
},
|
|
40
|
+
settings: {
|
|
41
|
+
react: {
|
|
42
|
+
version: "detect",
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "es2020",
|
|
4
|
+
"useDefineForClassFields": true,
|
|
5
|
+
"lib": ["es2020", "dom", "dom.iterable"],
|
|
6
|
+
"module": "esnext",
|
|
7
|
+
"skipLibCheck": true,
|
|
8
|
+
/* Bundler mode */
|
|
9
|
+
"moduleResolution": "bundler",
|
|
10
|
+
"allowImportingTsExtensions": true,
|
|
11
|
+
"isolatedModules": true,
|
|
12
|
+
"noEmit": true,
|
|
13
|
+
"jsx": "react-jsx",
|
|
14
|
+
/* Linting */
|
|
15
|
+
"strict": true,
|
|
16
|
+
"noUnusedLocals": true,
|
|
17
|
+
"noUnusedParameters": true,
|
|
18
|
+
"noFallthroughCasesInSwitch": true
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{plug_slug}}",
|
|
3
|
+
"private": true,
|
|
4
|
+
"scripts": {
|
|
5
|
+
"dev": "vite",
|
|
6
|
+
"build": "tsc && vite build",
|
|
7
|
+
"lint": "tsc && eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
|
8
|
+
"preview": "vite preview",
|
|
9
|
+
"check-format": "prettier --ignore-path .gitignore -c .",
|
|
10
|
+
"format": "prettier --ignore-path .gitignore --write .",
|
|
11
|
+
"package": "conformal-scripts package",
|
|
12
|
+
"validate": "conformal-scripts validate"
|
|
13
|
+
},
|
|
14
|
+
"type": "module"
|
|
15
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { useNumericParam } from "@conformal/plugin";
|
|
2
|
+
|
|
3
|
+
const Layout = () => {
|
|
4
|
+
const { value: gain, set: setGain } = useNumericParam("gain");
|
|
5
|
+
|
|
6
|
+
return (
|
|
7
|
+
<div>
|
|
8
|
+
<p>Current gain: {gain}%</p>
|
|
9
|
+
<p>
|
|
10
|
+
<span
|
|
11
|
+
onClick={() => {
|
|
12
|
+
setGain(Math.max(0, gain - 10));
|
|
13
|
+
}}
|
|
14
|
+
>
|
|
15
|
+
-
|
|
16
|
+
</span>
|
|
17
|
+
<span
|
|
18
|
+
onClick={() => {
|
|
19
|
+
setGain(Math.min(100, gain + 10));
|
|
20
|
+
}}
|
|
21
|
+
>
|
|
22
|
+
+
|
|
23
|
+
</span>
|
|
24
|
+
</p>
|
|
25
|
+
</div>
|
|
26
|
+
);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export default Layout;
|
|
File without changes
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Temporary workaround for https://github.com/oven-sh/bun/issues/4890
|
|
2
|
+
/// <reference lib="dom" />
|
|
3
|
+
/// <reference lib="dom.iterable" />
|
|
4
|
+
|
|
5
|
+
import App from "./App.tsx";
|
|
6
|
+
import * as Jotai from "jotai";
|
|
7
|
+
import { StrictMode, Suspense } from "react";
|
|
8
|
+
import * as Client from "react-dom/client";
|
|
9
|
+
import { Provider } from "@conformal/plugin";
|
|
10
|
+
import "./index.css";
|
|
11
|
+
import infos from "./mock_infos.ts";
|
|
12
|
+
|
|
13
|
+
const domElement = document.querySelector("#root");
|
|
14
|
+
|
|
15
|
+
if (!(domElement == null)) {
|
|
16
|
+
Client.createRoot(domElement).render(
|
|
17
|
+
<StrictMode>
|
|
18
|
+
<Jotai.Provider>
|
|
19
|
+
<Provider mockInfos={infos}>
|
|
20
|
+
<Suspense fallback={<></>}>
|
|
21
|
+
<App />
|
|
22
|
+
</Suspense>
|
|
23
|
+
</Provider>
|
|
24
|
+
</Jotai.Provider>
|
|
25
|
+
</StrictMode>,
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Info } from "@conformal/plugin";
|
|
2
|
+
|
|
3
|
+
const infos = new Map<string, Info>(
|
|
4
|
+
Object.entries({
|
|
5
|
+
bypass: {
|
|
6
|
+
title: "Bypass",
|
|
7
|
+
type_specific: {
|
|
8
|
+
t: "switch",
|
|
9
|
+
default: false,
|
|
10
|
+
},
|
|
11
|
+
},
|
|
12
|
+
gain: {
|
|
13
|
+
title: "Gain",
|
|
14
|
+
type_specific: {
|
|
15
|
+
t: "numeric",
|
|
16
|
+
default: 100,
|
|
17
|
+
valid_range: [0, 100],
|
|
18
|
+
units: "%",
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
}),
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
export default infos;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import react from "@vitejs/plugin-react-swc";
|
|
2
|
+
import license from "rollup-plugin-license";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
|
|
5
|
+
/** @type {import('vite').UserConfig} */
|
|
6
|
+
export default {
|
|
7
|
+
plugins: [
|
|
8
|
+
react(),
|
|
9
|
+
license({
|
|
10
|
+
thirdParty: {
|
|
11
|
+
output: {
|
|
12
|
+
file: join(__dirname, "installer_resources", "license.txt"),
|
|
13
|
+
template: (dependencies) =>
|
|
14
|
+
dependencies
|
|
15
|
+
.map(
|
|
16
|
+
(dependency) =>
|
|
17
|
+
`-----
|
|
18
|
+
${dependency.name} ${dependency.version} (${dependency.license})
|
|
19
|
+
|
|
20
|
+
${dependency.licenseText}
|
|
21
|
+
`,
|
|
22
|
+
)
|
|
23
|
+
.join("\n"),
|
|
24
|
+
},
|
|
25
|
+
allow: {
|
|
26
|
+
failOnUnlicensed: true,
|
|
27
|
+
failOnViolation: true,
|
|
28
|
+
test: "(MIT OR ISC)",
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
}),
|
|
32
|
+
],
|
|
33
|
+
};
|
package/tsconfig.json
ADDED