Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RECGEN-60 - Import error messages #32

Merged
merged 5 commits into from
Aug 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions generator/src/error/parse-error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export class ParseError extends Error {
constructor(public path: string, public line: number, public column: number, public cause: string, private template?: string) {

Check warning on line 2 in generator/src/error/parse-error.ts

View workflow job for this annotation

GitHub Actions / Test Generator

Constructor has too many parameters (5). Maximum allowed is 4
super(template ?
`Parse error in ${path} at line ${line}, column ${column} in template ${template}: ${cause}` :
`Parse error in ${path} at line ${line}, column ${column}: ${cause}`)

this.line = line
this.column = column
}
}
4 changes: 4 additions & 0 deletions generator/src/models/template-info.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface TemplateInfo {
path: string
lineOffset?: number
}
1 change: 1 addition & 0 deletions generator/src/models/template-metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ export interface TemplateMetadata {
content: Buffer;
constants: { [key: string]: any };
dependencies: string[];
frontMatterLength?: number;
variables: Record<string, unknown>;
}
15 changes: 13 additions & 2 deletions generator/src/rendering/renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,9 @@ export class Renderer {

// First, render all constants
for (const [key, value] of Object.entries(constants)) {
constants[key] = await engine.render(value, this.buildVariables())
constants[key] = await engine.render(value, this.buildVariables(), undefined, {
path: file.path,
})
}

const dependencies = Object.fromEntries(
Expand All @@ -102,7 +104,10 @@ export class Renderer {
const variables = this.buildVariables({constants, dependencies}, file.variables)
const output = engine.transformFilename(file.outputPath)

const content = await engine.render(file.content.toString(), variables, output)
const content = await engine.render(file.content.toString(), variables, output, {
path: file.path,
lineOffset: file.frontMatterLength ?? 0,
})
const fileExists = await filesystem?.exists(output)

if (!fileExists || await this.fileExistsConfirmation(output)) {
Expand Down Expand Up @@ -137,13 +142,18 @@ export class Renderer {
content,
constants: {},
dependencies: [],
frontMatterLength: 0,
variables,
}
}

const asString = content.toString()
const {data, content: body} = matter(asString)

const totalLines = asString.split('\n').length
const bodyLines = body.split('\n').length
const frontMatterLength = totalLines - bodyLines

const constants = data.constants ?? {}
const dependencies = data.dependencies ?? []
const id = data.id ?? null
Expand All @@ -156,6 +166,7 @@ export class Renderer {
content: Buffer.from(body),
constants,
dependencies,
frontMatterLength,
variables,
}
}
Expand Down
24 changes: 22 additions & 2 deletions generator/src/templating/nunjucks-template-engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import {StringModificationHelper} from '../helpers/string-modification-helper'
import {EntityFieldHelper} from '../helpers/entity-field-helper'
import {IncrementalDataHandler} from './incremental-data-handler'
import {Entity} from '../models/entity'
import {ParseError} from '../error/parse-error'
import {TemplateInfo} from '../models/template-info'

export class NunjucksTemplateEngine implements TemplateEngine {
private environment: nunjucks.Environment = this.buildEnvironment()
Expand All @@ -14,7 +16,7 @@ export class NunjucksTemplateEngine implements TemplateEngine {
this.environment = this.buildEnvironment(generatorName, incrementalDataHandler)
}

render(template: string, context: { [p: string]: any }, outputFile?: string): Promise<string> {
render(template: string, context: { [p: string]: any }, outputFile?: string, info?: TemplateInfo): Promise<string> {
return new Promise((resolve, reject) => {
this.environment.renderString(
template,
Expand All @@ -24,7 +26,7 @@ export class NunjucksTemplateEngine implements TemplateEngine {
},
(error, result) => {
if (error) {
reject(error)
reject(this.transformError(error, info, outputFile ? undefined : template))
} else {
resolve(result ?? '')
}
Expand Down Expand Up @@ -111,4 +113,22 @@ export class NunjucksTemplateEngine implements TemplateEngine {

return environment
}

transformError(error: Error, info?: TemplateInfo, template?: string): Error {
// This function attempts to transform the error into a more readable error, with correct line numbers.
// Although it is possible that the message changes in the future, it is still better than the default error message.
const asString = error.toString()
const path = info?.path

if (asString.includes('Template render error')) {
const matches = asString.match(/Template render error: \(unknown path\) \[Line (\d+), Column (\d+)]/)
const line = Number(matches?.[1] ?? 0) + (info?.lineOffset ?? 0)
const column = Number(matches?.[2] ?? 0)
const message = asString.split('\n')[1].trim()

return new ParseError(path ?? 'unknown', line, column, message, template)
}

return error
}
}
3 changes: 2 additions & 1 deletion generator/src/templating/template-engine.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import {IncrementalDataHandler} from './incremental-data-handler'
import {TemplateInfo} from '../models/template-info'

export interface TemplateEngine {
setup(generatorName: string, incrementalDataHandler: IncrementalDataHandler): Promise<void>;
render(template: string, context: { [key: string]: any }, outputFile?: string): Promise<string>;
render(template: string, context: { [key: string]: any }, outputFile?: string, info?: TemplateInfo): Promise<string>;
supports(path: string): boolean;
transformFilename(path: string): string;
}
2 changes: 2 additions & 0 deletions generator/test/rendering/renderer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,7 @@ Test file`.trim()
foo: 'bar',
},
dependencies: ['foo'],
frontMatterLength: 6,
variables: {},
}

Expand All @@ -301,6 +302,7 @@ Test file`.trim()
content: Buffer.from(fileContent),
constants: {},
dependencies: [],
frontMatterLength: 0,
variables: {
foo: 'bar',
},
Expand Down
42 changes: 42 additions & 0 deletions generator/test/templating/nunjucks-template-engine.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import {expect} from 'chai'
import {NunjucksTemplateEngine} from '../../src'
import {TemplateInfo} from '../../src/models/template-info'
import {ParseError} from '../../src/error/parse-error'

describe('nunjucks template engine', () => {
describe('render', () => {
Expand Down Expand Up @@ -60,4 +62,44 @@ describe('nunjucks template engine', () => {
expect(transformedPath).to.equal(expectedPath)
})
})

describe('transform parse errors', () => {
it('should transform parse errors into a ParseErrors object', () => {
// Arrange
const engine = new NunjucksTemplateEngine('views')

const info: TemplateInfo = {
path: 'index.njk',
lineOffset: 5,
}

const error = new Error(`Template render error: (unknown path) [Line 50, Column 4]
unknown block tag: abc`)

const result = engine.transformError(error, info)

// Assert
expect(result).to.be.instanceOf(ParseError)
expect((result as ParseError).line).to.equal(55)
expect((result as ParseError).column).to.equal(4)
expect((result as ParseError).message).to.equal('Parse error in index.njk at line 55, column 4: unknown block tag: abc')
})

it('should ignore non-parser errors', () => {
// Arrange
const engine = new NunjucksTemplateEngine('views')

const info: TemplateInfo = {
path: 'index.njk',
lineOffset: 5,
}

const error = new Error('Something else went wrong')

const result = engine.transformError(error, info)

// Assert
expect(result).not.to.be.instanceOf(ParseError)
})
})
})