Skip to content

Commit

Permalink
chore: add foundation-scripts package
Browse files Browse the repository at this point in the history
  • Loading branch information
notaphplover committed Oct 14, 2024
1 parent e4c5aaf commit 4fda05d
Show file tree
Hide file tree
Showing 9 changed files with 220 additions and 0 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"@inversifyjs/foundation-prettier-config": "workspace:*",
"@inversifyjs/foundation-eslint-config": "workspace:*",
"@inversifyjs/foundation-jest-config": "workspace:*",
"@inversifyjs/foundation-scripts": "workspace:*",
"@inversifyjs/foundation-typescript-config": "workspace:*",
"husky": "9.1.6",
"lint-staged": "15.2.10",
Expand Down
2 changes: 2 additions & 0 deletions packages/foundation/tools/scripts/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# npm installed packages
/node_modules/
5 changes: 5 additions & 0 deletions packages/foundation/tools/scripts/.lintstagedrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"*.js": [
"prettier --write"
]
}
60 changes: 60 additions & 0 deletions packages/foundation/tools/scripts/bin/getAffectedProjectsChunks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#!/usr/bin/env node

import process from 'node:process';
import { promisifiedExec } from '../src/promisifiedExec.js';

/**
* @param {Array} elements Elements
* @param {number} chunks chunks amount
* @returns {Array.<Array>}
*/
function buildChunks(elements, chunks) {
const chunkSize = Math.ceil(elements.length / chunks);

const chunksArray = [];

for (let i = 0; i < elements.length; i += chunkSize) {
chunksArray.push(elements.slice(i, i + chunkSize));
}

return chunksArray;
}

const TURBOREPO_ROOT_PACKAGE_NAME = '//';
const TURBOREPO_TASK_NOT_FOUND_MAGIC_STRING = '\u003cNONEXISTENT\u003e';

const taskToDryRun = process.argv[2];
const baseRef = process.argv[3];
const chunks = parseInt(process.argv[4]);

let execCommand = `pnpm exec turbo run ${taskToDryRun} --dry=json`;

if (baseRef !== undefined) {
execCommand += ` --filter ...[${baseRef}]`;
}

const stringifiedDryRun = (await promisifiedExec(execCommand)).trim();

const dryRunResult = JSON.parse(stringifiedDryRun);

/** @type {Array.<string>} */
const dryResultPackageNames = dryRunResult.packages.filter(
(packageName) => packageName !== TURBOREPO_ROOT_PACKAGE_NAME,
);

const tasks = dryRunResult.tasks.filter((task) =>
dryResultPackageNames.some(
(packageName) =>
task.taskId === `${packageName}#${taskToDryRun}` &&
!task.command.includes(TURBOREPO_TASK_NOT_FOUND_MAGIC_STRING),
),
);

/** @type {Array.<string>} */
const packageNames = tasks.map((task) => task.package);

const packageNameChunks = buildChunks(packageNames, chunks).filter(
(chunk) => chunk.length > 0,
);

process.stdout.write(JSON.stringify(packageNameChunks));
43 changes: 43 additions & 0 deletions packages/foundation/tools/scripts/bin/writeCommonJsPackageJson.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#!/usr/bin/env node

import fs from 'node:fs/promises';
import { argv } from 'node:process';
import path from 'node:path';
import { writeFile } from 'node:fs/promises';

/**
* @param {string} path
* @returns {Promise<boolean>}
*/
async function pathExists(path) {
try {
await fs.access(path);
return true;
} catch (_err) {
return false;
}
}

const directory = argv[2];

if (directory === undefined) {
throw new Error('Expected a path');
}

const directoryExists = await pathExists(directory);

if (!directoryExists) {
throw new Error(`Path ${directory} not found`);
}

const filePath = path.join(directory, 'package.json');

const packageJsonFileContent = JSON.stringify(
{
type: 'commonjs',
},
undefined,
2,
);

await writeFile(filePath, packageJsonFileContent);
43 changes: 43 additions & 0 deletions packages/foundation/tools/scripts/bin/writeEsmPackageJson.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#!/usr/bin/env node

import fs from 'node:fs/promises';
import { argv } from 'node:process';
import path from 'node:path';
import { writeFile } from 'node:fs/promises';

/**
* @param {string} path
* @returns {Promise<boolean>}
*/
async function pathExists(path) {
try {
await fs.access(path);
return true;
} catch (_err) {
return false;
}
}

const directory = argv[2];

if (directory === undefined) {
throw new Error('Expected a path');
}

const directoryExists = await pathExists(directory);

if (!directoryExists) {
throw new Error(`Path ${directory} not found`);
}

const filePath = path.join(directory, 'package.json');

const packageJsonFileContent = JSON.stringify(
{
type: 'module',
},
undefined,
2,
);

await writeFile(filePath, packageJsonFileContent);
21 changes: 21 additions & 0 deletions packages/foundation/tools/scripts/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"name": "@inversifyjs/foundation-scripts",
"private": true,
"description": "Common scripts for monorepo packages",
"repository": {
"type": "git",
"url": "git+https://github.com/notaphplover/one-game.git"
},
"author": "Multiple authors",
"license": "MIT",
"bugs": {
"url": "https://github.com/notaphplover/one-game/issues"
},
"type": "module",
"homepage": "https://github.com/notaphplover/one-game#readme",
"bin": {
"foundation-get-affected-project-chunks": "./bin/getAffectedProjectsChunks.js",
"foundation-ts-package-cjs": "./bin/writeCommonJsPackageJson.js",
"foundation-ts-package-esm": "./bin/writeEsmPackageJson.js"
}
}
3 changes: 3 additions & 0 deletions packages/foundation/tools/scripts/prettier.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import config from '@inversifyjs/foundation-prettier-config';

export default config;
42 changes: 42 additions & 0 deletions packages/foundation/tools/scripts/src/promisifiedExec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { exec } from 'node:child_process';

/**
* @param {string} command command
* @param {PromisifiedExecOptions} options true to pipe standard input output and error streams
* @returns {Promise<string>}
*/
export async function promisifiedExec(command, options) {
options = {
cwd: options?.cwd,
interactive: options?.interactive ?? false,
};

return new Promise((resolve, reject) => {
const childProcess = exec(
command,
{
cwd: options.cwd,
},
(error, stdout) => {
if (error === null) {
resolve(stdout);
} else {
reject(error);
}
},
);

if (options.interactive) {
childProcess.stdout.pipe(process.stdout);
childProcess.stderr.pipe(process.stderr);

process.stdin.pipe(childProcess.stdin);
}
});
}

/**
* @typedef {Object} PromisifiedExecOptions
* @property {boolean} interactive
* @property {string} cwd
*/

0 comments on commit 4fda05d

Please sign in to comment.