Skip to content

Commit

Permalink
feat: more errors
Browse files Browse the repository at this point in the history
  • Loading branch information
nyarthan committed Mar 4, 2024
1 parent 1d46fd1 commit 371e925
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 32 deletions.
5 changes: 5 additions & 0 deletions .changeset/angry-kangaroos-relate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@titanom/css2tailwind": patch
---

add error handling
75 changes: 43 additions & 32 deletions packages/css2tailwind/src/cli.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import * as fsp from 'node:fs/promises';
import * as path from 'node:path';

import { watch } from 'chokidar';
import { FSWatcher, watch } from 'chokidar';

Check failure on line 4 in packages/css2tailwind/src/cli.ts

View workflow job for this annotation

GitHub Actions / Lint, Format, Types

Import "FSWatcher" is only used as types
import yargs from 'yargs';
import { hideBin } from 'yargs/helpers';
import { z } from 'zod';

import { bootstrapStyles, parseStyles, writeStyles } from './build';

Check failure on line 9 in packages/css2tailwind/src/cli.ts

View workflow job for this annotation

GitHub Actions / Lint, Format, Types

'bootstrapStyles' is defined but never used
import {
CloseWatcherError,
dedupeSyntaxErrors,
isSyntaxError,
NoStylesDirectoryError,
Expand Down Expand Up @@ -69,60 +70,70 @@ function exitIf(exit: boolean, code: number) {
if (exit) process.exit(code);
}

async function buildAndHandleErrors(entry: string, tailwindConfig: Config) {
const buildResult = await compileAndWriteStyles(entry, tailwindConfig);
if (!buildResult.ok) handleBuildErrors(buildResult.error);
return buildResult;
}

function handleBuildErrors(error: unknown) {
if (Array.isArray(error)) {
const syntaxErrors = dedupeSyntaxErrors(error.filter(isSyntaxError));
syntaxErrors.forEach((error) => console.log(error.toString()));
}
}

function setupWatcher(entryPath: string, entry: string, tailwindConfig: Config): FSWatcher {
const watcher = watch(`${entryPath}/*/*.css`, {
awaitWriteFinish: { stabilityThreshold: 10, pollInterval: 10 },
});
watcher.on('change', () => void buildAndHandleErrors(entry, tailwindConfig));
return watcher;
}

let watchers: FSWatcher[] = [];

function gracefullyShutdown() {
Promise.all(watchers.map(async (watcher) => await watcher.close()))
.then(() => process.exit(0))
.catch(() => {
console.error(new CloseWatcherError('').toString());
process.exit(1);
});
}

async function main() {
await assertDirExists(stylesDirectory);

const tailwindConfig = await readTailwindConfig(args.config && path.join(cwd, args.config));

const dirents = await fsp.readdir(stylesDirectory, { withFileTypes: true });
const entries = dirents.filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);

const buildResults = await Promise.all(
entries.map(async (entry) => doTheThing(entry, tailwindConfig)),
entries.map(async (entry) => buildAndHandleErrors(entry, tailwindConfig)),
);

const buildErrors = buildResults.filter(isErr).flatMap(mapErrResultToError);

const syntaxErrors = dedupeSyntaxErrors(buildErrors.filter(isSyntaxError));

for (const syntaxError of syntaxErrors) {
console.log(syntaxError.toString());
}

exitIf(!args.watch && !!buildErrors.length, 1);
exitIf(!args.watch && buildErrors.length > 0, 1);
exitIf(!args.watch, 0);

for (const entry of entries) {
entries.forEach((entry) => {
const entryPath = path.join(stylesDirectory, entry);
const watcher = watch(`${entryPath}/*/*.css`, {
awaitWriteFinish: { stabilityThreshold: 10, pollInterval: 10 },
});
watcher.on('change', () => {
void (async () => {
const buildResult = await doTheThing(entry, tailwindConfig);

if (!buildResult.ok) {
const error = buildResult.error;
if (Array.isArray(error)) {
const syntaxErrors = dedupeSyntaxErrors(error.filter(isSyntaxError));
for (const syntaxError of syntaxErrors) {
console.log(syntaxError.toString());
}
}
}
})();
});
}
setupWatcher(entryPath, entry, tailwindConfig);
});

process.on('SIGINT', gracefullyShutdown);
process.on('SIGTERM', gracefullyShutdown);
}

void main();

async function doTheThing(
async function compileAndWriteStyles(
entry: string,
tailwindConfig: Config,
): Promise<Result<void, Error | SyntaxError[]>> {
try {
await bootstrapStyles(outputDirectory, entry);
const stylesResult = await parseStyles(
path.join(stylesDirectory, entry),
stylesDirectory,
Expand Down
12 changes: 12 additions & 0 deletions packages/css2tailwind/src/error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,18 @@ export class NoStylesDirectoryError extends Error {
}
}

export class CloseWatcherError extends Error {
private readonly errorName = 'ERR_CLOSE_WATCHER';

public constructor(message: string) {
super(message);
}

public override toString() {
return `${bgRed(` ${black(this.errorName)} `)} Failed to close watcher`;
}
}

export class CompilationError extends Error {
public constructor(message: string) {
super(message);
Expand Down

0 comments on commit 371e925

Please sign in to comment.