Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -29,27 +29,43 @@ const ERROR_INFO = {
level: DiagnosticLevel.Error,
};

const ERROR_INFO_WITH_URL = {
code: 4,
message: 'Test Error {0} with message {1}',
level: DiagnosticLevel.Error,
url: 'https://example.com/docs/lwc4',
};

const GENERIC_ERROR = {
code: 100,
message: 'Unexpected error: {0}',
level: DiagnosticLevel.Error,
};

const GENERIC_ERROR_WITH_URL = {
code: 100,
message: 'Unexpected error: {0}',
level: DiagnosticLevel.Error,
url: 'https://example.com/docs/lwc100',
};

class CustomError extends Error {
public lwcCode?: number;
public filename?: string;
public line?: number;
public column?: number;
public start?: number;
public length?: number;
public url?: string;

constructor(
message: string,
filename?: string,
line?: number,
column?: number,
start?: number,
length?: number
length?: number,
url?: string
) {
super(message);

Expand All @@ -60,6 +76,7 @@ class CustomError extends Error {
this.column = column;
this.start = start;
this.length = length;
this.url = url;
}
}

Expand Down Expand Up @@ -93,6 +110,34 @@ describe('error handling', () => {
})
).toEqual(target);
});

it('includes url in the compiler diagnostic when errorInfo has url', () => {
const target = {
code: 4,
message: 'LWC4: Test Error arg1 with message 500',
level: DiagnosticLevel.Error,
url: 'https://example.com/docs/lwc4',
filename: 'test.js',
location: DEFAULT_LOCATION,
};

expect(
generateCompilerDiagnostic(ERROR_INFO_WITH_URL, {
messageArgs: ['arg1', 500],
origin: {
filename: 'test.js',
location: DEFAULT_LOCATION,
},
})
).toEqual(target);
});

it('generates a compiler diagnostic with undefined url when errorInfo has no url', () => {
const diagnostic = generateCompilerDiagnostic(ERROR_INFO, {
messageArgs: ['arg1', 500],
});
expect(diagnostic.url).toBeUndefined();
});
});

describe('generate compiler error', () => {
Expand Down Expand Up @@ -141,6 +186,24 @@ describe('error handling', () => {
});
expect(error.location).toEqual(DEFAULT_LOCATION);
});

it('includes url in the compiler error when errorInfo has url', () => {
const args = ['arg1', 10];
const error = generateCompilerError(ERROR_INFO_WITH_URL, {
messageArgs: args,
});

expect(error.url).toEqual('https://example.com/docs/lwc4');
});

it('generates a compiler error with undefined url when errorInfo has no url', () => {
const args = ['arg1', 10];
const error = generateCompilerError(ERROR_INFO, {
messageArgs: args,
});

expect(error.url).toBeUndefined();
});
});

describe('normalizeToCompilerError', () => {
Expand Down Expand Up @@ -222,6 +285,39 @@ describe('error handling', () => {
})
).toEqual(target);
});

it('preserves url from existing compiler error', () => {
const error = new CompilerError(
100,
'LWC100: test err',
'test.js',
DEFAULT_LOCATION,
DiagnosticLevel.Error,
'https://example.com/docs/lwc100'
);
const normalized = normalizeToCompilerError(GENERIC_ERROR, error);
expect(normalized.url).toEqual('https://example.com/docs/lwc100');
});

it('includes url from error object when normalizing non-CompilerError', () => {
const error = new CustomError(
'test error',
'test.js',
3,
5,
16,
10,
'https://example.com/docs/custom'
);
const normalized = normalizeToCompilerError(GENERIC_ERROR, error);
expect(normalized.url).toEqual('https://example.com/docs/custom');
});

it('uses fallback url from errorInfo when error has no url', () => {
const error = new CustomError('test error', 'test.js', 3, 5, 16, 10);
const normalized = normalizeToCompilerError(GENERIC_ERROR_WITH_URL, error);
expect(normalized.url).toEqual('https://example.com/docs/lwc100');
});
});

describe('normalizeToDiagnostic', () => {
Expand Down Expand Up @@ -301,5 +397,164 @@ describe('error handling', () => {
})
).toEqual(target);
});

it('preserves url from compiler error when converting to diagnostic', () => {
const error = new CompilerError(
100,
'LWC100: test error',
'test.js',
DEFAULT_LOCATION,
DiagnosticLevel.Error,
'https://example.com/docs/lwc100'
);
const diagnostic = normalizeToDiagnostic(GENERIC_ERROR, error);
expect(diagnostic.url).toEqual('https://example.com/docs/lwc100');
});

it('includes url from error object when normalizing non-CompilerError', () => {
const error = new CustomError(
'test error',
'test.js',
2,
5,
10,
5,
'https://example.com/docs/custom'
);
const diagnostic = normalizeToDiagnostic(GENERIC_ERROR, error);
expect(diagnostic.url).toEqual('https://example.com/docs/custom');
});

it('uses fallback url from errorInfo when error has no url', () => {
const error = new CustomError('test error', 'test.js', 2, 5, 10, 5);
const diagnostic = normalizeToDiagnostic(GENERIC_ERROR_WITH_URL, error);
expect(diagnostic.url).toEqual('https://example.com/docs/lwc100');
});

