Skip to content

Commit f532f9f

Browse files
committed
Add other error types
1 parent c0ecd2a commit f532f9f

File tree

8 files changed

+672
-470
lines changed

8 files changed

+672
-470
lines changed

index.d.ts

Lines changed: 569 additions & 420 deletions
Large diffs are not rendered by default.

index.js

Lines changed: 85 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,55 @@ const { createHash } = require("./utilities/hash")
66
class TranslationError extends Error {
77
constructor(message) {
88
super(message)
9-
this.name = 'TranslationError'
9+
this.name = "TranslationError"
10+
Error.captureStackTrace(this, this.constructor)
11+
}
12+
}
13+
14+
class FileError extends Error {
15+
constructor(message) {
16+
super(message)
17+
this.name = "FileError"
18+
Error.captureStackTrace(this, this.constructor)
19+
}
20+
}
21+
22+
class RawError extends Error {
23+
constructor(message) {
24+
super(message)
25+
this.name = "RawError"
26+
Error.captureStackTrace(this, this.constructor)
27+
}
28+
}
29+
30+
class CSSError extends Error {
31+
constructor(message) {
32+
super(message)
33+
this.name = "CSSError"
34+
Error.captureStackTrace(this, this.constructor)
35+
}
36+
}
37+
38+
class ImageError extends Error {
39+
constructor(message) {
40+
super(message)
41+
this.name = "ImageError"
42+
Error.captureStackTrace(this, this.constructor)
43+
}
44+
}
45+
46+
class SVGError extends Error {
47+
constructor(message) {
48+
super(message)
49+
this.name = "SVGError"
50+
Error.captureStackTrace(this, this.constructor)
51+
}
52+
}
53+
54+
class JSONError extends Error {
55+
constructor(message) {
56+
super(message)
57+
this.name = "JSONError"
1058
Error.captureStackTrace(this, this.constructor)
1159
}
1260
}
@@ -45,7 +93,7 @@ function compile(path) {
4593
if (
4694
attributes.src ||
4795
["application/json", "application/ld+json"].includes(
48-
attributes.type
96+
attributes.type,
4997
)
5098
) {
5199
node.ignore = false
@@ -187,7 +235,7 @@ function validateSymlinks(path, base) {
187235
if (!part) continue
188236
current = resolve(current, part)
189237
if (lstatSync(current).isSymbolicLink()) {
190-
throw new Error(`FileError: symlinks are not allowed ("${current}")`)
238+
throw new FileError(`symlinks are not allowed ("${current}")`)
191239
}
192240
}
193241
}
@@ -199,33 +247,31 @@ function validateFile(path, base) {
199247
const type = extension(normalizedPath)
200248

201249
if (!type) {
202-
throw new Error(`FileError: path "${path}" has no extension`)
250+
throw new FileError(`path "${path}" has no extension`)
203251
}
204252

205253
if (!ALLOWED_READ_EXTENSIONS.includes(type)) {
206-
throw new Error(
207-
`FileError: unsupported file type "${type}" for path "${path}"`
208-
)
254+
throw new FileError(`unsupported file type "${type}" for path "${path}"`)
209255
}
210256

211257
const stats = lstatSync(normalizedPath)
212258
if (!stats.isFile()) {
213-
throw new Error(`FileError: path "${path}" is not a file`)
259+
throw new FileError(`path "${path}" is not a file`)
214260
}
215261

216262
if (stats.isSymbolicLink()) {
217-
throw new Error(`FileError: path "${path}" is a symbolic link`)
263+
throw new FileError(`path "${path}" is a symbolic link`)
218264
}
219265

220266
if (normalizedPath === normalizedBase) {
221-
throw new Error(
222-
`FileError: path "${path}" is the same as the current working directory "${base}"`
267+
throw new FileError(
268+
`path "${path}" is the same as the current working directory "${base}"`,
223269
)
224270
}
225271

226272
if (!normalizedPath.startsWith(normalizedBase + "/")) {
227-
throw new Error(
228-
`FileError: real path "${realPath}" is not within the current working directory "${realBase}"`
273+
throw new FileError(
274+
`real path "${realPath}" is not within the current working directory "${realBase}"`,
229275
)
230276
}
231277
}
@@ -243,9 +289,7 @@ function readFile(path, encoding) {
243289

244290
return readFileSync(path, encoding)
245291
} catch (exception) {
246-
throw new Error(
247-
`FileError: cannot read file "${path}": ${exception.message}`
248-
)
292+
throw new FileError(`cannot read file "${path}": ${exception.message}`)
249293
}
250294
}
251295

@@ -338,8 +382,8 @@ const attributes = (options) => {
338382
const left = result.left || "0"
339383
styles.push(
340384
`${decamelize(param)}:${escapeHTML(
341-
`${top} ${right} ${bottom} ${left}`
342-
)}`
385+
`${top} ${right} ${bottom} ${left}`,
386+
)}`,
343387
)
344388
} else if (typeof result === "string" || typeof result === "number") {
345389
styles.push(`${decamelize(param)}:${escapeHTML(result)}`)
@@ -488,9 +532,7 @@ const sanitizeHTML = (content) => {
488532
raw.load = function (path, options = {}) {
489533
const type = extension(path)
490534
if (!ALLOWED_RAW_EXTENSIONS.includes(type)) {
491-
throw new Error(
492-
`RawError: unsupported raw type "${type}" for path "${path}"`
493-
)
535+
throw new RawError(`unsupported raw type "${type}" for path "${path}"`)
494536
}
495537

496538
let content = readFile(path, "utf8")
@@ -647,7 +689,7 @@ css.load = function (path) {
647689
const content = readFile(file, "utf8")
648690
const { valid, message } = isCSSValid(content)
649691
if (!valid) {
650-
throw new Error(`CSSError: invalid CSS for path "${file}": ${message}`)
692+
throw new CSSError(`invalid CSS for path "${file}": ${message}`)
651693
}
652694
return css`
653695
${content}
@@ -879,9 +921,7 @@ function base64({ content, path }) {
879921
nodes.Img.load = function (path) {
880922
const type = extension(path)
881923
if (!ALLOWED_IMAGE_EXTENSIONS.includes(type)) {
882-
throw new Error(
883-
`ImageError: unsupported image type "${type}" for path "${path}"`
884-
)
924+
throw new ImageError(`unsupported image type "${type}" for path "${path}"`)
885925
}
886926
const content = readFile(path, "base64")
887927
return (options) => {
@@ -934,9 +974,7 @@ const sanitizeSVG = (content) => {
934974
nodes.Svg.load = function (path, options = {}) {
935975
const type = extension(path)
936976
if (type !== "svg") {
937-
throw new Error(
938-
`SVGError: unsupported SVG type "${type}" for path "${path}"`
939-
)
977+
throw new SVGError(`unsupported SVG type "${type}" for path "${path}"`)
940978
}
941979
let content = readFile(path, "utf8")
942980
if (options.sanitize !== false) {
@@ -976,26 +1014,28 @@ const json = {
9761014
try {
9771015
return JSON.parse(content)
9781016
} catch (exception) {
979-
throw new Error(
980-
`JSONError: cannot parse file "${file}": ${exception.message}`
981-
)
1017+
throw new JSONError(`cannot parse file "${file}": ${exception.message}`)
9821018
}
9831019
},
9841020
}
9851021

9861022
function i18n(translations) {
9871023
return function translate(language, key) {
9881024
if (key === undefined) {
989-
throw new TranslationError('key is undefined')
1025+
throw new TranslationError("key is undefined")
9901026
}
9911027
if (language === undefined) {
992-
throw new TranslationError('language is undefined')
1028+
throw new TranslationError("language is undefined")
9931029
}
9941030
if (translations[key] === undefined) {
995-
throw new TranslationError(`translation [${key}][${language}] is undefined`)
1031+
throw new TranslationError(
1032+
`translation [${key}][${language}] is undefined`,
1033+
)
9961034
}
9971035
if (translations[key][language] === undefined) {
998-
throw new TranslationError(`translation [${key}][${language}] is undefined`)
1036+
throw new TranslationError(
1037+
`translation [${key}][${language}] is undefined`,
1038+
)
9991039
}
10001040
return translations[key][language]
10011041
}
@@ -1022,7 +1062,7 @@ i18n.load = function (path, options = {}) {
10221062
}
10231063
if (!data[key] || !data[key][language]) {
10241064
throw new TranslationError(
1025-
`translation [${key}][${language}] is undefined`
1065+
`translation [${key}][${language}] is undefined`,
10261066
)
10271067
}
10281068
return data[key][language]
@@ -1037,19 +1077,19 @@ function component(fn, { styles, i18n, scripts } = {}) {
10371077
if (i18n) {
10381078
if (!a || !a.language) {
10391079
throw new TranslationError(
1040-
`language is undefined for component:\n${fn.toString()}`
1080+
`language is undefined for component:\n${fn.toString()}`,
10411081
)
10421082
}
10431083
const { language } = a
10441084
function translate(key) {
10451085
if (!key) {
10461086
throw new TranslationError(
1047-
`key is undefined for component:\n${fn.toString()}`
1087+
`key is undefined for component:\n${fn.toString()}`,
10481088
)
10491089
}
10501090
if (!i18n[key] || !i18n[key][language]) {
10511091
throw new TranslationError(
1052-
`translation [${key}][${language}] is undefined for component:\n${fn.toString()}`
1092+
`translation [${key}][${language}] is undefined for component:\n${fn.toString()}`,
10531093
)
10541094
}
10551095
const translation = i18n[key][language]
@@ -1090,5 +1130,11 @@ module.exports = {
10901130
tag,
10911131
i18n,
10921132
TranslationError,
1133+
FileError,
1134+
RawError,
1135+
CSSError,
1136+
ImageError,
1137+
SVGError,
1138+
JSONError,
10931139
...nodes,
10941140
}

test/css/load/index.test.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
const test = require("node:test")
22
const assert = require("node:assert")
3-
const { compile } = require("../../..")
3+
const { compile, CSSError } = require("../../..")
44

55
test("#css.load: throws when a broken css is loaded", () => {
66
try {
@@ -9,6 +9,7 @@ test("#css.load: throws when a broken css is loaded", () => {
99
assert.fail("Expected an error to be thrown")
1010
} catch (error) {
1111
assert.ok(error)
12-
assert.ok(error.message.includes("CSSError: invalid CSS for path"))
12+
assert.ok(error instanceof CSSError)
13+
assert.ok(error.message.includes("invalid CSS for path"))
1314
}
1415
})

test/i18n/translation-is-undefined-for-component/index.test.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,7 @@ test("i18n is going to throw if a translation is missing", async () => {
1111
error = exception
1212
}
1313
assert(error instanceof TranslationError)
14-
assert(error.message.includes("translation [bar][en] is undefined for component"))
14+
assert(
15+
error.message.includes("translation [bar][en] is undefined for component"),
16+
)
1517
})

test/img/not-found/index.test.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
const test = require("node:test")
22
const assert = require("node:assert")
3-
const { compile } = require("../../..")
3+
const { compile, FileError } = require("../../..")
44

55
test("#img.load: throws when an image is not found", () => {
66
try {
@@ -9,6 +9,7 @@ test("#img.load: throws when an image is not found", () => {
99
assert.fail("Expected an error to be thrown")
1010
} catch (error) {
1111
assert.ok(error)
12-
assert.ok(error.message.includes("FileError: cannot read file"))
12+
assert.ok(error instanceof FileError)
13+
assert.ok(error.message.includes("cannot read file"))
1314
}
1415
})

test/json/load/index.test.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
const test = require("node:test")
22
const assert = require("node:assert")
3-
const { compile } = require("../../..")
3+
const { compile, JSONError } = require("../../..")
44

55
test("#json.load: throws when a broken json is loaded", () => {
66
try {
@@ -9,6 +9,7 @@ test("#json.load: throws when a broken json is loaded", () => {
99
assert.fail("Expected an error to be thrown")
1010
} catch (error) {
1111
assert.ok(error)
12-
assert.ok(error.message.includes("JSONError: cannot parse file"))
12+
assert.ok(error instanceof JSONError)
13+
assert.ok(error.message.includes("cannot parse file"))
1314
}
1415
})

test/raw/error/index.test.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
const test = require("node:test")
22
const assert = require("node:assert")
3-
const { compile } = require("../../..")
3+
const { compile, RawError } = require("../../..")
44

55
test("#raw.load: throws when an unsupported SVG type is loaded", () => {
66
try {
@@ -9,6 +9,7 @@ test("#raw.load: throws when an unsupported SVG type is loaded", () => {
99
assert.fail("Expected an error to be thrown")
1010
} catch (error) {
1111
assert.ok(error)
12-
assert.ok(error.message.includes("RawError: unsupported raw type"))
12+
assert.ok(error instanceof RawError)
13+
assert.ok(error.message.includes("unsupported raw type"))
1314
}
1415
})

test/svg/error/index.test.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
const test = require("node:test")
22
const assert = require("node:assert")
3-
const { compile } = require("../../..")
3+
const { compile, SVGError } = require("../../..")
44

55
test("#svg.load: throws when an unsupported SVG type is loaded", () => {
66
try {
@@ -9,6 +9,7 @@ test("#svg.load: throws when an unsupported SVG type is loaded", () => {
99
assert.fail("Expected an error to be thrown")
1010
} catch (error) {
1111
assert.ok(error)
12-
assert.ok(error.message.includes("SVGError: unsupported SVG type"))
12+
assert.ok(error instanceof SVGError)
13+
assert.ok(error.message.includes("unsupported SVG type"))
1314
}
1415
})

0 commit comments

Comments
 (0)