better errors, and/or support
All checks were successful
Publish npm package / publish (push) Successful in 24s
All checks were successful
Publish npm package / publish (push) Successful in 24s
This commit is contained in:
@@ -451,7 +451,7 @@ test("file if",() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
test("no endif should throw",() => {
|
test("no endif should throw",() => {
|
||||||
expect(() => initRenderer("./testdata/no_end_if")).toThrow()
|
expect(() => initRenderer("./testdata/no_end_if")).toThrow("test.txt")
|
||||||
})
|
})
|
||||||
|
|
||||||
test("path vars cannot write outside target",() => {
|
test("path vars cannot write outside target",() => {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@gregorlohaus/tdir",
|
"name": "@gregorlohaus/tdir",
|
||||||
"version": "0.1.9",
|
"version": "0.2.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
|
|||||||
38
parser.ts
38
parser.ts
@@ -1,5 +1,5 @@
|
|||||||
import { readdirSync, statSync, readFileSync } from "node:fs"
|
import { readdirSync, statSync, readFileSync } from "node:fs"
|
||||||
import { join } from "node:path"
|
import { join, relative } from "node:path"
|
||||||
import { TextDecoder } from "node:util"
|
import { TextDecoder } from "node:util"
|
||||||
import { getDirectiveTokens, splitArgs } from "./scanner"
|
import { getDirectiveTokens, splitArgs } from "./scanner"
|
||||||
|
|
||||||
@@ -36,10 +36,15 @@ function extractCondition(expr: string | undefined, vars: TemplateVariable[]) {
|
|||||||
throw new Error(`Invalid condition expression: ${expr}`)
|
throw new Error(`Invalid condition expression: ${expr}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
function extractFromString(text: string, vars: TemplateVariable[]) {
|
function extractFromString(text: string, vars: TemplateVariable[], source = "template") {
|
||||||
for (const token of getDirectiveTokens(text)) {
|
for (const token of getDirectiveTokens(text)) {
|
||||||
if (token.type === "if" || token.type === "elseif") {
|
if (token.type === "if" || token.type === "elseif") {
|
||||||
extractCondition(token.condition, vars)
|
try {
|
||||||
|
extractCondition(token.condition, vars)
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof Error) throw new Error(`${source}: ${error.message}`)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const match of text.matchAll(VAR_RE)) {
|
for (const match of text.matchAll(VAR_RE)) {
|
||||||
@@ -47,7 +52,7 @@ function extractFromString(text: string, vars: TemplateVariable[]) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function validateIfBlocks(content: string, vars: TemplateVariable[]) {
|
function validateIfBlocks(content: string, vars: TemplateVariable[], source: string) {
|
||||||
const stack: { sawElse: boolean }[] = []
|
const stack: { sawElse: boolean }[] = []
|
||||||
|
|
||||||
for (const token of getDirectiveTokens(content)) {
|
for (const token of getDirectiveTokens(content)) {
|
||||||
@@ -59,22 +64,22 @@ function validateIfBlocks(content: string, vars: TemplateVariable[]) {
|
|||||||
stack.push({ sawElse: false })
|
stack.push({ sawElse: false })
|
||||||
} else if (directive === "elseif") {
|
} else if (directive === "elseif") {
|
||||||
const frame = stack[stack.length - 1]
|
const frame = stack[stack.length - 1]
|
||||||
if (!frame) throw new Error("Unexpected <@elseif> without <@if>")
|
if (!frame) throw new Error(`${source}: Unexpected <@elseif> without <@if>`)
|
||||||
if (frame.sawElse) throw new Error("Unexpected <@elseif> after <@else>")
|
if (frame.sawElse) throw new Error(`${source}: Unexpected <@elseif> after <@else>`)
|
||||||
extractCondition(condition!, vars)
|
extractCondition(condition!, vars)
|
||||||
} else if (directive === "else") {
|
} else if (directive === "else") {
|
||||||
const frame = stack[stack.length - 1]
|
const frame = stack[stack.length - 1]
|
||||||
if (!frame) throw new Error("Unexpected <@else> without <@if>")
|
if (!frame) throw new Error(`${source}: Unexpected <@else> without <@if>`)
|
||||||
if (frame.sawElse) throw new Error("Unexpected duplicate <@else>")
|
if (frame.sawElse) throw new Error(`${source}: Unexpected duplicate <@else>`)
|
||||||
frame.sawElse = true
|
frame.sawElse = true
|
||||||
} else if (directive === "endif") {
|
} else if (directive === "endif") {
|
||||||
if (stack.length === 0) throw new Error("Unexpected <@endif> without <@if>")
|
if (stack.length === 0) throw new Error(`${source}: Unexpected <@endif> without <@if>`)
|
||||||
stack.pop()
|
stack.pop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stack.length > 0) {
|
if (stack.length > 0) {
|
||||||
throw new Error("Unmatched <@if> without <@endif>")
|
throw new Error(`${source}: Unmatched <@if> without <@endif>`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,21 +93,22 @@ function isUtf8Text(buffer: Buffer): boolean {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function walkDir(dirPath: string, vars: TemplateVariable[]) {
|
function walkDir(dirPath: string, vars: TemplateVariable[], rootPath: string) {
|
||||||
const entries = readdirSync(dirPath).sort()
|
const entries = readdirSync(dirPath).sort()
|
||||||
for (const entry of entries) {
|
for (const entry of entries) {
|
||||||
const fullPath = join(dirPath, entry)
|
const fullPath = join(dirPath, entry)
|
||||||
extractFromString(entry, vars)
|
const relativePath = relative(rootPath, fullPath)
|
||||||
|
extractFromString(entry, vars, relativePath || entry)
|
||||||
|
|
||||||
const stat = statSync(fullPath)
|
const stat = statSync(fullPath)
|
||||||
if (stat.isDirectory()) {
|
if (stat.isDirectory()) {
|
||||||
walkDir(fullPath, vars)
|
walkDir(fullPath, vars, rootPath)
|
||||||
} else if (stat.isFile()) {
|
} else if (stat.isFile()) {
|
||||||
const content = readFileSync(fullPath)
|
const content = readFileSync(fullPath)
|
||||||
if (isUtf8Text(content)) {
|
if (isUtf8Text(content)) {
|
||||||
const text = content.toString("utf-8")
|
const text = content.toString("utf-8")
|
||||||
extractFromString(text, vars)
|
extractFromString(text, vars, relativePath || fullPath)
|
||||||
validateIfBlocks(text, vars)
|
validateIfBlocks(text, vars, relativePath || fullPath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -110,7 +116,7 @@ function walkDir(dirPath: string, vars: TemplateVariable[]) {
|
|||||||
|
|
||||||
export function parse(dirPath: string): TemplateVariable[] {
|
export function parse(dirPath: string): TemplateVariable[] {
|
||||||
const vars: TemplateVariable[] = []
|
const vars: TemplateVariable[] = []
|
||||||
walkDir(dirPath, vars)
|
walkDir(dirPath, vars, dirPath)
|
||||||
const seen = new Set<string>()
|
const seen = new Set<string>()
|
||||||
return vars.filter(v => {
|
return vars.filter(v => {
|
||||||
const key = `${v.path}:${v.type}`
|
const key = `${v.path}:${v.type}`
|
||||||
|
|||||||
Reference in New Issue
Block a user