Skip to content

Commit

Permalink
chore: copy over script and get it building
Browse files Browse the repository at this point in the history
  • Loading branch information
dotmh committed Jul 12, 2024
1 parent 7b49996 commit 9aaa0e7
Show file tree
Hide file tree
Showing 14 changed files with 610 additions and 146 deletions.
2 changes: 2 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pnpm-lock.yaml
bin
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
18.15.0
20.14.0
3 changes: 2 additions & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pnpm-lock.yaml
pnpm-lock.yaml
bin
18 changes: 18 additions & 0 deletions bin/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* Smart Builder
* =============
*
* This file is used to build the packages in the correct order
* It will read the pnpm-workspace.yaml file to get the list of packages
* and determine the order to build them in
* It will then run the build command set up in the BUILD_SCRIPT variable
* It is designed to __ONLY__ Work with pnpm workspaces not npm or yarn at the moment
*
* To run it use [TSX](https://www.npmjs.com/package/tsx)
* IT needs to run from the root project folder
*
* `tsx scripts/smart-builder.ts`
*
* To ignore a package from the build list add it to the .sbignore file at the root of the project
*/
export {};
188 changes: 188 additions & 0 deletions bin/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
/**
* Smart Builder
* =============
*
* This file is used to build the packages in the correct order
* It will read the pnpm-workspace.yaml file to get the list of packages
* and determine the order to build them in
* It will then run the build command set up in the BUILD_SCRIPT variable
* It is designed to __ONLY__ Work with pnpm workspaces not npm or yarn at the moment
*
* To run it use [TSX](https://www.npmjs.com/package/tsx)
* IT needs to run from the root project folder
*
* `tsx scripts/smart-builder.ts`
*
* To ignore a package from the build list add it to the .sbignore file at the root of the project
*/
/* eslint-disable promise/always-return */
/* eslint-disable promise/prefer-await-to-callbacks */
/* eslint-disable no-console */
import { readFile, stat } from 'node:fs/promises';
import { join } from 'node:path';
import yaml from 'yaml';
import { glob } from 'glob';
import { exec } from 'node:child_process';
import { EOL } from 'node:os';
import { cwd } from 'node:process';
const WORKSPACE = 'pnpm-workspace.yaml';
const PACKAGE_MANIFEST = 'package.json';
const BUILD_SCRIPT = 'pnpm --filter PACKAGE run build';
const SKIP_BUILD = process.env['SKIP_BUILD'] === 'yes';
const DEBUG = process.env['DEBUG'] === 'yes';
const SMART_BUKLDER_IGNORE = '.sbignore';
const lockFiles = {
pnpm: 'pnpm-lock.yaml',
yarn: 'yarn.lock',
npm: 'package-lock.json',
unknown: '',
};
const exists = async (file) => {
try {
const stats = await stat(file);
return stats.isFile();
}
catch {
return false;
}
};
const whichPackageManager = async () => {
const check = Object.entries(lockFiles);
const found = [];
for (const checking of check) {
const [manager, file] = checking;
const lockFileExists = await exists(join(cwd(), file));
if (lockFileExists) {
found.push(manager);
}
}
if (found.length === 0) {
throw new Error('No package manager found');
}
if (found.length > 1) {
throw new Error(`Multiple package managers found ${found.join(',')}`);
}
return found[0] ?? 'unknown';
};
const getSmartBuilderIgnoreFile = async () => {
try {
const raw = await readFile(join(cwd(), SMART_BUKLDER_IGNORE), 'utf-8');
return raw.split(EOL).map((str) => str.trim());
}
catch {
return [];
}
};
const loadWorkspace = async () => {
try {
console.log(process.cwd());
const raw = await readFile(join(cwd(), WORKSPACE), 'utf-8');
return yaml.parse(raw);
}
catch {
throw new Error('No workspace file found');
}
};
const getPackages = async () => {
const workspace = await loadWorkspace();
return workspace.packages;
};
const getPackageDeps = async (path) => {
const raw = await readFile(path, 'utf-8');
const pkg = JSON.parse(raw);
return { [pkg.name]: pkg.dependencies };
};
const convetToLocalOnly = (dependencies) => {
const dependenciesEntries = Object.entries(dependencies);
const localOnly = dependenciesEntries.map(([name, deps]) => {
if (!deps) {
return [name, {}];
}
const localDeps = Object.entries(deps).filter(([, deps]) => deps.startsWith('workspace:'));
return [name, Object.fromEntries(localDeps)];
});
return Object.fromEntries(localOnly);
};
const getBuildOrder = (allPackages) => {
const orderToBuild = [];
allPackages.forEach((huh) => {
const [data] = Object.entries(huh);
if (!data) {
return;
}
const [name, dependencies] = data;
const packageDependencies = Object.keys(dependencies);
//if we don't have any dependencies, add to the build order first
if (packageDependencies.length === 0) {
console.log(`Skipping ${name} as it has no dependencies`);
orderToBuild.unshift(name);
}
else {
packageDependencies.forEach((dep) => {
if (!orderToBuild.includes(dep)) {
orderToBuild.unshift(dep);
}
else {
const index = orderToBuild.indexOf(dep);
orderToBuild.splice(index, 0, dep);
}
});
orderToBuild.push(name);
}
});
return [...new Set(orderToBuild)];
};
const filterDependancies = (buildOrder, ignoring = []) => buildOrder.filter((name) => !ignoring.includes(name));
const getAllPackagesUnderAPackage = async (path) => {
const pathMinusGlob = path.split('/').reverse().slice(1).reverse().join('/');
const pattern = join(process.cwd(), pathMinusGlob, '**', PACKAGE_MANIFEST);
const list = await glob(pattern);
return list.filter((packagePath) => !packagePath.includes('node_modules'));
};
const build = (usePackage) => {
return new Promise((resolve, reject) => {
const command = BUILD_SCRIPT.replace('PACKAGE', usePackage);
exec(command, (error, stdout) => {
if (error) {
reject(error);
return;
}
resolve(stdout);
});
});
};
const buildBuildList = async (buildList) => {
for (const buildItem of buildList) {
console.log(`Building ${buildItem}`);
await build(buildItem);
}
};
const main = async () => {
const packageManager = await whichPackageManager();
if (packageManager !== 'pnpm') {
throw new Error(`ONLY PNPM is support ${packageManager} found`);
}
console.log('SEARCHING for packages using', packageManager);
const packages = await getPackages();
const ignore = await getSmartBuilderIgnoreFile();
const list = await Promise.all(packages.map(getAllPackagesUnderAPackage));
const allPackages = await Promise.all(list.flat().map(getPackageDeps));
const localOnly = allPackages.map((dependencies) => convetToLocalOnly(dependencies));
const buildOrder = filterDependancies(getBuildOrder(localOnly), ignore);
console.log('About to build the following package');
console.log(buildOrder.join(EOL));
if (!SKIP_BUILD) {
await buildBuildList(buildOrder);
}
else {
console.log('Skip build is set! Skipping');
}
console.log('DONE!');
};
main().catch((error) => {
console.log('Build failed:', error.message ?? 'Unknown error');
if (DEBUG) {
console.error(error);
}
process.exit(1);
});
15 changes: 0 additions & 15 deletions libs/hello/package.json

This file was deleted.

4 changes: 0 additions & 4 deletions libs/hello/src/index.ts

This file was deleted.

8 changes: 0 additions & 8 deletions libs/hello/tests/index.test.ts

This file was deleted.

7 changes: 0 additions & 7 deletions libs/hello/tsconfig.json

This file was deleted.

8 changes: 7 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
{
"name": "@dotmh/ts-base",
"name": "@dotmh/smart-builder",
"version": "1.0.0",
"description": "The basic typescript project setup for DotMH",
"main": "index.js",
"type": "module",
"prettier": "@dotmh/prettier-config",
"scripts": {
"build": "tsc",
"test": "pnpm run -r test",
"prepare": "husky",
"lint": "eslint . --fix --ext .ts",
Expand All @@ -24,6 +25,7 @@
"@commitlint/config-conventional": "^19.2.2",
"@dotmh/eslint-config-ts": "^1.0.0",
"@dotmh/prettier-config": "^1.0.0",
"@dotmh/tsconfig": "^2.1.0",
"@secretlint/secretlint-rule-preset-recommend": "^8.2.4",
"@types/node": "20.14.9",
"eslint": "^8.57.0",
Expand All @@ -36,5 +38,9 @@
"engines": {
"node": "^20.14.0",
"pnpm": "^9.4.0"
},
"dependencies": {
"glob": "^11.0.0",
"yaml": "^2.4.5"
}
}
Loading

0 comments on commit 9aaa0e7

Please sign in to comment.