it('returns undefined url when neither error nor errorInfo has url', () => {
const error = new CustomError('test error', 'test.js', 2, 5, 10, 5);
const diagnostic = normalizeToDiagnostic(GENERIC_ERROR, error);
expect(diagnostic.url).toBeUndefined();
});
});

describe('CompilerError', () => {
describe('constructor', () => {
it('creates a CompilerError with all properties including url', () => {
const error = new CompilerError(
100,
'test message',
'test.js',
DEFAULT_LOCATION,
DiagnosticLevel.Error,
'https://example.com/docs/lwc100'
);

expect(error.code).toEqual(100);
expect(error.message).toEqual('test message');
expect(error.filename).toEqual('test.js');
expect(error.location).toEqual(DEFAULT_LOCATION);
expect(error.level).toEqual(DiagnosticLevel.Error);
expect(error.url).toEqual('https://example.com/docs/lwc100');
});

it('creates a CompilerError with undefined url when not provided', () => {
const error = new CompilerError(100, 'test message');
expect(error.url).toBeUndefined();
});
});

describe('static from', () => {
it('creates a CompilerError from a diagnostic preserving url', () => {
const diagnostic = {
code: 100,
message: 'test message',
level: DiagnosticLevel.Error,
filename: 'test.js',
location: DEFAULT_LOCATION,
url: 'https://example.com/docs/lwc100',
};

const error = CompilerError.from(diagnostic);

expect(error.code).toEqual(100);
expect(error.message).toEqual('test message');
expect(error.filename).toEqual('test.js');
expect(error.location).toEqual(DEFAULT_LOCATION);
expect(error.url).toEqual('https://example.com/docs/lwc100');
});

it('creates a CompilerError from a diagnostic without url', () => {
const diagnostic = {
code: 100,
message: 'test message',
level: DiagnosticLevel.Error,
filename: 'test.js',
location: DEFAULT_LOCATION,
};

const error = CompilerError.from(diagnostic);

expect(error.url).toBeUndefined();
});

it('uses origin for filename and location when provided', () => {
const diagnostic = {
code: 100,
message: 'test message',
level: DiagnosticLevel.Error,
filename: 'old.js',
location: { line: 1, column: 1, start: 1, length: 1 },
url: 'https://example.com/docs/lwc100',
};

const error = CompilerError.from(diagnostic, {
filename: 'new.js',
location: DEFAULT_LOCATION,
});

expect(error.filename).toEqual('new.js');
expect(error.location).toEqual(DEFAULT_LOCATION);
expect(error.url).toEqual('https://example.com/docs/lwc100');
});
});

describe('toDiagnostic', () => {
it('converts CompilerError to diagnostic preserving url', () => {
const error = new CompilerError(
100,
'test message',
'test.js',
DEFAULT_LOCATION,
DiagnosticLevel.Error,
'https://example.com/docs/lwc100'
);

const diagnostic = error.toDiagnostic();

expect(diagnostic).toEqual({
code: 100,
message: 'test message',
filename: 'test.js',
location: DEFAULT_LOCATION,
level: DiagnosticLevel.Error,
url: 'https://example.com/docs/lwc100',
});
});

it('converts CompilerError to diagnostic with undefined url', () => {
const error = new CompilerError(
100,
'test message',
'test.js',
DEFAULT_LOCATION,
DiagnosticLevel.Error
);

const diagnostic = error.toDiagnostic();

expect(diagnostic.url).toBeUndefined();
});
});
});
});
18 changes: 14 additions & 4 deletions packages/@lwc/errors/src/compiler/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export function generateCompilerDiagnostic(
code: errorInfo.code,
message,
level: getEffectiveErrorLevel(errorInfo, useStrictErrorOverride),
url: errorInfo.url,
};

if (config && config.origin) {
Expand All @@ -81,7 +82,14 @@ export function generateCompilerError(
): CompilerError {
const message = generateErrorMessage(errorInfo, config && config.messageArgs);
const level = getEffectiveErrorLevel(errorInfo, useStrictErrorOverride);
const error = new CompilerError(errorInfo.code, message, undefined, undefined, level);
const error = new CompilerError(
errorInfo.code,
message,
undefined,
undefined,
level,
errorInfo.url
);

if (config) {
error.filename = getFilename(config.origin);
Expand Down Expand Up @@ -126,7 +134,7 @@ export function normalizeToCompilerError(
}
return error;
}
const { code, message, filename, location, level } = convertErrorToDiagnostic(
const { code, message, filename, location, level, url } = convertErrorToDiagnostic(
error,
fallbackErrorInfo,
origin,
Expand All @@ -138,7 +146,8 @@ export function normalizeToCompilerError(
`${error.name}: ${message}`,
filename,
location,
level
level,
url
);
compilerError.stack = error.stack;
return compilerError;
Expand Down Expand Up @@ -199,9 +208,10 @@ function convertErrorToDiagnostic(
'level' in error ? error : fallbackErrorInfo,
useStrictErrorOverride
);
const url = error.url ?? fallbackErrorInfo.url;
const filename = getFilename(origin, error);
const location = getLocation(origin, error);

// TODO [#1289]: Preserve stack information
return { code, message, level, filename, location };
return { code, message, level, filename, location, url };
}
Loading
Loading