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.
Files changed (39) hide show
  1. package/.eslintrc.cjs +9 -0
  2. package/.prettierignore +1 -0
  3. package/package.json +21 -0
  4. package/package.json.bak +21 -0
  5. package/src/config.ts +97 -0
  6. package/src/index.ts +16 -0
  7. package/src/stamp.ts +46 -0
  8. package/src/template.test.ts +69 -0
  9. package/src/uuid.ts +4 -0
  10. package/template/.gitattributes +2 -0
  11. package/template/.github/actions/bootstrap/action.yml +11 -0
  12. package/template/.github/workflows/ci.yml +17 -0
  13. package/template/.github/workflows/release.yml +68 -0
  14. package/template/Cargo.toml +6 -0
  15. package/template/package.json +47 -0
  16. package/template/prettier.config.mjs +1 -0
  17. package/template/rust/{{plug_slug}}/component/Cargo.toml +9 -0
  18. package/template/rust/{{plug_slug}}/component/src/lib.rs +82 -0
  19. package/template/rust/{{plug_slug}}/vst/Cargo.toml +13 -0
  20. package/template/rust/{{plug_slug}}/vst/about.hbs +13 -0
  21. package/template/rust/{{plug_slug}}/vst/about.toml +10 -0
  22. package/template/rust/{{plug_slug}}/vst/src/lib.rs +49 -0
  23. package/template/rustfmt.toml +0 -0
  24. package/template/web/eslint-config-custom/config.cjs +45 -0
  25. package/template/web/eslint-config-custom/package.json +5 -0
  26. package/template/web/tsconfig/package.json +4 -0
  27. package/template/web/tsconfig/tsconfig.json +20 -0
  28. package/template/web/{{plug_slug}}/.eslintrc.cjs +8 -0
  29. package/template/web/{{plug_slug}}/bundle.json +7 -0
  30. package/template/web/{{plug_slug}}/index.html +10 -0
  31. package/template/web/{{plug_slug}}/package.json +15 -0
  32. package/template/web/{{plug_slug}}/src/App.tsx +11 -0
  33. package/template/web/{{plug_slug}}/src/Layout.tsx +29 -0
  34. package/template/web/{{plug_slug}}/src/index.css +0 -0
  35. package/template/web/{{plug_slug}}/src/main.tsx +29 -0
  36. package/template/web/{{plug_slug}}/src/mock_infos.ts +24 -0
  37. package/template/web/{{plug_slug}}/tsconfig.json +7 -0
  38. package/template/web/{{plug_slug}}/vite.config.ts +33 -0
  39. package/tsconfig.json +4 -0
package/.eslintrc.cjs ADDED
@@ -0,0 +1,9 @@
1
+ module.exports = {
2
+ root: true,
3
+ extends: ["eslint-config-custom"],
4
+ parserOptions: {
5
+ project: ["./tsconfig.json"],
6
+ tsconfigRootDir: __dirname,
7
+ },
8
+ ignorePatterns: ["template/**"],
9
+ };
@@ -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
+ }
@@ -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,4 @@
1
+ const uuidHex = () =>
2
+ crypto.randomUUID().replace(/-/g, "").replace(/../g, "0x$&, ").slice(0, -1);
3
+
4
+ export default uuidHex;
@@ -0,0 +1,2 @@
1
+ *.wav filter=lfs diff=lfs merge=lfs -text
2
+ *.woff2 filter=lfs diff=lfs merge=lfs -text
@@ -0,0 +1,11 @@
1
+ name: "Bootstrap"
2
+ description: "Get ready to run ci or release"
3
+ runs:
4
+ using: "composite"
5
+ steps:
6
+ - run: brew install oven-sh/bun/bun
7
+ shell: bash
8
+ - run: bun install --frozen-lockfile
9
+ shell: bash
10
+ - run: bun run bootstrap
11
+ shell: bash
@@ -0,0 +1,17 @@
1
+ name: CI
2
+ run-name: CI
3
+
4
+ on:
5
+ push:
6
+ branches:
7
+ - "*"
8
+
9
+ jobs:
10
+ ci:
11
+ runs-on: macos-14
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+ with:
15
+ lfs: "true"
16
+ - uses: ./.github/actions/bootstrap
17
+ - run: bun run ci
@@ -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,6 @@
1
+ [workspace]
2
+ resolver = "2"
3
+ members = [
4
+ "rust/{{plug_slug}}/component",
5
+ "rust/{{plug_slug}}/vst",
6
+ ]
@@ -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,9 @@
1
+ [package]
2
+ name = "{{plug_slug}}_component"
3
+ edition = "2021"
4
+ rust-version = "1.79.0"
5
+ publish = false
6
+
7
+ [dependencies]
8
+ conformal_vst_wrapper = "0.1.0"
9
+ conformal_component = "0.1.0"
@@ -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,10 @@
1
+ accepted = [
2
+ "MIT",
3
+ "ISC",
4
+ "MPL-2.0",
5
+ "Apache-2.0 WITH LLVM-exception",
6
+ "Apache-2.0",
7
+ "Unicode-DFS-2016",
8
+ ]
9
+
10
+ ignore-dev-dependencies = true
@@ -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,5 @@
1
+ {
2
+ "name": "eslint-config-custom",
3
+ "main": "config.cjs",
4
+ "private": true
5
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "name": "tsconfig",
3
+ "private": true
4
+ }
@@ -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,8 @@
1
+ module.exports = {
2
+ root: true,
3
+ extends: ["eslint-config-custom"],
4
+ parserOptions: {
5
+ project: ["./tsconfig.json"],
6
+ tsconfigRootDir: __dirname,
7
+ },
8
+ };
@@ -0,0 +1,7 @@
1
+ {
2
+ "rustPackage": "{{plug_slug}}_vst",
3
+ "name": "{{plug_name}}",
4
+ "id": "com.{{task_marker}}.{{plug_slug}}",
5
+ "sig": "{{task_marker}}",
6
+ "version": "0.0.0"
7
+ }
@@ -0,0 +1,10 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ </head>
6
+ <body>
7
+ <div id="root"></div>
8
+ <script type="module" src="src/main.tsx"></script>
9
+ </body>
10
+ </html>
@@ -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,11 @@
1
+ import { DevModeTools } from "@conformal/plugin";
2
+ import Layout from "./Layout.tsx";
3
+
4
+ const App = () => (
5
+ <div>
6
+ <Layout />
7
+ <DevModeTools />
8
+ </div>
9
+ );
10
+
11
+ export default App;
@@ -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,7 @@
1
+ {
2
+ "extends": "tsconfig/tsconfig.json",
3
+ "include": ["src", "vite.config.ts"],
4
+ "compilerOptions": {
5
+ "types": ["vite/client"]
6
+ }
7
+ }
@@ -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
@@ -0,0 +1,4 @@
1
+ {
2
+ "extends": "tsconfig/tsconfig.json",
3
+ "include": ["src"]
4
+ }