Skip to content

Improve Plugins Developer Experience #60

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
807 changes: 0 additions & 807 deletions .yarn/releases/yarn-3.3.0.cjs

This file was deleted.

873 changes: 873 additions & 0 deletions .yarn/releases/yarn-3.5.1.cjs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion .yarnrc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ plugins:
- path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
spec: "@yarnpkg/plugin-workspace-tools"

yarnPath: .yarn/releases/yarn-3.3.0.cjs
yarnPath: .yarn/releases/yarn-3.5.1.cjs
5 changes: 1 addition & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"license": "MIT",
"packageManager": "yarn@3.3.0",
"packageManager": "yarn@3.5.1",
"workspaces": {
"packages": [
"packages/*"
Expand All @@ -9,13 +9,10 @@
"scripts": {
"postinstall": "node -e \"try { require('husky').install() } catch (e) {if (e.code !== 'MODULE_NOT_FOUND') throw e}\" && yarn build",
"stablestudio-plugin": "yarn workspace @stability/stablestudio-plugin",
"stablestudio-plugin-example": "yarn workspace @stability/stablestudio-plugin-example",
"stablestudio-plugin-stability": "yarn workspace @stability/stablestudio-plugin-stability",
"stablestudio-plugin-webgpu": "yarn workspace @stability/stablestudio-plugin-webgpu",
"stablestudio-plugin-webui": "yarn workspace @stability/stablestudio-plugin-webui",
"stablestudio-ui": "yarn workspace @stability/stablestudio-ui",
"dev:use-example-plugin": "cross-env VITE_USE_EXAMPLE_PLUGIN=true yarn dev",
"dev:use-webui-plugin": "cross-env VITE_USE_WEBUI_PLUGIN=true yarn dev",
"dev": "yarn workspaces foreach --all --interlaced --verbose --parallel --jobs unlimited run dev",
"build": "yarn workspaces foreach --all --interlaced --verbose --jobs unlimited run build",
"clean": "yarn workspaces foreach --all --interlaced --verbose --parallel --jobs unlimited run clean && rimraf node_modules"
Expand Down
1 change: 1 addition & 0 deletions packages/create-stablestudio-plugin/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
dist
1 change: 1 addition & 0 deletions packages/create-stablestudio-plugin/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# create-stablestudio-plugin
64 changes: 64 additions & 0 deletions packages/create-stablestudio-plugin/create-plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/* eslint-disable import/no-extraneous-dependencies */
import path from "path";

import chalk from "chalk";

import type { PackageManager } from "./helpers/get-pkg-manager";
import { tryGitInit } from "./helpers/git";
import { isFolderEmpty } from "./helpers/is-folder-empty";
import { isWriteable } from "./helpers/is-writeable";
import { makeDir } from "./helpers/make-dir";

import { installTemplate, TemplateType } from "./templates";

export async function createPlugin({
pluginName,
pluginPath,
packageManager,
template = "default",
}: {
pluginName: string;
pluginPath: string;
packageManager: PackageManager;
example?: string;
examplePath?: string;
template?: TemplateType;
}): Promise<void> {
const root = path.resolve(pluginPath);

if (!(await isWriteable(path.dirname(root)))) {
console.error(
"The plugin path is not writable, please check folder permissions and try again."
);
console.error(
"It is likely you do not have write permissions for this folder."
);
process.exit(1);
}

const appName = path.basename(root);

await makeDir(root);
if (!isFolderEmpty(root, appName)) {
process.exit(1);
}

console.log(`Creating a new StableStudio plugin in ${chalk.green(root)}.`);
console.log();

process.chdir(root);

await installTemplate({
pluginName,
root,
packageManager,
template,
});

if (tryGitInit(root)) {
console.log("Initialized a git repository.");
console.log();
}

console.log();
}
17 changes: 17 additions & 0 deletions packages/create-stablestudio-plugin/helpers/get-pkg-manager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export type PackageManager = "npm" | "pnpm" | "yarn";

export function getPkgManager(): PackageManager {
const userAgent = process.env.npm_config_user_agent;

if (userAgent) {
if (userAgent.startsWith("yarn")) {
return "yarn";
} else if (userAgent.startsWith("pnpm")) {
return "pnpm";
} else {
return "npm";
}
} else {
return "npm";
}
}
48 changes: 48 additions & 0 deletions packages/create-stablestudio-plugin/helpers/git.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/* eslint-disable import/no-extraneous-dependencies */
import { execSync } from "child_process";
import fs from "fs";
import path from "path";

function isInGitRepository(): boolean {
try {
execSync("git rev-parse --is-inside-work-tree", { stdio: "ignore" });
return true;
} catch (_) {}
return false;
}

function isInMercurialRepository(): boolean {
try {
execSync("hg --cwd . root", { stdio: "ignore" });
return true;
} catch (_) {}
return false;
}

export function tryGitInit(root: string): boolean {
let didInit = false;
try {
execSync("git --version", { stdio: "ignore" });
if (isInGitRepository() || isInMercurialRepository()) {
return false;
}

execSync("git init", { stdio: "ignore" });
didInit = true;

execSync("git checkout -b main", { stdio: "ignore" });

execSync("git add -A", { stdio: "ignore" });
execSync('git commit -m "Initial commit from Create StableStudio Plugin"', {
stdio: "ignore",
});
return true;
} catch (e) {
if (didInit) {
try {
fs.rmSync(path.join(root, ".git"), { recursive: true, force: true });
} catch (_) {}
}
return false;
}
}
76 changes: 76 additions & 0 deletions packages/create-stablestudio-plugin/helpers/install.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/* eslint-disable import/no-extraneous-dependencies */
import chalk from 'chalk'
import spawn from 'cross-spawn'
import type { PackageManager } from './get-pkg-manager'

interface InstallArgs {
/**
* Indicate whether to install packages using npm, pnpm or Yarn.
*/
packageManager: PackageManager
/**
* Indicate whether the given dependencies are devDependencies.
*/
devDependencies?: boolean
}

/**
* Spawn a package manager installation with either Yarn or NPM.
*
* @returns A Promise that resolves once the installation is finished.
*/
export function install(
root: string,
{ packageManager, devDependencies }: InstallArgs
): Promise<void> {
/**
* (p)npm-specific command-line flags.
*/
const npmFlags: string[] = []
/**
* Yarn-specific command-line flags.
*/
const yarnFlags: string[] = []
/**
* Return a Promise that resolves once the installation is finished.
*/
return new Promise((resolve, reject) => {
let args: string[]
let command = packageManager
const useYarn = packageManager === 'yarn'


args = ['install'];

/**
* Add any package manager-specific flags.
*/
if (useYarn) {
args.push(...yarnFlags)
} else {
args.push(...npmFlags)
}

/**
* Spawn the installation process.
*/
const child = spawn(command, args, {
stdio: 'inherit',
env: {
...process.env,
ADBLOCK: '1',
// we set NODE_ENV to development as pnpm skips dev
// dependencies when production
NODE_ENV: 'development',
DISABLE_OPENCOLLECTIVE: '1',
},
})
child.on('close', (code) => {
if (code !== 0) {
reject({ command: `${command} ${args.join(' ')}` })
return
}
resolve()
})
})
}
63 changes: 63 additions & 0 deletions packages/create-stablestudio-plugin/helpers/is-folder-empty.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/* eslint-disable import/no-extraneous-dependencies */
import fs from "fs";
import path from "path";

import chalk from "chalk";

export function isFolderEmpty(root: string, name: string): boolean {
const validFiles = [
".DS_Store",
".git",
".gitattributes",
".gitignore",
".gitlab-ci.yml",
".hg",
".hgcheck",
".hgignore",
".idea",
".npmignore",
".travis.yml",
"LICENSE",
"Thumbs.db",
"docs",
"mkdocs.yml",
"npm-debug.log",
"yarn-debug.log",
"yarn-error.log",
"yarnrc.yml",
".yarn",
];

const conflicts = fs
.readdirSync(root)
.filter((file) => !validFiles.includes(file))
// Support IntelliJ IDEA-based editors
.filter((file) => !/\.iml$/.test(file));

if (conflicts.length > 0) {
console.log(
`The directory ${chalk.green(name)} contains files that could conflict:`
);
console.log();
for (const file of conflicts) {
try {
const stats = fs.lstatSync(path.join(root, file));
if (stats.isDirectory()) {
console.log(` ${chalk.blue(file)}/`);
} else {
console.log(` ${file}`);
}
} catch {
console.log(` ${file}`);
}
}
console.log();
console.log(
"Either try using a new directory name, or remove the files listed above."
);
console.log();
return false;
}

return true;
}
10 changes: 10 additions & 0 deletions packages/create-stablestudio-plugin/helpers/is-writeable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import fs from "fs";

export async function isWriteable(directory: string): Promise<boolean> {
try {
await fs.promises.access(directory, (fs.constants || fs).W_OK);
return true;
} catch (err) {
return false;
}
}
8 changes: 8 additions & 0 deletions packages/create-stablestudio-plugin/helpers/make-dir.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import fs from "fs";

export function makeDir(
root: string,
options = { recursive: true }
): Promise<string | undefined> {
return fs.promises.mkdir(root, options);
}
20 changes: 20 additions & 0 deletions packages/create-stablestudio-plugin/helpers/validate-pkg.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// eslint-disable-next-line import/no-extraneous-dependencies
import validateProjectName from "validate-npm-package-name";

export function validateNpmName(name: string): {
valid: boolean;
problems?: string[];
} {
const nameValidation = validateProjectName(name);
if (nameValidation.validForNewPackages) {
return { valid: true };
}

return {
valid: false,
problems: [
...(nameValidation.errors || []),
...(nameValidation.warnings || []),
],
};
}
Loading