0
Fork 0
mirror of https://github.com/withastro/astro.git synced 2024-12-30 22:03:56 -05:00

Add new preferences module (#9115)

This commit is contained in:
Nate Moore 2023-11-29 12:43:40 -06:00 committed by GitHub
parent 34e96b141a
commit 3b77889b47
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 507 additions and 4 deletions

View file

@ -0,0 +1,17 @@
---
'astro': minor
---
Adds the `astro preferences` command to manage user preferences. User preferences are specific to individual Astro users, unlike the `astro.config.mjs` file which changes behavior for everyone working on a project.
User preferences are scoped to the current project by default, stored in a local `.astro/settings.json` file. Using the `--global` flag, user preferences can also be applied to every Astro project on the current machine. Global user preferences are stored in an operating system-specific location.
```sh
# Disable the dev overlay for the current user in the current project
npm run astro preferences disable devOverlay
# Disable the dev overlay for the current user in all Astro projects on this machine
npm run astro preferences --global disable devOverlay
# Check if the dev overlay is enabled for the current user
npm run astro preferences list devOverlay
```

View file

@ -134,11 +134,14 @@
"deterministic-object-hash": "^2.0.1", "deterministic-object-hash": "^2.0.1",
"devalue": "^4.3.2", "devalue": "^4.3.2",
"diff": "^5.1.0", "diff": "^5.1.0",
"dlv": "^1.1.3",
"dset": "^3.1.3",
"es-module-lexer": "^1.4.1", "es-module-lexer": "^1.4.1",
"esbuild": "^0.19.6", "esbuild": "^0.19.6",
"estree-walker": "^3.0.3", "estree-walker": "^3.0.3",
"execa": "^8.0.1", "execa": "^8.0.1",
"fast-glob": "^3.3.2", "fast-glob": "^3.3.2",
"flattie": "^1.1.0",
"github-slugger": "^2.0.0", "github-slugger": "^2.0.0",
"gray-matter": "^4.0.3", "gray-matter": "^4.0.3",
"html-escaper": "^3.0.3", "html-escaper": "^3.0.3",
@ -185,6 +188,7 @@
"@types/cookie": "^0.5.4", "@types/cookie": "^0.5.4",
"@types/debug": "^4.1.12", "@types/debug": "^4.1.12",
"@types/diff": "^5.0.8", "@types/diff": "^5.0.8",
"@types/dlv": "^1.1.4",
"@types/dom-view-transitions": "^1.0.4", "@types/dom-view-transitions": "^1.0.4",
"@types/estree": "^1.0.5", "@types/estree": "^1.0.5",
"@types/hast": "^3.0.3", "@types/hast": "^3.0.3",

View file

@ -35,6 +35,7 @@ import type {
import type { AstroComponentFactory, AstroComponentInstance } from '../runtime/server/index.js'; import type { AstroComponentFactory, AstroComponentInstance } from '../runtime/server/index.js';
import type { OmitIndexSignature, Simplify } from '../type-utils.js'; import type { OmitIndexSignature, Simplify } from '../type-utils.js';
import type { SUPPORTED_MARKDOWN_FILE_EXTENSIONS } from './../core/constants.js'; import type { SUPPORTED_MARKDOWN_FILE_EXTENSIONS } from './../core/constants.js';
import type { AstroPreferences } from '../preferences/index.js';
export { type AstroIntegrationLogger }; export { type AstroIntegrationLogger };
@ -1678,6 +1679,7 @@ export interface AstroAdapterFeatures {
export interface AstroSettings { export interface AstroSettings {
config: AstroConfig; config: AstroConfig;
adapter: AstroAdapter | undefined; adapter: AstroAdapter | undefined;
preferences: AstroPreferences;
injectedRoutes: InjectedRoute[]; injectedRoutes: InjectedRoute[];
resolvedInjectedRoutes: ResolvedInjectedRoute[]; resolvedInjectedRoutes: ResolvedInjectedRoute[];
pageExtensions: string[]; pageExtensions: string[];

View file

@ -14,6 +14,7 @@ type CLICommand =
| 'sync' | 'sync'
| 'check' | 'check'
| 'info' | 'info'
| 'preferences'
| 'telemetry'; | 'telemetry';
/** Display --help flag */ /** Display --help flag */
@ -33,6 +34,7 @@ async function printAstroHelp() {
['info', 'List info about your current Astro setup.'], ['info', 'List info about your current Astro setup.'],
['preview', 'Preview your build locally.'], ['preview', 'Preview your build locally.'],
['sync', 'Generate content collection types.'], ['sync', 'Generate content collection types.'],
['preferences', 'Configure user preferences.'],
['telemetry', 'Configure telemetry settings.'], ['telemetry', 'Configure telemetry settings.'],
], ],
'Global Flags': [ 'Global Flags': [
@ -64,6 +66,7 @@ function resolveCommand(flags: yargs.Arguments): CLICommand {
'add', 'add',
'sync', 'sync',
'telemetry', 'telemetry',
'preferences',
'dev', 'dev',
'build', 'build',
'preview', 'preview',
@ -114,6 +117,12 @@ async function runCommand(cmd: string, flags: yargs.Arguments) {
const exitCode = await sync({ flags }); const exitCode = await sync({ flags });
return process.exit(exitCode); return process.exit(exitCode);
} }
case 'preferences': {
const { preferences } = await import('./preferences/index.js');
const [subcommand, key, value] = flags._.slice(3).map(v => v.toString());
const exitCode = await preferences(subcommand, key, value, { flags });
return process.exit(exitCode);
}
} }
// In verbose/debug mode, we log the debug logs asap before any potential errors could appear // In verbose/debug mode, we log the debug logs asap before any potential errors could appear
@ -177,7 +186,7 @@ async function runCommand(cmd: string, flags: yargs.Arguments) {
/** The primary CLI action */ /** The primary CLI action */
export async function cli(args: string[]) { export async function cli(args: string[]) {
const flags = yargs(args); const flags = yargs(args, { boolean: ['global'], alias: { g: 'global' } });
const cmd = resolveCommand(flags); const cmd = resolveCommand(flags);
try { try {
await runCommand(cmd, flags); await runCommand(cmd, flags);

View file

@ -0,0 +1,227 @@
/* eslint-disable no-console */
import type yargs from 'yargs-parser';
import type { AstroSettings } from '../../@types/astro.js';
import { bold } from 'kleur/colors';
import { fileURLToPath } from 'node:url';
import * as msg from '../../core/messages.js';
import { createLoggerFromFlags, flagsToAstroInlineConfig } from '../flags.js';
import { resolveConfig } from '../../core/config/config.js';
import { createSettings } from '../../core/config/settings.js';
import { coerce, isValidKey, type PreferenceKey } from '../../preferences/index.js';
import { DEFAULT_PREFERENCES } from '../../preferences/defaults.js';
import dlv from 'dlv';
// @ts-expect-error flattie types are mispackaged
import { flattie } from 'flattie';
import { formatWithOptions } from 'node:util';
import { collectErrorMetadata } from '../../core/errors/dev/utils.js';
interface PreferencesOptions {
flags: yargs.Arguments;
}
const PREFERENCES_SUBCOMMANDS = ['get', 'set', 'enable', 'disable', 'delete', 'reset', 'list'] as const;
export type Subcommand = typeof PREFERENCES_SUBCOMMANDS[number];
function isValidSubcommand(subcommand: string): subcommand is Subcommand {
return PREFERENCES_SUBCOMMANDS.includes(subcommand as Subcommand);
}
export async function preferences(subcommand: string, key: string, value: string | undefined, { flags }: PreferencesOptions): Promise<number> {
if (!isValidSubcommand(subcommand) || flags?.help || flags?.h) {
msg.printHelp({
commandName: 'astro preferences',
usage: '[command]',
tables: {
Commands: [
['list', 'Pretty print all current preferences'],
['list --json', 'Log all current preferences as a JSON object'],
['get [key]', 'Log current preference value'],
['set [key] [value]', 'Update preference value'],
['reset [key]', 'Reset preference value to default'],
['enable [key]', 'Set a boolean preference to true'],
['disable [key]', 'Set a boolean preference to false'],
],
Flags: [
['--global', 'Scope command to global preferences (all Astro projects) rather than the current project'],
],
},
});
return 0;
}
const inlineConfig = flagsToAstroInlineConfig(flags);
const logger = createLoggerFromFlags(flags);
const { astroConfig } = await resolveConfig(inlineConfig ?? {}, 'dev');
const settings = await createSettings(astroConfig, fileURLToPath(astroConfig.root));
const opts: SubcommandOptions = {
location: flags.global ? 'global' : undefined,
json: flags.json
}
if (subcommand === 'list') {
return listPreferences(settings, opts);
}
if (subcommand === 'enable' || subcommand === 'disable') {
key = `${key}.enabled` as PreferenceKey;
}
if (!isValidKey(key)) {
logger.error('preferences', `Unknown preference "${key}"\n`);
return 1;
}
if (subcommand === 'set' && value === undefined) {
const type = typeof dlv(DEFAULT_PREFERENCES, key);
console.error(msg.formatErrorMessage(collectErrorMetadata(new Error(`Please provide a ${type} value for "${key}"`)), true));
return 1;
}
switch (subcommand) {
case 'get': return getPreference(settings, key, opts);
case 'set': return setPreference(settings, key, value, opts);
case 'reset':
case 'delete': return resetPreference(settings, key, opts);
case 'enable': return enablePreference(settings, key, opts);
case 'disable': return disablePreference(settings, key, opts);
}
}
interface SubcommandOptions {
location?: 'global' | 'project';
json?: boolean;
}
// Default `location` to "project" to avoid reading default preferencesa
async function getPreference(settings: AstroSettings, key: PreferenceKey, { location = 'project' }: SubcommandOptions) {
try {
let value = await settings.preferences.get(key, { location });
if (value && typeof value === 'object' && !Array.isArray(value)) {
if (Object.keys(value).length === 0) {
value = dlv(DEFAULT_PREFERENCES, key);
console.log(msg.preferenceDefaultIntro(key));
}
prettyPrint({ [key]: value });
return 0;
}
if (value === undefined) {
const defaultValue = await settings.preferences.get(key);
console.log(msg.preferenceDefault(key, defaultValue));
return 0;
}
console.log(msg.preferenceGet(key, value));
return 0;
} catch {}
return 1;
}
async function setPreference(settings: AstroSettings, key: PreferenceKey, value: unknown, { location }: SubcommandOptions) {
try {
const defaultType = typeof dlv(DEFAULT_PREFERENCES, key);
if (typeof coerce(key, value) !== defaultType) {
throw new Error(`${key} expects a "${defaultType}" value!`)
}
await settings.preferences.set(key, coerce(key, value), { location });
console.log(msg.preferenceSet(key, value))
return 0;
} catch (e) {
if (e instanceof Error) {
console.error(msg.formatErrorMessage(collectErrorMetadata(e), true));
return 1;
}
throw e;
}
}
async function enablePreference(settings: AstroSettings, key: PreferenceKey, { location }: SubcommandOptions) {
try {
await settings.preferences.set(key, true, { location });
console.log(msg.preferenceEnabled(key.replace('.enabled', '')))
return 0;
} catch {}
return 1;
}
async function disablePreference(settings: AstroSettings, key: PreferenceKey, { location }: SubcommandOptions) {
try {
await settings.preferences.set(key, false, { location });
console.log(msg.preferenceDisabled(key.replace('.enabled', '')))
return 0;
} catch {}
return 1;
}
async function resetPreference(settings: AstroSettings, key: PreferenceKey, { location }: SubcommandOptions) {
try {
await settings.preferences.set(key, undefined as any, { location });
console.log(msg.preferenceReset(key))
return 0;
} catch {}
return 1;
}
async function listPreferences(settings: AstroSettings, { location, json }: SubcommandOptions) {
const store = await settings.preferences.getAll({ location });
if (json) {
console.log(JSON.stringify(store, null, 2));
return 0;
}
prettyPrint(store);
return 0;
}
function prettyPrint(value: Record<string, string | number | boolean>) {
const flattened = flattie(value);
const table = formatTable(flattened, ['Preference', 'Value']);
console.log(table);
}
const chars = {
h: '─',
hThick: '━',
hThickCross: '┿',
v: '│',
vRight: '├',
vRightThick: '┝',
vLeft: '┤',
vLeftThick: '┥',
hTop: '┴',
hBottom: '┬',
topLeft: '╭',
topRight: '╮',
bottomLeft: '╰',
bottomRight: '╯',
}
function formatTable(object: Record<string, string | number | boolean>, columnLabels: [string, string]) {
const [colA, colB] = columnLabels;
const colALength = [colA, ...Object.keys(object)].reduce(longest, 0) + 3;
const colBLength = [colB, ...Object.values(object)].reduce(longest, 0) + 3;
function formatRow(i: number, a: string, b: string | number | boolean, style: (value: string | number | boolean) => string = (v) => v.toString()): string {
return `${chars.v} ${style(a)} ${space(colALength - a.length - 2)} ${chars.v} ${style(b)} ${space(colBLength - b.toString().length - 3)} ${chars.v}`
}
const top = `${chars.topLeft}${chars.h.repeat(colALength + 1)}${chars.hBottom}${chars.h.repeat(colBLength)}${chars.topRight}`
const bottom = `${chars.bottomLeft}${chars.h.repeat(colALength + 1)}${chars.hTop}${chars.h.repeat(colBLength)}${chars.bottomRight}`
const divider = `${chars.vRightThick}${chars.hThick.repeat(colALength + 1)}${chars.hThickCross}${chars.hThick.repeat(colBLength)}${chars.vLeftThick}`
const rows: string[] = [top, formatRow(-1, colA, colB, bold), divider];
let i = 0;
for (const [key, value] of Object.entries(object)) {
rows.push(formatRow(i, key, value, (v) => formatWithOptions({ colors: true }, v)));
i++;
}
rows.push(bottom);
return rows.join('\n');
}
function space(len: number) {
return ' '.repeat(len);
}
const longest = (a: number, b: string | number | boolean) => {
const { length: len } = b.toString();
return a > len ? a : len;
};

View file

@ -10,11 +10,14 @@ import { formatYAMLException, isYAMLException } from '../errors/utils.js';
import { SUPPORTED_MARKDOWN_FILE_EXTENSIONS } from './../constants.js'; import { SUPPORTED_MARKDOWN_FILE_EXTENSIONS } from './../constants.js';
import { AstroTimer } from './timer.js'; import { AstroTimer } from './timer.js';
import { loadTSConfig } from './tsconfig.js'; import { loadTSConfig } from './tsconfig.js';
import createPreferences from '../../preferences/index.js';
export function createBaseSettings(config: AstroConfig): AstroSettings { export function createBaseSettings(config: AstroConfig): AstroSettings {
const { contentDir } = getContentPaths(config); const { contentDir } = getContentPaths(config);
const preferences = createPreferences(config);
return { return {
config, config,
preferences,
tsConfig: undefined, tsConfig: undefined,
tsConfigPath: undefined, tsConfigPath: undefined,
adapter: undefined, adapter: undefined,

View file

@ -29,6 +29,9 @@ async function createRestartedContainer(
return newContainer; return newContainer;
} }
const configRE = new RegExp(`.*astro\.config\.((mjs)|(cjs)|(js)|(ts))$`);
const preferencesRE = new RegExp(`.*\.astro\/settings\.json$`);
export function shouldRestartContainer( export function shouldRestartContainer(
{ settings, inlineConfig, restartInFlight }: Container, { settings, inlineConfig, restartInFlight }: Container,
changedFile: string changedFile: string
@ -43,9 +46,9 @@ export function shouldRestartContainer(
} }
// Otherwise, watch for any astro.config.* file changes in project root // Otherwise, watch for any astro.config.* file changes in project root
else { else {
const exp = new RegExp(`.*astro\.config\.((mjs)|(cjs)|(js)|(ts))$`);
const normalizedChangedFile = vite.normalizePath(changedFile); const normalizedChangedFile = vite.normalizePath(changedFile);
shouldRestart = exp.test(normalizedChangedFile); shouldRestart = configRE.test(normalizedChangedFile) || preferencesRE.test(normalizedChangedFile);
} }
if (!shouldRestart && settings.watchFiles.length > 0) { if (!shouldRestart && settings.watchFiles.length > 0) {

View file

@ -25,6 +25,7 @@ export type LoggerLabel =
| 'vite' | 'vite'
| 'watch' | 'watch'
| 'middleware' | 'middleware'
| 'preferences'
// SKIP_FORMAT: A special label that tells the logger not to apply any formatting. // SKIP_FORMAT: A special label that tells the logger not to apply any formatting.
// Useful for messages that are already formatted, like the server start message. // Useful for messages that are already formatted, like the server start message.
| 'SKIP_FORMAT'; | 'SKIP_FORMAT';

View file

@ -3,6 +3,7 @@ import {
bgRed, bgRed,
bgWhite, bgWhite,
bgYellow, bgYellow,
bgCyan,
black, black,
blue, blue,
bold, bold,
@ -110,6 +111,34 @@ export function telemetryEnabled() {
].join('\n'); ].join('\n');
} }
export function preferenceEnabled(name: string) {
return `${green('◉')} ${name} is now ${bgGreen(black(' enabled '))}\n`;
}
export function preferenceSet(name: string, value: any) {
return `${green('◉')} ${name} has been set to ${bgGreen(black(` ${JSON.stringify(value)} `))}\n`;
}
export function preferenceGet(name: string, value: any) {
return `${green('◉')} ${name} is set to ${bgGreen(black(` ${JSON.stringify(value)} `))}\n`;
}
export function preferenceDefaultIntro(name: string) {
return `${yellow('◯')} ${name} has not been set. It defaults to\n`;
}
export function preferenceDefault(name: string, value: any) {
return `${yellow('◯')} ${name} has not been set. It defaults to ${bgYellow(black(` ${JSON.stringify(value)} `))}\n`;
}
export function preferenceDisabled(name: string) {
return `${yellow('◯')} ${name} is now ${bgYellow(black(' disabled '))}\n`;
}
export function preferenceReset(name: string) {
return `${cyan('◆')} ${name} has been ${bgCyan(black(' reset '))}\n`;
}
export function telemetryDisabled() { export function telemetryDisabled() {
return [ return [
green('▶ Anonymous telemetry ') + bgGreen(' disabled '), green('▶ Anonymous telemetry ') + bgGreen(' disabled '),

View file

@ -0,0 +1,33 @@
# Preferences
The preferences module implements global and local user preferences for controlling certain Astro behavior. Whereas the `astro.config.mjs` file controls project-specific behavior for every user of a project, preferences are user-specific.
The design of Preferences is inspired by [Git](https://git-scm.com/book/en/v2/Customizing-Git-Git-Configuration) and [Visual Studio Code](https://code.visualstudio.com/docs/getstarted/settings). Both systems implement similar layering approaches with project-specific and global settings.
## `AstroPreferences`
The `AstroPreferences` interface exposes both a `get` and `set` function.
### Reading a preference
`preferences.get("dot.separated.value")` will read a preference value from multiple sources if needed. Local project preferences are read from `.astro/settings.json`, if it exists. Next, global user preferences are read from `<homedir>/<os-specific-preferences-dir>/astro/settings.json`. If neither of those are found, the default preferences defined in [`./defaults.ts`](./defaults.ts) will apply.
In order to read a preference from a specific location, you can pass the `location: "global" | "project"` option.
```js
await preferences.get('dot.separated.value', { location: 'global' });
```
### Writing a preference
`preferences.set("dot.separated.value", true)` will store a preference value. By default, preferences are stored locally in a project.
In order to set a global user preference, you can pass the `location: "global"` option.
```js
await preferences.set('dot.separated.value', 'value', { location: 'global' });
```
## Relation to Telemetry
This module evolved from the existing `@astrojs/telemetry` package, but has been generalized for user-facing `astro` preferences. At some point, we'll need to merge the logic in `@astrojs/telemetry` and the logic in this module so that all preferences are stored in the same location.

View file

@ -0,0 +1,8 @@
export const DEFAULT_PREFERENCES = {
devOverlay: {
/** Specifies whether the user has the Dev Overlay enabled */
enabled: true,
},
}
export type Preferences = typeof DEFAULT_PREFERENCES;

View file

@ -0,0 +1,91 @@
import type { AstroConfig } from '../@types/astro.js';
import { fileURLToPath } from 'node:url';
import os from 'node:os';
import process from 'node:process';
import path from 'node:path';
import dget from 'dlv';
import { DEFAULT_PREFERENCES, type Preferences } from './defaults.js';
import { PreferenceStore } from './store.js';
type DotKeys<T> = T extends object ? { [K in keyof T]:
`${Exclude<K, symbol>}${DotKeys<T[K]> extends never ? "" : `.${DotKeys<T[K]>}`}`
}[keyof T] : never
export type GetDotKey<
T extends Record<string | number, any>,
K extends string
> = K extends `${infer U}.${infer Rest}` ? GetDotKey<T[U], Rest> : T[K]
export interface PreferenceOptions {
location?: 'global' | 'project';
}
export type PreferenceKey = DotKeys<Preferences>;
export interface AstroPreferences {
get<Key extends PreferenceKey>(key: Key, opts?: PreferenceOptions): Promise<GetDotKey<Preferences, Key>>;
set<Key extends PreferenceKey>(key: Key, value: GetDotKey<Preferences, Key>, opts?: PreferenceOptions): Promise<void>;
getAll(opts?: PreferenceOptions): Promise<Record<string, any>>;
}
export function isValidKey(key: string): key is PreferenceKey {
return dget(DEFAULT_PREFERENCES, key) !== undefined;
}
export function coerce(key: string, value: unknown) {
const type = typeof dget(DEFAULT_PREFERENCES, key);
switch (type) {
case 'string': return value;
case 'number': return Number(value);
case 'boolean': {
if (value === 'true' || value === 1) return true;
if (value === 'false' || value === 0) return false;
}
}
return value as any;
}
export default function createPreferences(config: AstroConfig): AstroPreferences {
const global = new PreferenceStore(getGlobalPreferenceDir());
const project = new PreferenceStore(fileURLToPath(new URL('./.astro/', config.root)));
const stores = { global, project };
return {
async get(key, { location } = {}) {
if (!location) return project.get(key) ?? global.get(key) ?? dget(DEFAULT_PREFERENCES, key);
return stores[location].get(key);
},
async set(key, value, { location = 'project' } = {}) {
stores[location].set(key, value);
},
async getAll({ location } = {}) {
if (!location) return Object.assign({}, stores['global'].getAll(), stores['project'].getAll());
return stores[location].getAll();
},
}
}
// Adapted from https://github.com/sindresorhus/env-paths
export function getGlobalPreferenceDir() {
const name = 'astro';
const homedir = os.homedir();
const macos = () => path.join(homedir, 'Library', 'Preferences', name);
const win = () => {
const { APPDATA = path.join(homedir, 'AppData', 'Roaming') } = process.env;
return path.join(APPDATA, name, 'Config');
};
const linux = () => {
const { XDG_CONFIG_HOME = path.join(homedir, '.config') } = process.env;
return path.join(XDG_CONFIG_HOME, name);
};
switch (process.platform) {
case 'darwin':
return macos();
case 'win32':
return win();
default:
return linux();
}
}

View file

@ -0,0 +1,59 @@
import dget from 'dlv';
import { dset } from 'dset';
import fs from 'node:fs';
import path from 'node:path';
export class PreferenceStore {
private file: string;
constructor(private dir: string, filename = 'settings.json') {
this.file = path.join(this.dir, filename);
}
private _store?: Record<string, any>;
private get store(): Record<string, any> {
if (this._store) return this._store;
if (fs.existsSync(this.file)) {
try {
this._store = JSON.parse(fs.readFileSync(this.file).toString());
} catch {}
}
if (!this._store) {
this._store = {};
this.write();
}
return this._store;
}
private set store(value: Record<string, any>) {
this._store = value;
this.write();
}
write() {
if (!this._store || Object.keys(this._store).length === 0) return;
fs.mkdirSync(this.dir, { recursive: true });
fs.writeFileSync(this.file, JSON.stringify(this.store, null, '\t'));
}
clear(): void {
this.store = {};
fs.rmSync(this.file, { recursive: true });
}
delete(key: string): boolean {
dset(this.store, key, undefined);
this.write();
return true;
}
get(key: string): any {
return dget(this.store, key);
}
has(key: string): boolean {
return typeof this.get(key) !== 'undefined';
}
set(key: string, value: any): void {
if (this.get(key) === value) return;
dset(this.store, key, value);
this.write();
}
getAll(): Record<string, any> {
return this.store;
}
}

View file

@ -384,7 +384,7 @@ async function getScriptsAndStyles({ pipeline, filePath }: GetScriptsAndStylesPa
children: '', children: '',
}); });
if (settings.config.devOverlay.enabled) { if (settings.config.devOverlay.enabled && await settings.preferences.get('devOverlay.enabled')) {
scripts.add({ scripts.add({
props: { props: {
type: 'module', type: 'module',

View file

@ -550,6 +550,12 @@ importers:
diff: diff:
specifier: ^5.1.0 specifier: ^5.1.0
version: 5.1.0 version: 5.1.0
dlv:
specifier: ^1.1.3
version: 1.1.3
dset:
specifier: ^3.1.3
version: 3.1.3
es-module-lexer: es-module-lexer:
specifier: ^1.4.1 specifier: ^1.4.1
version: 1.4.1 version: 1.4.1
@ -565,6 +571,9 @@ importers:
fast-glob: fast-glob:
specifier: ^3.3.2 specifier: ^3.3.2
version: 3.3.2 version: 3.3.2
flattie:
specifier: ^1.1.0
version: 1.1.0
github-slugger: github-slugger:
specifier: ^2.0.0 specifier: ^2.0.0
version: 2.0.0 version: 2.0.0
@ -693,6 +702,9 @@ importers:
'@types/diff': '@types/diff':
specifier: ^5.0.8 specifier: ^5.0.8
version: 5.0.8 version: 5.0.8
'@types/dlv':
specifier: ^1.1.4
version: 1.1.4
'@types/dom-view-transitions': '@types/dom-view-transitions':
specifier: ^1.0.4 specifier: ^1.0.4
version: 1.0.4 version: 1.0.4
@ -12217,6 +12229,11 @@ packages:
resolution: {integrity: sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==} resolution: {integrity: sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==}
dev: true dev: true
/flattie@1.1.0:
resolution: {integrity: sha512-xU99gDEnciIwJdGcBmNHnzTJ/w5AT+VFJOu6sTB6WM8diOYNA3Sa+K1DiEBQ7XH4QikQq3iFW1U+jRVcotQnBw==}
engines: {node: '>=8'}
dev: false
/for-each@0.3.3: /for-each@0.3.3:
resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
dependencies: dependencies: