Skip to content

Commit

Permalink
feat: setup build and hooks packages
Browse files Browse the repository at this point in the history
  • Loading branch information
joelcox22 committed May 21, 2024
1 parent df7c1a1 commit 974901a
Show file tree
Hide file tree
Showing 15 changed files with 992 additions and 34 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
node_modules
*.tgz
tsconfig.tsbuildinfo
.parcel-cache
dist
9 changes: 7 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,26 @@
"license": "UNLICENSED",
"type": "module",
"scripts": {
"build": "yarn workspaces run build",
"build": "tsx packages/build/lib/build.ts",
"lint": "node packages/lint/index.js",
"test": "vitest"
},
"repository": {
"type": "git",
"url": "https://github.com/joelcox22/lint.git"
"url": "https://github.com/joelcox22/public-libs.git"
},
"devDependencies": {
"@joelbot/eslint-config": "^1.0.0",
"@joelbot/lint": "^1.0.0",
"@parcel/packager-ts": "2.12.0",
"@parcel/transformer-typescript-types": "2.12.0",
"@types/debug": "^4.1.12",
"eslint": "^8.0.0",
"react": "^18.3.1",
"semantic-release": "^23.0.2",
"semantic-release-monorepo": "^8.0.0",
"ts-node": "^10.9.2",
"tsx": "^4.10.5",
"typescript": "^5.0.0",
"vitest": "^1.6.0"
},
Expand Down
130 changes: 130 additions & 0 deletions packages/build/lib/build.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import spawn from '@expo/spawn-async';
import esbuild from 'esbuild';
import Debug from 'debug';
import * as util from './util.js';

async function buildLibrary(dir: string) {
const debug = Debug('@joelbot/build:library');

const packageJson = JSON.parse(fs.readFileSync(path.join(dir, 'package.json'), 'utf-8'));
if (packageJson.devDependencies?.typescript) {
debug('typescript is a devDependency, compiling to ESM and CommonJS');

fs.rmSync(path.join(dir, 'dist'), { force: true, recursive: true });

const options: Partial<Parameters<typeof esbuild.build>[0]> = {
entryPoints: [path.join(dir, 'lib/**/*.ts')],
outdir: path.join(dir, 'dist'),
};

const tsconfigPath = path.join(os.tmpdir(), 'tsconfig.json');
debug('temporary tsconfigPath', tsconfigPath);

const rootDir = path.join(dir, 'lib');
const outDir = path.join(dir, 'dist');
debug('rootDir', rootDir);
debug('outDir', outDir);

fs.writeFileSync(
tsconfigPath,
JSON.stringify(
{
compilerOptions: {
incremental: false,
target: 'ESNext',
jsx: 'react',
moduleDetection: 'auto',
moduleResolution: 'NodeNext',
module: 'NodeNext',
allowArbitraryExtensions: true,
declaration: true,
declarationMap: true,
sourceMap: false,
rootDir,
outDir,
emitDeclarationOnly: true,
removeComments: false,
importsNotUsedAsValues: 'remove',
downlevelIteration: true,
emitBOM: true,
newLine: 'lf',
stripInternal: true,
noEmitOnError: true,
isolatedModules: true,
allowSyntheticDefaultImports: true,
allowImportingTsExtensions: false,
esModuleInterop: true,
forceConsistentCasingInFileNames: true,
strict: true,
skipLibCheck: true,
},
include: [path.join(rootDir, '**/*')],
},
null,
2,
),
);

try {
await Promise.all([
// declaration only typescript build
spawn('yarn', ['tsc', '-p', tsconfigPath], {
stdio: 'inherit',
}),
// ESM
esbuild.build({
...options,
format: 'esm',
}),
// CJS
esbuild.build({
...options,
format: 'cjs',
outExtension: {
'.js': '.cjs',
},
}),
]);
} catch (err) {
process.exit(1);
}
} else {
debug('typescript is not a devDependency, copying lib to dist');
debug('todo: esbuild js so we get esm+cjs output in dist dir, similar to ts lib output');
fs.cpSync(path.join(dir, 'lib'), path.join(dir, 'dist'), { recursive: true });
}
}

