Skip to content

Commit

Permalink
Merge pull request #2945 from opral/lorissigrist/parjs-160-gracefully…
Browse files Browse the repository at this point in the history
…-handle-project-errors

Paraglide Gracefully handle project errors
  • Loading branch information
LorisSigrist authored Jun 18, 2024
2 parents 1930355 + e1281d7 commit 9276b3b
Show file tree
Hide file tree
Showing 7 changed files with 84 additions and 27 deletions.
5 changes: 5 additions & 0 deletions .changeset/fresh-parrots-double.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@inlang/paraglide-unplugin": minor
---

Only warn about non-fatal errors insetad of erroring
5 changes: 5 additions & 0 deletions .changeset/soft-jobs-hunt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@inlang/paraglide-js": minor
---

Gracefully handle errors in the Inlang Project. Only crash on errors that are fatal to paraglide specifically
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ beforeEach(() => {
})
})

test("it should exit if the project has errors", async () => {
test("it should exit if the project has a fatal error", async () => {
mockFs({
"/project.inlang/settings.json": JSON.stringify({
// invalid source language tag
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { loadProject, type InlangProject } from "@inlang/sdk"
import { loadProject } from "@inlang/sdk"
import { Command } from "commander"
import nodeFsPromises from "node:fs/promises"
import { resolve } from "node:path"
import { Command } from "commander"
import { Logger } from "~/services/logger/index.js"
import { openRepository, findRepoRoot } from "@lix-js/client"
import { runCompiler } from "~/cli/steps/run-compiler.js"
import { DEFAULT_OUTDIR } from "~/cli/defaults.js"
import { classifyProjectErrors } from "~/services/error-handling.js"

export const compileCommand = new Command()
.name("compile")
Expand Down Expand Up @@ -33,14 +34,29 @@ export const compileCommand = new Command()
logger.warn(`Could not find repository root for path ${path}`)
}

const project = exitIfErrors(
await loadProject({
projectPath: path,
repo,
appId: PARJS_MARKTEPLACE_ID,
}),
logger
)
const project = await loadProject({
projectPath: path,
repo,
appId: PARJS_MARKTEPLACE_ID,
})

if (project.errors().length > 0) {
const { nonFatalErrors, fatalErrors } = classifyProjectErrors(project.errors())
if (fatalErrors.length > 0) {
logger.error(`The project has fatal errors:`)
for (const error of [...fatalErrors, ...nonFatalErrors]) {
logger.error(error)
}
process.exit(1)
}

if (nonFatalErrors.length > 0) {
logger.warn(`The project has warnings:`)
for (const error of nonFatalErrors) {
logger.warn(error)
}
}
}

await runCompiler({
project,
Expand Down Expand Up @@ -77,17 +93,3 @@ export const compileCommand = new Command()

logger.info("Successfully compiled the project.")
})

/**
* Utility function to exit when the project has errors.
*/
const exitIfErrors = (project: InlangProject, logger: Logger) => {
if (project.errors().length > 0) {
logger.warn(`The project has errors:`)
for (const error of project.errors()) {
logger.error(error)
}
process.exit(1)
}
return project
}
1 change: 1 addition & 0 deletions inlang/source-code/paraglide/paraglide-js/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export { compile } from "./compiler/compile.js"
export { writeOutput } from "./services/file-handling/write-output.js"
export { Logger, type LoggerOptions } from "./services/logger/index.js"
export { classifyProjectErrors } from "./services/error-handling.js"

export type MessageIndexFunction<T extends string> = (
params: Record<string, never>,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import type { InlangProject } from "@inlang/sdk"

interface ModuleError extends Error {
name: string
module: string
}

export function classifyProjectErrors(errors: ReturnType<InlangProject["errors"]>) {
const isModuleError = (error: Error): error is ModuleError =>
error instanceof Error && "name" in error && error.name.includes("Module") && "module" in error

const [moduleErrors, otherErrors] = split(errors as Error[], isModuleError)

const isFatalModuleError = (error: ModuleError): error is ModuleError =>
error.module.includes("plugin")
const [fatalModuleErrors, nonFatalModuleErrors] = split(moduleErrors, isFatalModuleError)

const fatalErrors = [...fatalModuleErrors, ...otherErrors]
const nonFatalErrors = [...nonFatalModuleErrors]

return { fatalErrors, nonFatalErrors }
}

/**
* Splits an array into two arrays based on the predicate
*/
function split<T, U extends T>(array: T[], predicate: (value: T) => value is U): [U[], T[]] {
const result: U[] = []
const rest: T[] = []
for (const item of array) {
if (predicate(item)) {
result.push(item)
} else {
rest.push(item)
}
}
return [result, rest]
}
10 changes: 8 additions & 2 deletions inlang/source-code/paraglide/paraglide-unplugin/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
import { openRepository, findRepoRoot } from "@lix-js/client"
import path from "node:path"
import fs from "node:fs/promises"
import { compile, writeOutput, Logger } from "@inlang/paraglide-js/internal"
import { compile, writeOutput, Logger, classifyProjectErrors } from "@inlang/paraglide-js/internal"
import crypto from "node:crypto"

const PLUGIN_NAME = "unplugin-paraglide"
Expand Down Expand Up @@ -113,7 +113,13 @@ export const paraglide = createUnplugin((config: UserConfig) => {

project.errors.subscribe((errors) => {
if (errors.length === 0) return
for (const error of errors) {

const { fatalErrors, nonFatalErrors } = classifyProjectErrors(errors)
for (const error of nonFatalErrors) {
logger.warn(error.message)
}

for (const error of fatalErrors) {
if (error instanceof Error) {
logger.error(error.message) // hide the stack trace
} else {
Expand Down

0 comments on commit 9276b3b

Please sign in to comment.