Skip to content

Commit

Permalink
[tooltip] Adds new npm run createPackage script
Browse files Browse the repository at this point in the history
Details:
- Adds new private package @sigma/template
- Adds new TypeScript script bin/create-package.ts, that asks the dev
  the new package name, copies the proper files, updates the proper
  registries
- Adds ts-node dependency, `createPackage` NPM script
- Updates README.md
  • Loading branch information
jacomyal committed Sep 19, 2024
1 parent 9e6d012 commit 9ee789b
Show file tree
Hide file tree
Showing 10 changed files with 337 additions and 0 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,12 @@ This will open the Storybook in your web browser, which live reloads when you mo
You can contribute by submitting [issues tickets](http://github.com/jacomyal/sigma.js/issues) and proposing [pull requests](http://github.com/jacomyal/sigma.js/pulls). Make sure that tests and linting pass before submitting any pull request.

You can also browse the related documentation [here](https://github.com/jacomyal/sigma.js/tree/main/CONTRIBUTING.md).

## How to start a new package

Run `npm run createPackage` from the project root. It will:

- Ask you the new package name
- Copy the `packages/template` folder
- Update the new package `package.json` entries (name, description, exports)
- Update various other files (buildable packages list in `tsconfig.json`, Preconstruct compatible packages list in `package.json`...)
117 changes: 117 additions & 0 deletions bin/create-package.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/* eslint-env node */
import * as process from "node:process";
import * as path from "path";
import * as readline from "readline";
import { spawn } from "child_process";
import { promises as fs } from "fs";

async function readJSONFile<T = unknown>(filePath: string): Promise<T> {
const fileContent = await fs.readFile(filePath, "utf8");
return JSON.parse(fileContent);
}

async function writeJSONFile(filePath: string, data: unknown) {
await fs.writeFile(filePath, JSON.stringify(data, null, 2), "utf8");
}

function prompt(question: string): Promise<string> {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});

return new Promise((resolve) =>
rl.question(question, (answer) => {
rl.close();
resolve(answer);
}),
);
}

async function copyFolder(src: string, dest: string) {
await fs.mkdir(dest, { recursive: true });
const entries = await fs.readdir(src, { withFileTypes: true });

const copyPromises = entries.map(async (entry) => {
const srcPath = path.join(src, entry.name);
const destPath = path.join(dest, entry.name);

if (entry.isDirectory()) {
return copyFolder(srcPath, destPath);
} else {
return fs.copyFile(srcPath, destPath);
}
});

await Promise.all(copyPromises);
}

function runCommand(command: string, args: string[]): Promise<void> {
return new Promise((resolve, reject) => {
const child = spawn(command, args, { stdio: "inherit" });

child.on("close", (code) => {
if (code === 0) {
resolve();
} else {
reject(new Error(`Command failed with exit code ${code}`));
}
});
});
}

async function createPackage() {
// 1. Ask for a new package name
const packageName = await prompt("Enter a new package name (lowercase letters, digits, and dashes only): ");

// Validate the package name
if (!/^[a-z0-9-]+$/.test(packageName)) {
throw new Error("Package name can only contain lowercase letters, digits, and dashes.");
}

// 2. Copy the template folder
const srcDir = path.resolve(__dirname, "../packages/template");
const destDir = path.resolve(__dirname, `../packages/${packageName}`);
await copyFolder(srcDir, destDir);

// 3. Update the package.json
const packageJsonPath = path.join(destDir, "package.json");
const packageJson = await readJSONFile<Record<string, unknown>>(packageJsonPath);
packageJson.name = `@sigma/${packageName}`;
packageJson.description = "TODO";
delete packageJson.private;

await writeJSONFile(packageJsonPath, packageJson);

// 4. Update the references in tsconfig.json
const tsconfigPath = path.resolve(__dirname, "../tsconfig.json");
const tsconfig = await readJSONFile<Record<string, unknown>>(tsconfigPath);
(tsconfig.references as { path: string }[]).push({ path: `./packages/${packageName}` });
await writeJSONFile(tsconfigPath, tsconfig);

// 5. Add the package to the Preconstruct packages array in package.json
const rootPackageJsonPath = path.resolve(__dirname, "../package.json");
const rootPackageJson = await readJSONFile<Record<string, unknown> & { preconstruct: { packages: string[] } }>(
rootPackageJsonPath,
);
rootPackageJson.preconstruct = {
...(rootPackageJson.preconstruct || {}),
packages: (rootPackageJson.preconstruct.packages || []).concat(`packages/${packageName}`),
};
await writeJSONFile(rootPackageJsonPath, rootPackageJson);

// 7. Run npm run prettify and npm install using spawn with stdio inheritance
await runCommand("npm", ["run", "prettify"]);
await runCommand("npx", ["preconstruct", "fix"]);
await runCommand("npm", ["install"]);

// eslint-disable-next-line no-console
console.log(`Package ${packageName} has been successfully created.`);
}

// Run the script
createPackage().catch((e: Error) => {
// eslint-disable-next-line no-console
console.error(e);
process.exit(1);
});
141 changes: 141 additions & 0 deletions package-lock.json

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

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"lint": "eslint .",
"test": "npm run test --workspace=@sigma/test",
"start": "preconstruct dev && npm run start --workspace=@sigma/storybook",
"createPackage": "ts-node bin/create-package.ts",
"postinstall": "preconstruct dev",
"postpublish": "preconstruct dev",
"website:build": "npm run build --workspace=@sigma/website && npm run build --workspace=@sigma/demo && cp -R packages/demo/build packages/website/build/demo && npm run build --workspace=@sigma/storybook && cp -R packages/storybook/storybook-static packages/website/build/storybook",
Expand All @@ -31,6 +32,7 @@
"lerna": "^8.1.2",
"prettier": "^3.2.5",
"rimraf": "^5.0.5",
"ts-node": "^10.9.2",
"typescript": "^5.4.2",
"typescript-eslint": "^7.2.0"
},
Expand Down
2 changes: 2 additions & 0 deletions packages/template/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules
dist
4 changes: 4 additions & 0 deletions packages/template/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.gitignore
node_modules
src
tsconfig.json
3 changes: 3 additions & 0 deletions packages/template/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Sigma.js - Template package

This package contains a template to start a new package in the sigma.js monorepo.
Loading

0 comments on commit 9ee789b

Please sign in to comment.