async function main() {
const debug = Debug('@joelbot/build');
for await (const dir of util.packageDirs()) {
debug('building in directory', dir);
const packageJson = JSON.parse(fs.readFileSync(path.join(dir, 'package.json'), 'utf-8'));
if (packageJson.private) {
debug('package is private, skipping');
continue;
}
if (packageJson.publishConfig?.access !== 'public') {
debug('package publishConfig.access is not "public", skipping');
continue;
}
if (packageJson.scripts?.build) {
debug('custom build script defined in package.json, running that');
await spawn('yarn', ['build'], {
stdio: 'inherit',
cwd: dir,
});
continue;
}
if (!fs.existsSync(path.join(dir, 'lib'))) {
debug('lib directory does not exist - nothing to build, skipping');
continue;
}
await buildLibrary(dir);
}
}

main();
35 changes: 35 additions & 0 deletions packages/build/lib/util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import Debug from 'debug';
import * as fs from 'fs';
import * as path from 'path';
import * as cp from 'child_process';

export async function* packageDirs() {
const debug = Debug('@joelbot/build:util:packageDirs');
debug('finding workspaces from', process.cwd());
if (!fs.existsSync('package.json')) {
debug('no package.json found, doing nothing');
return;
}
const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf-8'));
if ('workspaces' in packageJson) {
debug('workspaces are enabled in package.json');
if (!fs.existsSync('yarn.lock')) {
console.log('package.json has workspaces enabled, but no yarn.lock file in current directory, doing nothing. try running build from workspace root.');
// todo: allow running build from non-workspace root directory at some point
return;
}
const yarn = cp.spawnSync('yarn', ['workspaces', 'info']);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const workspaces = Object.values(JSON.parse(yarn.stdout.toString())) as any;
debug(
'workspaces', // eslint-disable-next-line @typescript-eslint/no-explicit-any
workspaces.map((ws: any) => ws.location),
);
for (const { location } of workspaces) {
yield path.join(process.cwd(), location);
}
} else {
debug('workspaces are not enabled in package.json, yielding current directory');
yield process.cwd();
}
}
31 changes: 31 additions & 0 deletions packages/build/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"name": "@joelbot/build",
"version": "0.0.0",
"private": false,
"description": "Build tools for JoelBot",
"author": "Joel Cox <[email protected]>",
"license": "0BSD",
"type": "module",
"repository": {
"type": "git",
"url": "https://github.com/joelcox22/public-libs"
},
"dependencies": {
"@expo/spawn-async": "^1.7.2",
"@joelbot/tsconfig": "^1.0.4",
"debug": "^4.3.4",
"esbuild": "^0.21.3",
"tsc": "^2.0.4"
},
"bin": {
"build": "dist/build.js"
},
"publishConfig": {
"access": "public"
},
"release": {
"branches": [
"main"
]
}
}
14 changes: 8 additions & 6 deletions packages/eslint-config/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,19 @@ import Debug from 'debug';

const debug = Debug('@joelbot/eslint-config');

const ignores = fs
.readFileSync('.gitignore', 'utf-8')
.split('\n')
.map((line) => line.trim())
.filter((line) => !line.startsWith('#') && line.trim() !== '');
const ignores = fs.existsSync('.gitignore')
? fs
.readFileSync('.gitignore', 'utf-8')
.split('\n')
.map((line) => line.trim())
.filter((line) => !line.startsWith('#') && line.trim() !== '')
: [];

debug('calculated eslint ignore list from .gitignore:', ignores);

const config = [
{
ignores,
ignores: [...ignores, '**/dist/**'],
},
js.configs.recommended,
...ts.configs.recommended,
Expand Down
3 changes: 0 additions & 3 deletions packages/eslint-config/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,6 @@
"author": "Joel Cox <[email protected]>",
"license": "0BSD",
"type": "module",
"scripts": {
"build": "exit 0"
},
"repository": {
"type": "git",
"url": "https://github.com/joelcox22/lint.git"
Expand Down
33 changes: 33 additions & 0 deletions packages/hooks/lib/use-async-effect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import * as React from 'react';

type AsyncCallback<T> = (abortSignal: AbortSignal) => Promise<T>;
type AsyncResult<T> = [loading: true, error: undefined, result: undefined] | [loading: false, error: Error, result: undefined] | [loading: false, error: undefined, result: T];
type RiskyAsyncResult<T> = [loading: true, result: undefined] | [loading: false, result: T];

export function useAsync<T>(effect: AsyncCallback<T>, deps: React.DependencyList = []): AsyncResult<T> {
const [state, setState] = React.useState<AsyncResult<T>>([true, undefined, undefined]);
React.useEffect(() => {
setState([true, undefined, undefined]);
const abortController = new AbortController();
let aborted = false;
effect(abortController.signal)
.then((result) => {
setState([false, undefined, result]);
})
.catch((err) => {
if (aborted) return;
setState([false, err, undefined]);
});
return () => {
aborted = true;
abortController.abort();
};
}, deps);
return state;
}

export function useRiskyAsync<T>(effect: AsyncCallback<T>, deps: React.DependencyList = []): RiskyAsyncResult<T> {
const [loading, error, result] = useAsync(effect, deps);
if (error) throw error;
return [loading, result] as RiskyAsyncResult<T>;
}
30 changes: 30 additions & 0 deletions packages/hooks/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"name": "@joelbot/hooks",
"version": "0.0.0",
"private": false,
"description": "A collection of useful react hooks",
"author": "Joel Cox <[email protected]>",
"license": "0BSD",
"type": "module",
"repository": {
"type": "git",
"url": "https://github.com/joelcox22/public-libs"
},
"devDependencies": {
"typescript": "^5.4.5"
},
"peerDependencies": {
"react": "^18.0.0"
},
"exports": {
"use-async-effect": "./dist/use-async-effect.js"
},
"publishConfig": {
"access": "public"
},
"release": {
"branches": [
"main"
]
}
}
5 changes: 1 addition & 4 deletions packages/lint/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,9 @@
"author": "Joel Cox <[email protected]>",
"license": "0BSD",
"type": "module",
"scripts": {
"build": "exit 0"
},
"repository": {
"type": "git",
"url": "https://github.com/joelcox22/lint.git"
"url": "https://github.com/joelcox22/public-libs.git"
},
"dependencies": {
"app-root-path": "^3.1.0",
Expand Down
3 changes: 0 additions & 3 deletions packages/release/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,6 @@
"author": "Joel Cox <[email protected]>",
"license": "0BSD",
"type": "module",
"scripts": {
"build": "exit 0"
},
"dependencies": {
"app-root-path": "^3.1.0",
"debug": "^4.3.4"
Expand Down
3 changes: 0 additions & 3 deletions packages/test-react-javascript/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@
"description": "a simple package with react to test the lint config with",
"license": "0BSD",
"type": "module",
"scripts": {
"build": "exit 0"
},
"dependencies": {
"react": "^18.2.0"
}
Expand Down
3 changes: 0 additions & 3 deletions packages/test-react-typescript/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@
"license": "0BSD",
"description": "a simple package with react to test the lint config with",
"type": "module",
"scripts": {
"build": "exit 0"
},
"dependencies": {
"immer": "^10.0.4",
"react": "^18.2.0",
Expand Down
5 changes: 1 addition & 4 deletions packages/tsconfig/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,9 @@
"author": "Joel Cox <[email protected]>",
"license": "0BSD",
"type": "module",
"scripts": {
"build": "exit 0"
},
"repository": {
"type": "git",
"url": "https://github.com/joelcox22/lint.git"
"url": "https://github.com/joelcox22/public-libs.git"
},
"publishConfig": {
"access": "public"
Expand Down
Loading

0 comments on commit 974901a

Please sign in to comment.