summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--packages/astro/src/@types/astro.ts1
-rw-r--r--packages/astro/src/core/config/settings.ts1
-rw-r--r--packages/astro/src/core/dev/container.ts11
-rw-r--r--packages/astro/src/core/errors/errors-data.ts2
-rw-r--r--packages/astro/src/core/errors/utils.ts4
-rw-r--r--packages/astro/src/core/render/dev/environment.ts1
-rw-r--r--packages/astro/src/core/render/environment.ts5
-rw-r--r--packages/astro/src/events/error.ts54
-rw-r--r--packages/astro/src/vite-plugin-astro-server/request.ts5
-rw-r--r--packages/astro/test/events.test.js45
-rw-r--r--packages/astro/test/test-utils.js16
-rw-r--r--packages/astro/test/units/vite-plugin-astro-server/request.test.js8
-rw-r--r--packages/telemetry/src/index.ts1
13 files changed, 130 insertions, 24 deletions
diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts
index a1d8ff9f2..866c2eaf0 100644
--- a/packages/astro/src/@types/astro.ts
+++ b/packages/astro/src/@types/astro.ts
@@ -990,6 +990,7 @@ export interface AstroSettings {
tsConfig: TsConfigJson | undefined;
tsConfigPath: string | undefined;
watchFiles: string[];
+ forceDisableTelemetry: boolean;
}
export type AsyncRendererComponentFn<U> = (
diff --git a/packages/astro/src/core/config/settings.ts b/packages/astro/src/core/config/settings.ts
index 96cdcbe21..c9ac9da5e 100644
--- a/packages/astro/src/core/config/settings.ts
+++ b/packages/astro/src/core/config/settings.ts
@@ -18,6 +18,7 @@ export function createBaseSettings(config: AstroConfig): AstroSettings {
renderers: [jsxRenderer],
scripts: [],
watchFiles: [],
+ forceDisableTelemetry: false,
};
}
diff --git a/packages/astro/src/core/dev/container.ts b/packages/astro/src/core/dev/container.ts
index e059b1d06..5b2b04835 100644
--- a/packages/astro/src/core/dev/container.ts
+++ b/packages/astro/src/core/dev/container.ts
@@ -47,6 +47,7 @@ export interface CreateContainerParams {
// The string passed to --config and the resolved path
configFlag?: string;
configFlagPath?: string;
+ disableTelemetry?: boolean;
}
export async function createContainer(params: CreateContainerParams = {}): Promise<Container> {
@@ -55,8 +56,13 @@ export async function createContainer(params: CreateContainerParams = {}): Promi
logging = defaultLogging,
settings = await createDefaultDevSettings(params.userConfig, params.root),
fs = nodeFs,
+ disableTelemetry,
} = params;
+ if (disableTelemetry) {
+ settings.forceDisableTelemetry = true;
+ }
+
// Initialize
applyPolyfill();
settings = await runHookConfigSetup({
@@ -143,11 +149,14 @@ export function isStarted(container: Container): boolean {
return !!container.viteServer.httpServer?.listening;
}
+/**
+ * Only used in tests
+ */
export async function runInContainer(
params: CreateContainerParams,
callback: (container: Container) => Promise<void> | void
) {
- const container = await createContainer(params);
+ const container = await createContainer({ ...params, disableTelemetry: true });
try {
await callback(container);
} finally {
diff --git a/packages/astro/src/core/errors/errors-data.ts b/packages/astro/src/core/errors/errors-data.ts
index bd8a36749..e8a788f27 100644
--- a/packages/astro/src/core/errors/errors-data.ts
+++ b/packages/astro/src/core/errors/errors-data.ts
@@ -4,7 +4,7 @@
import type { ZodError } from 'zod';
-interface ErrorData {
+export interface ErrorData {
code: number;
title: string;
message?: string | ((...params: any) => string);
diff --git a/packages/astro/src/core/errors/utils.ts b/packages/astro/src/core/errors/utils.ts
index 0d503b09d..b4d37354f 100644
--- a/packages/astro/src/core/errors/utils.ts
+++ b/packages/astro/src/core/errors/utils.ts
@@ -1,6 +1,6 @@
import { DiagnosticCode } from '@astrojs/compiler/shared/diagnostics.js';
import type { SSRError } from '../../@types/astro.js';
-import { AstroErrorCodes, AstroErrorData } from './errors-data.js';
+import { AstroErrorCodes, AstroErrorData, ErrorData } from './errors-data.js';
/**
* Get the line and character based on the offset
@@ -96,7 +96,7 @@ export function getErrorDataByCode(code: AstroErrorCodes | DiagnosticCode) {
if (entry) {
return {
name: entry[0],
- data: entry[1],
+ data: entry[1] as ErrorData,
};
}
}
diff --git a/packages/astro/src/core/render/dev/environment.ts b/packages/astro/src/core/render/dev/environment.ts
index 1fabf486d..58a126b02 100644
--- a/packages/astro/src/core/render/dev/environment.ts
+++ b/packages/astro/src/core/render/dev/environment.ts
@@ -36,6 +36,7 @@ export function createDevelopmentEnvironment(
site: settings.config.site,
ssr: settings.config.output === 'server',
streaming: true,
+ telemetry: Boolean(settings.forceDisableTelemetry),
});
return {
diff --git a/packages/astro/src/core/render/environment.ts b/packages/astro/src/core/render/environment.ts
index edf3cc0e6..76cb014cd 100644
--- a/packages/astro/src/core/render/environment.ts
+++ b/packages/astro/src/core/render/environment.ts
@@ -21,6 +21,7 @@ export interface Environment {
site?: string;
ssr: boolean;
streaming: boolean;
+ telemetry?: boolean;
}
export type CreateEnvironmentArgs = Environment;
@@ -33,6 +34,9 @@ export type CreateBasicEnvironmentArgs = Partial<Environment> & {
logging: CreateEnvironmentArgs['logging'];
};
+/**
+ * Only used in tests
+ */
export function createBasicEnvironment(options: CreateBasicEnvironmentArgs): Environment {
const mode = options.mode ?? 'development';
return createEnvironment({
@@ -48,5 +52,6 @@ export function createBasicEnvironment(options: CreateBasicEnvironmentArgs): Env
routeCache: new RouteCache(options.logging, mode),
ssr: options.ssr ?? true,
streaming: options.streaming ?? true,
+ telemetry: false,
});
}
diff --git a/packages/astro/src/events/error.ts b/packages/astro/src/events/error.ts
index 12ec3c650..f8342fbf4 100644
--- a/packages/astro/src/events/error.ts
+++ b/packages/astro/src/events/error.ts
@@ -1,10 +1,12 @@
import { ZodError } from 'zod';
-import { AstroErrorData, ErrorWithMetadata } from '../core/errors/index.js';
+import { AstroError, AstroErrorData, ErrorWithMetadata } from '../core/errors/index.js';
+import { getErrorDataByCode } from '../core/errors/utils.js';
const EVENT_ERROR = 'ASTRO_CLI_ERROR';
interface ErrorEventPayload {
code: number | undefined;
+ name: string;
isFatal: boolean;
plugin?: string | undefined;
cliCommand: string;
@@ -22,10 +24,8 @@ interface ConfigErrorEventPayload extends ErrorEventPayload {
* content like a filename, filepath, or any other code-specific value.
* We also trim this value even further to just a few words.
*
- * Our goal is to remove this entirely before v1.0.0 is released, as we work
- * to add a proper error code system (see AstroErrorCodes for examples).
- *
- * TODO(fks): Remove around v1.0.0 release.
+ * This is only used for errors that do not come from us so we can get a basic
+ * and anonymous idea of what the error is about.
*/
const ANONYMIZE_MESSAGE_REGEX = /^(\w| )+/;
function anonymizeErrorMessage(msg: string): string | undefined {
@@ -47,6 +47,7 @@ export function eventConfigError({
}): { eventName: string; payload: ConfigErrorEventPayload }[] {
const payload: ConfigErrorEventPayload = {
code: AstroErrorData.UnknownConfigError.code,
+ name: 'ZodError',
isFatal,
isConfig: true,
cliCommand: cmd,
@@ -64,12 +65,51 @@ export function eventError({
cmd: string;
isFatal: boolean;
}): { eventName: string; payload: ErrorEventPayload }[] {
+ const errorData =
+ AstroError.is(err) && err.errorCode ? getErrorDataByCode(err.errorCode)?.data : undefined;
+
const payload: ErrorEventPayload = {
- code: err.code || AstroErrorData.UnknownError.code,
+ code: err.code || err.errorCode || AstroErrorData.UnknownError.code,
+ name: err.name,
plugin: err.plugin,
cliCommand: cmd,
isFatal: isFatal,
- anonymousMessageHint: anonymizeErrorMessage(err.message),
+ anonymousMessageHint:
+ errorData && errorData.message
+ ? getSafeErrorMessage(errorData.message)
+ : anonymizeErrorMessage(err.message),
};
return [{ eventName: EVENT_ERROR, payload }];
}
+
+/**
+ * Safely get the error message from an error, even if it's a function.
+ */
+// eslint-disable-next-line @typescript-eslint/ban-types
+function getSafeErrorMessage(message: string | Function): string {
+ if (typeof message === 'string') {
+ return message;
+ } else {
+ return String.raw({
+ raw: extractStringFromFunction(message.toString()),
+ });
+ }
+
+ function extractStringFromFunction(func: string) {
+ const arrowIndex = func.indexOf('=>') + '=>'.length;
+
+ return func
+ .slice(arrowIndex)
+ .trim()
+ .slice(1, -1)
+ .replace(
+ /\${([^}]+)}/gm,
+ (str, match1) =>
+ `${match1
+ .split(/\.?(?=[A-Z])/)
+ .join('_')
+ .toUpperCase()}`
+ )
+ .replace(/\\`/g, '`');
+ }
+}
diff --git a/packages/astro/src/vite-plugin-astro-server/request.ts b/packages/astro/src/vite-plugin-astro-server/request.ts
index 1a144de5f..6dbbbb916 100644
--- a/packages/astro/src/vite-plugin-astro-server/request.ts
+++ b/packages/astro/src/vite-plugin-astro-server/request.ts
@@ -8,6 +8,7 @@ import { createSafeError } from '../core/errors/index.js';
import { error } from '../core/logger/core.js';
import * as msg from '../core/messages.js';
import { removeTrailingForwardSlash } from '../core/path.js';
+import { eventError, telemetry } from '../events/index.js';
import { runWithErrorHandling } from './controller.js';
import { handle500Response } from './response.js';
import { handleRoute, matchRoute } from './route.js';
@@ -83,6 +84,10 @@ export async function handleRequest(
// Our error should already be complete, but let's try to add a bit more through some guesswork
const errorWithMetadata = collectErrorMetadata(err, config.root);
+ if (env.telemetry !== false) {
+ telemetry.record(eventError({ cmd: 'dev', err: errorWithMetadata, isFatal: false }));
+ }
+
error(env.logging, null, msg.formatErrorMessage(errorWithMetadata));
handle500Response(moduleLoader, res, errorWithMetadata);
diff --git a/packages/astro/test/events.test.js b/packages/astro/test/events.test.js
index a31c501b9..98eaa2d54 100644
--- a/packages/astro/test/events.test.js
+++ b/packages/astro/test/events.test.js
@@ -1,5 +1,6 @@
import { expect } from 'chai';
import { AstroErrorData } from '../dist/core/errors/errors-data.js';
+import { AstroError } from '../dist/core/errors/errors.js';
import * as events from '../dist/events/index.js';
describe('Events', () => {
@@ -427,6 +428,7 @@ describe('Events', () => {
eventName: 'ASTRO_CLI_ERROR',
payload: {
code: AstroErrorData.UnknownConfigError.code,
+ name: 'ZodError',
isFatal: true,
isConfig: true,
cliCommand: 'COMMAND_NAME',
@@ -451,6 +453,7 @@ describe('Events', () => {
payload: {
code: 1234,
plugin: 'TEST PLUGIN',
+ name: 'Error',
isFatal: true,
cliCommand: 'COMMAND_NAME',
anonymousMessageHint: 'TEST ERROR MESSAGE',
@@ -458,6 +461,30 @@ describe('Events', () => {
});
});
+ it('returns the expected event payload for AstroError', () => {
+ const [event] = events.eventError({
+ err: new AstroError({
+ ...AstroErrorData.ClientAddressNotAvailable,
+ message: AstroErrorData.ClientAddressNotAvailable.message('mysuperadapter'),
+ }),
+ cmd: 'COMMAND_NAME',
+ isFatal: false,
+ });
+
+ expect(event).to.deep.equal({
+ eventName: 'ASTRO_CLI_ERROR',
+ payload: {
+ anonymousMessageHint:
+ '`Astro.clientAddress` is not available in the `ADAPTER_NAME` adapter. File an issue with the adapter to add support.',
+ cliCommand: 'COMMAND_NAME',
+ code: 3002,
+ isFatal: false,
+ name: 'ClientAddressNotAvailable',
+ plugin: undefined,
+ },
+ });
+ });
+
it('returns the expected event payload with a generic error', () => {
const [event] = events.eventError({
err: new Error('TEST ERROR MESSAGE'),
@@ -468,6 +495,7 @@ describe('Events', () => {
eventName: 'ASTRO_CLI_ERROR',
payload: {
code: AstroErrorData.UnknownError.code,
+ name: 'Error',
plugin: undefined,
isFatal: false,
cliCommand: 'COMMAND_NAME',
@@ -480,9 +508,26 @@ describe('Events', () => {
const [event] = events.eventError({
err: new Error('TEST ERROR MESSAGE: Sensitive data is "/Users/MYNAME/foo.astro"'),
cmd: 'COMMAND_NAME',
+ name: 'Error',
isFatal: true,
});
expect(event.payload.anonymousMessageHint).to.equal('TEST ERROR MESSAGE');
});
+
+ it('properly exclude stack traces from anonymousMessageHint', () => {
+ // Some libraries/frameworks returns stack traces in the error message, make sure we don't include that in the anonymousMessageHint
+ const [event] = events.eventError({
+ err: new Error(`[postcss] /home/projects/github-ssfd5p/src/components/Counter.css:3:15: Missed semicolon
+ at Input.error (file:///home/projects/github-ssfd5p/node_modules/postcss/lib/input.js:148:16)
+ at Parser.checkMissedSemicolon (file:///home/projects/github-ssfd5p/node_modules/postcss/lib/parser.js:596:22)
+ at Parser.decl (file:///home/projects/github-ssfd5p/node_modules/postcss/lib/parser.js:279:12)
+ at Parser.other (file:///home/projects/github-ssfd5p/node_modules/postcss/lib/parser.js:128:18)
+ at Parser.parse (file:///home/projects/github-ssfd5p/node_modules/postcss/lib/parser.js:72:16)`),
+ cmd: 'COMMAND_NAME',
+ name: 'Error',
+ isFatal: true,
+ });
+ expect(event.payload.anonymousMessageHint).to.be.undefined;
+ });
});
});
diff --git a/packages/astro/test/test-utils.js b/packages/astro/test/test-utils.js
index 47e753a0a..f9e1e40ee 100644
--- a/packages/astro/test/test-utils.js
+++ b/packages/astro/test/test-utils.js
@@ -1,17 +1,17 @@
-import { execa } from 'execa';
import { polyfill } from '@astrojs/webapi';
+import { execa } from 'execa';
+import fastGlob from 'fast-glob';
import fs from 'fs';
+import os from 'os';
+import stripAnsi from 'strip-ansi';
import { fileURLToPath } from 'url';
+import { sync } from '../dist/cli/sync/index.js';
+import build from '../dist/core/build/index.js';
import { loadConfig } from '../dist/core/config/config.js';
import { createSettings } from '../dist/core/config/index.js';
import dev from '../dist/core/dev/index.js';
-import build from '../dist/core/build/index.js';
-import preview from '../dist/core/preview/index.js';
-import { sync } from '../dist/cli/sync/index.js';
import { nodeLogDestination } from '../dist/core/logger/node.js';
-import os from 'os';
-import stripAnsi from 'strip-ansi';
-import fastGlob from 'fast-glob';
+import preview from '../dist/core/preview/index.js';
// polyfill WebAPIs to globalThis for Node v12, Node v14, and Node v16
polyfill(globalThis, {
@@ -243,7 +243,7 @@ const cliPath = fileURLToPath(new URL('../astro.js', import.meta.url));
/** Returns a process running the Astro CLI. */
export function cli(/** @type {string[]} */ ...args) {
- const spawned = execa('node', [cliPath, ...args]);
+ const spawned = execa('node', [cliPath, ...args], { env: {'ASTRO_TELEMETRY_DISABLED': true}});
spawned.stdout.setEncoding('utf8');
diff --git a/packages/astro/test/units/vite-plugin-astro-server/request.test.js b/packages/astro/test/units/vite-plugin-astro-server/request.test.js
index e48fa27ba..66a5c1638 100644
--- a/packages/astro/test/units/vite-plugin-astro-server/request.test.js
+++ b/packages/astro/test/units/vite-plugin-astro-server/request.test.js
@@ -1,13 +1,13 @@
import { expect } from 'chai';
-import { createLoader } from '../../../dist/core/module-loader/index.js';
-import { createController, handleRequest } from '../../../dist/vite-plugin-astro-server/index.js';
import { createDefaultDevSettings } from '../../../dist/core/config/index.js';
+import { createLoader } from '../../../dist/core/module-loader/index.js';
import { createBasicEnvironment } from '../../../dist/core/render/index.js';
import { createRouteManifest } from '../../../dist/core/routing/index.js';
-import { defaultLogging as logging } from '../../test-utils.js';
import { createComponent, render } from '../../../dist/runtime/server/index.js';
-import { createRequestAndResponse, createFs, createAstroModule } from '../test-utils.js';
+import { createController, handleRequest } from '../../../dist/vite-plugin-astro-server/index.js';
+import { defaultLogging as logging } from '../../test-utils.js';
+import { createAstroModule, createFs, createRequestAndResponse } from '../test-utils.js';
async function createDevEnvironment(overrides = {}) {
const env = createBasicEnvironment({
diff --git a/packages/telemetry/src/index.ts b/packages/telemetry/src/index.ts
index f10bff475..3bac11d52 100644
--- a/packages/telemetry/src/index.ts
+++ b/packages/telemetry/src/index.ts
@@ -145,7 +145,6 @@ export class AstroTelemetry {
// to preview what data would be sent.
return Promise.resolve();
}
-
return post({
context,
meta,