mirror of
https://github.com/withastro/astro.git
synced 2024-12-16 21:46:22 -05:00
improve telemetry (#8600)
This commit is contained in:
parent
0119a271ae
commit
ed54d46449
6 changed files with 155 additions and 426 deletions
6
.changeset/large-shoes-hammer.md
Normal file
6
.changeset/large-shoes-hammer.md
Normal file
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
'@astrojs/telemetry': patch
|
||||
'astro': patch
|
||||
---
|
||||
|
||||
Improve config info telemetry
|
|
@ -9,7 +9,12 @@ import ora from 'ora';
|
|||
import preferredPM from 'preferred-pm';
|
||||
import prompts from 'prompts';
|
||||
import type yargs from 'yargs-parser';
|
||||
import { loadTSConfig, resolveConfigPath, resolveRoot } from '../../core/config/index.js';
|
||||
import {
|
||||
loadTSConfig,
|
||||
resolveConfig,
|
||||
resolveConfigPath,
|
||||
resolveRoot,
|
||||
} from '../../core/config/index.js';
|
||||
import {
|
||||
defaultTSConfig,
|
||||
presets,
|
||||
|
@ -23,7 +28,7 @@ import { appendForwardSlash } from '../../core/path.js';
|
|||
import { apply as applyPolyfill } from '../../core/polyfill.js';
|
||||
import { parseNpmName } from '../../core/util.js';
|
||||
import { eventCliSession, telemetry } from '../../events/index.js';
|
||||
import { createLoggerFromFlags } from '../flags.js';
|
||||
import { createLoggerFromFlags, flagsToAstroInlineConfig } from '../flags.js';
|
||||
import { generate, parse, t, visit } from './babel.js';
|
||||
import { ensureImport } from './imports.js';
|
||||
import { wrapDefaultExport } from './wrapper.js';
|
||||
|
@ -87,7 +92,9 @@ async function getRegistry(): Promise<string> {
|
|||
}
|
||||
|
||||
export async function add(names: string[], { flags }: AddOptions) {
|
||||
telemetry.record(eventCliSession('add'));
|
||||
const inlineConfig = flagsToAstroInlineConfig(flags);
|
||||
const { userConfig } = await resolveConfig(inlineConfig, 'add');
|
||||
telemetry.record(eventCliSession('add', userConfig));
|
||||
applyPolyfill();
|
||||
if (flags.help || names.length === 0) {
|
||||
printHelp({
|
||||
|
|
|
@ -140,7 +140,6 @@ export const AstroConfigSchema = z.object({
|
|||
.optional()
|
||||
.default(ASTRO_CONFIG_DEFAULTS.build.excludeMiddleware),
|
||||
})
|
||||
.optional()
|
||||
.default({}),
|
||||
server: z.preprocess(
|
||||
// preprocess
|
||||
|
@ -158,7 +157,6 @@ export const AstroConfigSchema = z.object({
|
|||
port: z.number().optional().default(ASTRO_CONFIG_DEFAULTS.server.port),
|
||||
headers: z.custom<OutgoingHttpHeaders>().optional(),
|
||||
})
|
||||
.optional()
|
||||
.default({})
|
||||
),
|
||||
redirects: z
|
||||
|
@ -274,27 +272,11 @@ export const AstroConfigSchema = z.object({
|
|||
.optional()
|
||||
.default(ASTRO_CONFIG_DEFAULTS.experimental.optimizeHoistedScript),
|
||||
})
|
||||
.passthrough()
|
||||
.refine(
|
||||
(d) => {
|
||||
const validKeys = Object.keys(ASTRO_CONFIG_DEFAULTS.experimental);
|
||||
const invalidKeys = Object.keys(d).filter((key) => !validKeys.includes(key));
|
||||
if (invalidKeys.length > 0) return false;
|
||||
return true;
|
||||
},
|
||||
(d) => {
|
||||
const validKeys = Object.keys(ASTRO_CONFIG_DEFAULTS.experimental);
|
||||
const invalidKeys = Object.keys(d).filter((key) => !validKeys.includes(key));
|
||||
return {
|
||||
message: `Invalid experimental key: \`${invalidKeys.join(
|
||||
', '
|
||||
)}\`. \nMake sure the spelling is correct, and that your Astro version supports this experiment.\nSee https://docs.astro.build/en/reference/configuration-reference/#experimental-flags for more information.`,
|
||||
};
|
||||
}
|
||||
.strict(
|
||||
`Invalid or outdated experimental feature.\nCheck for incorrect spelling or outdated Astro version.\nSee https://docs.astro.build/en/reference/configuration-reference/#experimental-flags for a list of all current experiments.`
|
||||
)
|
||||
.optional()
|
||||
.default({}),
|
||||
legacy: z.object({}).optional().default({}),
|
||||
legacy: z.object({}).default({}),
|
||||
});
|
||||
|
||||
export type AstroConfigType = z.infer<typeof AstroConfigSchema>;
|
||||
|
|
|
@ -1,25 +1,8 @@
|
|||
import type { AstroUserConfig } from '../@types/astro.js';
|
||||
import type { AstroIntegration, AstroUserConfig } from '../@types/astro.js';
|
||||
import { AstroConfigSchema } from '../core/config/schema.js';
|
||||
|
||||
const EVENT_SESSION = 'ASTRO_CLI_SESSION_STARTED';
|
||||
|
||||
interface ConfigInfo {
|
||||
markdownPlugins: string[];
|
||||
adapter: string | null;
|
||||
integrations: string[];
|
||||
trailingSlash: undefined | 'always' | 'never' | 'ignore';
|
||||
build:
|
||||
| undefined
|
||||
| {
|
||||
format: undefined | 'file' | 'directory';
|
||||
};
|
||||
markdown:
|
||||
| undefined
|
||||
| {
|
||||
drafts: undefined | boolean;
|
||||
syntaxHighlight: undefined | 'shiki' | 'prism' | false;
|
||||
};
|
||||
}
|
||||
|
||||
interface EventPayload {
|
||||
cliCommand: string;
|
||||
config?: ConfigInfo;
|
||||
|
@ -28,87 +11,126 @@ interface EventPayload {
|
|||
optionalIntegrations?: number;
|
||||
}
|
||||
|
||||
const multiLevelKeys = new Set([
|
||||
'build',
|
||||
'markdown',
|
||||
'markdown.shikiConfig',
|
||||
'server',
|
||||
'vite',
|
||||
'vite.resolve',
|
||||
'vite.css',
|
||||
'vite.json',
|
||||
'vite.server',
|
||||
'vite.server.fs',
|
||||
'vite.build',
|
||||
'vite.preview',
|
||||
'vite.optimizeDeps',
|
||||
'vite.ssr',
|
||||
'vite.worker',
|
||||
]);
|
||||
function configKeys(obj: Record<string, any> | undefined, parentKey: string): string[] {
|
||||
if (!obj) {
|
||||
return [];
|
||||
type ConfigInfoValue = string | boolean | string[] | undefined;
|
||||
type ConfigInfoRecord = Record<string, ConfigInfoValue>;
|
||||
type ConfigInfoBase = {
|
||||
[alias in keyof AstroUserConfig]: ConfigInfoValue | ConfigInfoRecord;
|
||||
};
|
||||
export interface ConfigInfo extends ConfigInfoBase {
|
||||
build: ConfigInfoRecord;
|
||||
image: ConfigInfoRecord;
|
||||
markdown: ConfigInfoRecord;
|
||||
experimental: ConfigInfoRecord;
|
||||
legacy: ConfigInfoRecord;
|
||||
vite: ConfigInfoRecord | undefined;
|
||||
}
|
||||
|
||||
function measureIsDefined(val: unknown) {
|
||||
// if val is undefined, measure undefined as a value
|
||||
if (val === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
// otherwise, convert the value to a boolean
|
||||
return Boolean(val);
|
||||
}
|
||||
|
||||
return Object.entries(obj)
|
||||
.map(([key, value]) => {
|
||||
if (typeof value === 'object' && !Array.isArray(value)) {
|
||||
const localKey = parentKey ? parentKey + '.' + key : key;
|
||||
if (multiLevelKeys.has(localKey)) {
|
||||
let keys = configKeys(value, localKey).map((subkey) => key + '.' + subkey);
|
||||
keys.unshift(key);
|
||||
return keys;
|
||||
}
|
||||
}
|
||||
type StringLiteral<T> = T extends string ? (string extends T ? never : T) : never;
|
||||
|
||||
return key;
|
||||
})
|
||||
.flat(1);
|
||||
/**
|
||||
* Measure supports string literal values. Passing a generic `string` type
|
||||
* results in an error, to make sure generic user input is never measured directly.
|
||||
*/
|
||||
function measureStringLiteral<T extends string>(
|
||||
val: StringLiteral<T> | boolean | undefined
|
||||
): string | boolean | undefined {
|
||||
return val;
|
||||
}
|
||||
|
||||
function measureIntegration(val: AstroIntegration | false | null | undefined): string | undefined {
|
||||
if (!val || !val.name) {
|
||||
return undefined;
|
||||
}
|
||||
return val.name;
|
||||
}
|
||||
|
||||
function sanitizeConfigInfo(obj: object | undefined, validKeys: string[]): ConfigInfoRecord {
|
||||
if (!obj || validKeys.length === 0) {
|
||||
return {};
|
||||
}
|
||||
return validKeys.reduce(
|
||||
(result, key) => {
|
||||
result[key] = measureIsDefined((obj as Record<string, unknown>)[key]);
|
||||
return result;
|
||||
},
|
||||
{} as Record<string, boolean | undefined>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function creates an anonymous ConfigInfo object from the user's config.
|
||||
* All values are sanitized to preserve anonymity. Simple "exist" boolean checks
|
||||
* are used by default, with a few additional sanitized values added manually.
|
||||
* Helper functions should always be used to ensure correct sanitization.
|
||||
*/
|
||||
function createAnonymousConfigInfo(userConfig: AstroUserConfig) {
|
||||
// Sanitize and measure the generic config object
|
||||
// NOTE(fks): Using _def is the correct, documented way to get the `shape`
|
||||
// from a Zod object that includes a wrapping default(), optional(), etc.
|
||||
// Even though `_def` appears private, it is type-checked for us so that
|
||||
// any changes between versions will be detected.
|
||||
const configInfo: ConfigInfo = {
|
||||
...sanitizeConfigInfo(userConfig, Object.keys(AstroConfigSchema.shape)),
|
||||
build: sanitizeConfigInfo(
|
||||
userConfig.build,
|
||||
Object.keys(AstroConfigSchema.shape.build._def.innerType.shape)
|
||||
),
|
||||
image: sanitizeConfigInfo(
|
||||
userConfig.image,
|
||||
Object.keys(AstroConfigSchema.shape.image._def.innerType.shape)
|
||||
),
|
||||
markdown: sanitizeConfigInfo(
|
||||
userConfig.markdown,
|
||||
Object.keys(AstroConfigSchema.shape.markdown._def.innerType.shape)
|
||||
),
|
||||
experimental: sanitizeConfigInfo(
|
||||
userConfig.experimental,
|
||||
Object.keys(AstroConfigSchema.shape.experimental._def.innerType.shape)
|
||||
),
|
||||
legacy: sanitizeConfigInfo(
|
||||
userConfig.legacy,
|
||||
Object.keys(AstroConfigSchema.shape.legacy._def.innerType.shape)
|
||||
),
|
||||
vite: userConfig.vite
|
||||
? sanitizeConfigInfo(userConfig.vite, Object.keys(userConfig.vite))
|
||||
: undefined,
|
||||
};
|
||||
// Measure string literal/enum configuration values
|
||||
configInfo.build.format = measureStringLiteral(userConfig.build?.format);
|
||||
configInfo.markdown.syntaxHighlight = measureStringLiteral(userConfig.markdown?.syntaxHighlight);
|
||||
configInfo.output = measureStringLiteral(userConfig.output);
|
||||
configInfo.scopedStyleStrategy = measureStringLiteral(userConfig.scopedStyleStrategy);
|
||||
configInfo.trailingSlash = measureStringLiteral(userConfig.trailingSlash);
|
||||
// Measure integration & adapter usage
|
||||
configInfo.adapter = measureIntegration(userConfig.adapter);
|
||||
configInfo.integrations = userConfig.integrations
|
||||
?.flat(100)
|
||||
.map(measureIntegration)
|
||||
.filter(Boolean) as string[];
|
||||
// Return the sanitized ConfigInfo object
|
||||
return configInfo;
|
||||
}
|
||||
|
||||
export function eventCliSession(
|
||||
cliCommand: string,
|
||||
userConfig?: AstroUserConfig,
|
||||
userConfig: AstroUserConfig,
|
||||
flags?: Record<string, any>
|
||||
): { eventName: string; payload: EventPayload }[] {
|
||||
// Filter out falsy integrations
|
||||
const configValues = userConfig
|
||||
? {
|
||||
markdownPlugins: [
|
||||
...(userConfig?.markdown?.remarkPlugins?.map((p) =>
|
||||
typeof p === 'string' ? p : typeof p
|
||||
) ?? []),
|
||||
...(userConfig?.markdown?.rehypePlugins?.map((p) =>
|
||||
typeof p === 'string' ? p : typeof p
|
||||
) ?? []),
|
||||
] as string[],
|
||||
adapter: userConfig?.adapter?.name ?? null,
|
||||
integrations: (userConfig?.integrations ?? [])
|
||||
.filter(Boolean)
|
||||
.flat()
|
||||
.map((i: any) => i?.name),
|
||||
trailingSlash: userConfig?.trailingSlash,
|
||||
build: userConfig?.build
|
||||
? {
|
||||
format: userConfig?.build?.format,
|
||||
}
|
||||
: undefined,
|
||||
markdown: userConfig?.markdown
|
||||
? {
|
||||
drafts: userConfig.markdown?.drafts,
|
||||
syntaxHighlight: userConfig.markdown?.syntaxHighlight,
|
||||
}
|
||||
: undefined,
|
||||
}
|
||||
: undefined;
|
||||
|
||||
// Filter out yargs default `_` flag which is the cli command
|
||||
const cliFlags = flags ? Object.keys(flags).filter((name) => name != '_') : undefined;
|
||||
|
||||
const payload: EventPayload = {
|
||||
cliCommand,
|
||||
configKeys: userConfig ? configKeys(userConfig, '') : undefined,
|
||||
config: configValues,
|
||||
config: createAnonymousConfigInfo(userConfig),
|
||||
flags: cliFlags,
|
||||
};
|
||||
return [{ eventName: EVENT_SESSION, payload }];
|
||||
|
|
|
@ -5,44 +5,8 @@ import * as events from '../dist/events/index.js';
|
|||
|
||||
describe('Events', () => {
|
||||
describe('eventCliSession()', () => {
|
||||
it('All top-level keys added', () => {
|
||||
const config = {
|
||||
root: 1,
|
||||
srcDir: 2,
|
||||
publicDir: 3,
|
||||
outDir: 4,
|
||||
site: 5,
|
||||
base: 6,
|
||||
trailingSlash: 7,
|
||||
experimental: 8,
|
||||
};
|
||||
const expected = Object.keys(config);
|
||||
const [{ payload }] = events.eventCliSession(
|
||||
{
|
||||
cliCommand: 'dev',
|
||||
},
|
||||
config
|
||||
);
|
||||
expect(payload.configKeys).to.deep.equal(expected);
|
||||
});
|
||||
|
||||
it('configKeys includes format', () => {
|
||||
const config = {
|
||||
srcDir: 1,
|
||||
build: {
|
||||
format: 'file',
|
||||
},
|
||||
};
|
||||
const [{ payload }] = events.eventCliSession(
|
||||
{
|
||||
cliCommand: 'dev',
|
||||
},
|
||||
config
|
||||
);
|
||||
expect(payload.configKeys).to.deep.equal(['srcDir', 'build', 'build.format']);
|
||||
});
|
||||
|
||||
it('config.build.format', () => {
|
||||
it('string literal "build.format" is included', () => {
|
||||
const config = {
|
||||
srcDir: 1,
|
||||
build: {
|
||||
|
@ -58,59 +22,8 @@ describe('Events', () => {
|
|||
expect(payload.config.build.format).to.equal('file');
|
||||
});
|
||||
|
||||
it('configKeys includes server props', () => {
|
||||
const config = {
|
||||
srcDir: 1,
|
||||
server: {
|
||||
host: 'example.com',
|
||||
port: 8033,
|
||||
},
|
||||
};
|
||||
const [{ payload }] = events.eventCliSession(
|
||||
{
|
||||
cliCommand: 'dev',
|
||||
},
|
||||
config
|
||||
);
|
||||
expect(payload.configKeys).to.deep.equal(['srcDir', 'server', 'server.host', 'server.port']);
|
||||
});
|
||||
|
||||
it('configKeys is deep', () => {
|
||||
const config = {
|
||||
publicDir: 1,
|
||||
markdown: {
|
||||
drafts: true,
|
||||
shikiConfig: {
|
||||
lang: 1,
|
||||
theme: 2,
|
||||
wrap: 3,
|
||||
},
|
||||
syntaxHighlight: 'shiki',
|
||||
remarkPlugins: [],
|
||||
rehypePlugins: [],
|
||||
},
|
||||
};
|
||||
const [{ payload }] = events.eventCliSession(
|
||||
{
|
||||
cliCommand: 'dev',
|
||||
},
|
||||
config
|
||||
);
|
||||
expect(payload.configKeys).to.deep.equal([
|
||||
'publicDir',
|
||||
'markdown',
|
||||
'markdown.drafts',
|
||||
'markdown.shikiConfig',
|
||||
'markdown.shikiConfig.lang',
|
||||
'markdown.shikiConfig.theme',
|
||||
'markdown.shikiConfig.wrap',
|
||||
'markdown.syntaxHighlight',
|
||||
'markdown.remarkPlugins',
|
||||
'markdown.rehypePlugins',
|
||||
]);
|
||||
});
|
||||
|
||||
it('syntaxHighlight', () => {
|
||||
it('string literal "markdown.syntaxHighlight" is included', () => {
|
||||
const config = {
|
||||
markdown: {
|
||||
syntaxHighlight: 'shiki',
|
||||
|
@ -145,233 +58,16 @@ describe('Events', () => {
|
|||
},
|
||||
config
|
||||
);
|
||||
expect(payload.configKeys).is.deep.equal([
|
||||
'root',
|
||||
'vite',
|
||||
'vite.css',
|
||||
'vite.css.modules',
|
||||
'vite.base',
|
||||
'vite.mode',
|
||||
'vite.define',
|
||||
'vite.publicDir',
|
||||
expect(Object.keys(payload.config.vite)).is.deep.equal([
|
||||
'css',
|
||||
'base',
|
||||
'mode',
|
||||
'define',
|
||||
'publicDir',
|
||||
]);
|
||||
});
|
||||
|
||||
it('vite.resolve keys are captured', async () => {
|
||||
const config = {
|
||||
vite: {
|
||||
resolve: {
|
||||
alias: {
|
||||
a: 'b',
|
||||
},
|
||||
dedupe: ['one', 'two'],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const [{ payload }] = events.eventCliSession(
|
||||
{
|
||||
cliCommand: 'dev',
|
||||
},
|
||||
config
|
||||
);
|
||||
expect(payload.configKeys).is.deep.equal([
|
||||
'vite',
|
||||
'vite.resolve',
|
||||
'vite.resolve.alias',
|
||||
'vite.resolve.dedupe',
|
||||
]);
|
||||
});
|
||||
|
||||
it('vite.css keys are captured', async () => {
|
||||
const config = {
|
||||
vite: {
|
||||
resolve: {
|
||||
dedupe: ['one', 'two'],
|
||||
},
|
||||
css: {
|
||||
modules: [],
|
||||
postcss: {},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const [{ payload }] = events.eventCliSession(
|
||||
{
|
||||
cliCommand: 'dev',
|
||||
},
|
||||
config
|
||||
);
|
||||
expect(payload.configKeys).is.deep.equal([
|
||||
'vite',
|
||||
'vite.resolve',
|
||||
'vite.resolve.dedupe',
|
||||
'vite.css',
|
||||
'vite.css.modules',
|
||||
'vite.css.postcss',
|
||||
]);
|
||||
});
|
||||
|
||||
it('vite.server keys are captured', async () => {
|
||||
const config = {
|
||||
vite: {
|
||||
server: {
|
||||
host: 'example.com',
|
||||
open: true,
|
||||
fs: {
|
||||
strict: true,
|
||||
allow: ['a', 'b'],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const [{ payload }] = events.eventCliSession(
|
||||
{
|
||||
cliCommand: 'dev',
|
||||
},
|
||||
config
|
||||
);
|
||||
expect(payload.configKeys).is.deep.equal([
|
||||
'vite',
|
||||
'vite.server',
|
||||
'vite.server.host',
|
||||
'vite.server.open',
|
||||
'vite.server.fs',
|
||||
'vite.server.fs.strict',
|
||||
'vite.server.fs.allow',
|
||||
]);
|
||||
});
|
||||
|
||||
it('vite.build keys are captured', async () => {
|
||||
const config = {
|
||||
vite: {
|
||||
build: {
|
||||
target: 'one',
|
||||
outDir: 'some/dir',
|
||||
cssTarget: {
|
||||
one: 'two',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const [{ payload }] = events.eventCliSession(
|
||||
{
|
||||
cliCommand: 'dev',
|
||||
},
|
||||
config
|
||||
);
|
||||
expect(payload.configKeys).is.deep.equal([
|
||||
'vite',
|
||||
'vite.build',
|
||||
'vite.build.target',
|
||||
'vite.build.outDir',
|
||||
'vite.build.cssTarget',
|
||||
]);
|
||||
});
|
||||
|
||||
it('vite.preview keys are captured', async () => {
|
||||
const config = {
|
||||
vite: {
|
||||
preview: {
|
||||
host: 'example.com',
|
||||
port: 8080,
|
||||
another: {
|
||||
a: 'b',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const [{ payload }] = events.eventCliSession(
|
||||
{
|
||||
cliCommand: 'dev',
|
||||
},
|
||||
config
|
||||
);
|
||||
expect(payload.configKeys).is.deep.equal([
|
||||
'vite',
|
||||
'vite.preview',
|
||||
'vite.preview.host',
|
||||
'vite.preview.port',
|
||||
'vite.preview.another',
|
||||
]);
|
||||
});
|
||||
|
||||
it('vite.optimizeDeps keys are captured', async () => {
|
||||
const config = {
|
||||
vite: {
|
||||
optimizeDeps: {
|
||||
entries: ['one', 'two'],
|
||||
exclude: ['secret', 'name'],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const [{ payload }] = events.eventCliSession(
|
||||
{
|
||||
cliCommand: 'dev',
|
||||
},
|
||||
config
|
||||
);
|
||||
expect(payload.configKeys).is.deep.equal([
|
||||
'vite',
|
||||
'vite.optimizeDeps',
|
||||
'vite.optimizeDeps.entries',
|
||||
'vite.optimizeDeps.exclude',
|
||||
]);
|
||||
});
|
||||
|
||||
it('vite.ssr keys are captured', async () => {
|
||||
const config = {
|
||||
vite: {
|
||||
ssr: {
|
||||
external: ['a'],
|
||||
target: { one: 'two' },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const [{ payload }] = events.eventCliSession(
|
||||
{
|
||||
cliCommand: 'dev',
|
||||
},
|
||||
config
|
||||
);
|
||||
expect(payload.configKeys).is.deep.equal([
|
||||
'vite',
|
||||
'vite.ssr',
|
||||
'vite.ssr.external',
|
||||
'vite.ssr.target',
|
||||
]);
|
||||
});
|
||||
|
||||
it('vite.worker keys are captured', async () => {
|
||||
const config = {
|
||||
vite: {
|
||||
worker: {
|
||||
format: { a: 'b' },
|
||||
plugins: ['a', 'b'],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const [{ payload }] = events.eventCliSession(
|
||||
{
|
||||
cliCommand: 'dev',
|
||||
},
|
||||
config
|
||||
);
|
||||
expect(payload.configKeys).is.deep.equal([
|
||||
'vite',
|
||||
'vite.worker',
|
||||
'vite.worker.format',
|
||||
'vite.worker.plugins',
|
||||
]);
|
||||
});
|
||||
|
||||
it('falsy integrations', () => {
|
||||
it('falsy integrations are handled', () => {
|
||||
const config = {
|
||||
srcDir: 1,
|
||||
integrations: [null, undefined, false],
|
||||
|
@ -385,7 +81,7 @@ describe('Events', () => {
|
|||
expect(payload.config.integrations.length).to.equal(0);
|
||||
});
|
||||
|
||||
it('finds names for integration arrays', () => {
|
||||
it('only integration names are included', () => {
|
||||
const config = {
|
||||
integrations: [{ name: 'foo' }, [{ name: 'bar' }, { name: 'baz' }]],
|
||||
};
|
||||
|
@ -393,6 +89,14 @@ describe('Events', () => {
|
|||
expect(payload.config.integrations).to.deep.equal(['foo', 'bar', 'baz']);
|
||||
});
|
||||
|
||||
it('only adapter name is included', () => {
|
||||
const config = {
|
||||
adapter: {name: 'ADAPTER_NAME'},
|
||||
};
|
||||
const [{ payload }] = events.eventCliSession({ cliCommand: 'dev' }, config);
|
||||
expect(payload.config.adapter).to.equal('ADAPTER_NAME');
|
||||
});
|
||||
|
||||
it('includes cli flags in payload', () => {
|
||||
const config = {};
|
||||
const flags = {
|
||||
|
|
|
@ -1,9 +1,17 @@
|
|||
# Astro Telemetry
|
||||
|
||||
This package is used to collect anonymous telemetry data within the Astro CLI. It is enabled by default. Telemetry data does not contain any personal identifying information and can be disabled via:
|
||||
This package is used to collect anonymous telemetry data within the Astro CLI.
|
||||
|
||||
It can be disabled in Astro using either method documented below:
|
||||
|
||||
```shell
|
||||
# Option 1: Run this to disable telemetry globally across your entire machine.
|
||||
astro telemetry disable
|
||||
```
|
||||
|
||||
See the [CLI documentation](https://docs.astro.build/en/reference/cli-reference/#astro-telemetry) for more options on configuration telemetry.
|
||||
```shell
|
||||
# Option 2: The ASTRO_TELEMETRY_DISABLED environment variable disables telemetry when set.
|
||||
ASTRO_TELEMETRY_DISABLED=1 astro dev
|
||||
```
|
||||
|
||||
Visit https://astro.build/telemetry/ for more information about our approach to anonymous telemetry in Astro.
|
||||
|
|
Loading…
Reference in a new issue