From 9ee789b87e1293e6f240fc941cf44293c92589a1 Mon Sep 17 00:00:00 2001 From: Alexis Jacomy Date: Thu, 19 Sep 2024 16:41:59 +0200 Subject: [PATCH] [tooltip] Adds new `npm run createPackage` script 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 --- README.md | 9 ++ bin/create-package.ts | 117 ++++++++++++++++++++++++++ package-lock.json | 141 ++++++++++++++++++++++++++++++++ package.json | 2 + packages/template/.gitignore | 2 + packages/template/.npmignore | 4 + packages/template/README.md | 3 + packages/template/package.json | 32 ++++++++ packages/template/src/index.ts | 4 + packages/template/tsconfig.json | 23 ++++++ 10 files changed, 337 insertions(+) create mode 100644 bin/create-package.ts create mode 100644 packages/template/.gitignore create mode 100644 packages/template/.npmignore create mode 100644 packages/template/README.md create mode 100644 packages/template/package.json create mode 100644 packages/template/src/index.ts create mode 100644 packages/template/tsconfig.json diff --git a/README.md b/README.md index 560172ed4..ae89e8350 100644 --- a/README.md +++ b/README.md @@ -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`...) diff --git a/bin/create-package.ts b/bin/create-package.ts new file mode 100644 index 000000000..e8b89a008 --- /dev/null +++ b/bin/create-package.ts @@ -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(filePath: string): Promise { + 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 { + 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 { + 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>(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>(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 & { 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); +}); diff --git a/package-lock.json b/package-lock.json index 1d7163f9d..85b164054 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,6 +22,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" } @@ -2196,6 +2197,28 @@ "node": ">=0.1.90" } }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "node_modules/@discoveryjs/json-ext": { "version": "0.5.7", "license": "MIT", @@ -5043,6 +5066,10 @@ "resolved": "packages/storybook", "link": true }, + "node_modules/@sigma/template": { + "resolved": "packages/template", + "link": true + }, "node_modules/@sigma/test": { "resolved": "packages/test", "link": true @@ -7675,6 +7702,30 @@ "integrity": "sha512-3l1L5PzWVa7l0691TjnsZ0yOIEwG9DziSqu5IPZPlI5Dowi7z42cEym8Y35GHbgHvPcBfNxfrbxm7Cncn4nByQ==", "license": "MIT" }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "license": "MIT" + }, "node_modules/@tufjs/canonical-json": { "version": "1.0.0", "license": "MIT", @@ -11969,6 +12020,12 @@ } } }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "license": "MIT" + }, "node_modules/cross-spawn": { "version": "7.0.3", "license": "MIT", @@ -12754,6 +12811,15 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/diff-sequences": { "version": "29.6.3", "license": "MIT", @@ -18656,6 +18722,12 @@ "version": "4.0.0", "license": "ISC" }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "license": "ISC" + }, "node_modules/make-fetch-happen": { "version": "13.0.0", "license": "ISC", @@ -28041,6 +28113,55 @@ "node": ">=6.10" } }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-node/node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "license": "MIT" + }, "node_modules/tsconfck": { "version": "3.0.3", "license": "MIT", @@ -29105,6 +29226,12 @@ "version": "2.4.0", "license": "MIT" }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "license": "MIT" + }, "node_modules/validate-npm-package-license": { "version": "3.0.4", "license": "Apache-2.0", @@ -30189,6 +30316,15 @@ "node": ">=12" } }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "license": "MIT", @@ -30730,6 +30866,11 @@ "storybook": "^8.0.0" } }, + "packages/template": { + "name": "@sigma/template", + "version": "0.0.1-alpha.0", + "license": "MIT" + }, "packages/test": { "name": "@sigma/test", "dependencies": { diff --git a/package.json b/package.json index 67aec7514..0ad1d676c 100644 --- a/package.json +++ b/package.json @@ -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", @@ -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" }, diff --git a/packages/template/.gitignore b/packages/template/.gitignore new file mode 100644 index 000000000..f06235c46 --- /dev/null +++ b/packages/template/.gitignore @@ -0,0 +1,2 @@ +node_modules +dist diff --git a/packages/template/.npmignore b/packages/template/.npmignore new file mode 100644 index 000000000..0e9d6d53f --- /dev/null +++ b/packages/template/.npmignore @@ -0,0 +1,4 @@ +.gitignore +node_modules +src +tsconfig.json diff --git a/packages/template/README.md b/packages/template/README.md new file mode 100644 index 000000000..a2f12c638 --- /dev/null +++ b/packages/template/README.md @@ -0,0 +1,3 @@ +# Sigma.js - Template package + +This package contains a template to start a new package in the sigma.js monorepo. diff --git a/packages/template/package.json b/packages/template/package.json new file mode 100644 index 000000000..3245783d0 --- /dev/null +++ b/packages/template/package.json @@ -0,0 +1,32 @@ +{ + "name": "@sigma/template", + "private": true, + "version": "0.0.1-alpha.0", + "description": "A template to start a new package in the sigma.js monorepo", + "main": "dist/sigma-template.cjs.js", + "module": "dist/sigma-template.esm.js", + "files": [ + "/dist" + ], + "sideEffects": false, + "homepage": "https://www.sigmajs.org", + "bugs": "http://github.com/jacomyal/sigma.js/issues", + "repository": { + "type": "git", + "url": "http://github.com/jacomyal/sigma.js.git" + }, + "preconstruct": { + "entrypoints": [ + "index.ts" + ] + }, + "license": "MIT", + "exports": { + ".": { + "module": "./dist/sigma-template.esm.js", + "import": "./dist/sigma-template.cjs.mjs", + "default": "./dist/sigma-template.cjs.js" + }, + "./package.json": "./package.json" + } +} diff --git a/packages/template/src/index.ts b/packages/template/src/index.ts new file mode 100644 index 000000000..ac3b3cb12 --- /dev/null +++ b/packages/template/src/index.ts @@ -0,0 +1,4 @@ +export default function template() { + // eslint-disable-next-line no-console + console.log("Hello world!"); +} diff --git a/packages/template/tsconfig.json b/packages/template/tsconfig.json new file mode 100644 index 000000000..01de8170f --- /dev/null +++ b/packages/template/tsconfig.json @@ -0,0 +1,23 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "target": "ESNext", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + "moduleResolution": "node", + "allowSyntheticDefaultImports": true, + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "declaration": true + }, + "include": ["src"], + "exclude": ["src/**/__docs__", "src/**/__test__"] +}