From 57a2cedfdccab5d963851baf2ccf52da3010881c Mon Sep 17 00:00:00 2001 From: Hrishikesh Kokate Date: Thu, 21 May 2026 19:29:23 +0530 Subject: [PATCH 01/10] feat: add watch-ignore flag --- src/commands/dev/dev.ts | 1 + src/commands/dev/index.ts | 8 +++++++ src/commands/serve/serve.ts | 1 + src/lib/edge-functions/proxy.ts | 10 +++++++++ src/lib/edge-functions/registry.ts | 35 ++++++++++++++++++++++++++++-- src/utils/proxy-server.ts | 3 +++ src/utils/proxy.ts | 3 +++ 7 files changed, 59 insertions(+), 2 deletions(-) diff --git a/src/commands/dev/dev.ts b/src/commands/dev/dev.ts index f17b4ef5ba1..ad62ddd9407 100644 --- a/src/commands/dev/dev.ts +++ b/src/commands/dev/dev.ts @@ -319,6 +319,7 @@ export const dev = async (options: OptionValues, command: BaseCommand) => { accountId, functionsRegistry, repositoryRoot, + watchIgnore: options.watchIgnore ?? [], deployEnvironment, }) diff --git a/src/commands/dev/index.ts b/src/commands/dev/index.ts index 24a00228e6a..49643940d31 100644 --- a/src/commands/dev/index.ts +++ b/src/commands/dev/index.ts @@ -94,6 +94,12 @@ export const createDevCommand = (program: BaseCommand) => { .argParser(validateShortFlagArgs), ) .addOption(new Option('--skip-gitignore', 'skip adding .netlify to .gitignore file')) + .option( + '--watch-ignore ', + 'exclude a path from file watching. accepts a file path or a directory path; ignored directories exclude their entire subtree and cannot be partially re-included. can be specified multiple times.', + (value: string, previous: string[]) => [...previous, value], + [] as string[], + ) .addExamples([ 'netlify dev', 'netlify dev -d public', @@ -106,6 +112,8 @@ export const createDevCommand = (program: BaseCommand) => { 'netlify dev --edge-inspect-brk', 'netlify dev --edge-inspect-brk=127.0.0.1:9229', 'netlify dev --skip-gitignore # skip adding .netlify to .gitignore', + 'netlify dev --watch-ignore src/posts # exclude a content directory from file watching', + 'netlify dev --watch-ignore src/posts --watch-ignore content # exclude multiple directories', 'BROWSER=none netlify dev # disable browser auto opening', ]) .addHelpText('after', () => { diff --git a/src/commands/serve/serve.ts b/src/commands/serve/serve.ts index e52e88842cb..c65581eaaab 100644 --- a/src/commands/serve/serve.ts +++ b/src/commands/serve/serve.ts @@ -210,6 +210,7 @@ export const serve = async (options: OptionValues, command: BaseCommand) => { siteInfo, state, accountId, + watchIgnore: [], deployEnvironment: [], }) diff --git a/src/lib/edge-functions/proxy.ts b/src/lib/edge-functions/proxy.ts index 09225c0b5d9..97326def833 100644 --- a/src/lib/edge-functions/proxy.ts +++ b/src/lib/edge-functions/proxy.ts @@ -95,6 +95,7 @@ export const initializeProxy = async ({ settings, siteInfo, state, + watchIgnore, deployEnvironment, }: { accountId: string @@ -117,6 +118,7 @@ export const initializeProxy = async ({ settings: ServerSettings siteInfo: $TSFixMe state: LocalState + watchIgnore: string[] deployEnvironment: { key: string; value: string; isSecret: boolean; scopes: string[] }[] }) => { const isolatePort = await getAvailablePort() @@ -139,7 +141,9 @@ export const initializeProxy = async ({ inspectSettings, port: isolatePort, projectDir, + publishDir: settings.dist, repositoryRoot, + watchIgnore, deployEnvironment, }) return async (req: ExtendedIncomingMessage) => { @@ -214,7 +218,9 @@ const prepareServer = async ({ inspectSettings, port, projectDir, + publishDir, repositoryRoot, + watchIgnore, deployEnvironment, }: { aiGatewayContext?: AIGatewayContext | null @@ -228,7 +234,9 @@ const prepareServer = async ({ inspectSettings: Parameters[0]['inspectSettings'] port: number projectDir: string + publishDir: string repositoryRoot?: string + watchIgnore: string[] deployEnvironment: { key: string; value: string; isSecret: boolean; scopes: string[] }[] }) => { try { @@ -267,8 +275,10 @@ const prepareServer = async ({ getUpdatedConfig, importMapFromTOML: config.functions?.['*'].deno_import_map, projectDir, + publishDir, runIsolate, servePath, + watchIgnore, deployEnvironment: deployEnvironment .filter(({ scopes }) => scopes.includes('functions')) // Scopes should be opaque to the functions registry: We just filtered down to only variables diff --git a/src/lib/edge-functions/registry.ts b/src/lib/edge-functions/registry.ts index 58b69b67149..5021da4533e 100644 --- a/src/lib/edge-functions/registry.ts +++ b/src/lib/edge-functions/registry.ts @@ -1,5 +1,6 @@ import { readFile } from 'fs/promises' -import { join } from 'path' +import { statSync } from 'fs' +import { join, resolve } from 'path' import { fileURLToPath } from 'url' import type { Declaration, EdgeFunction, FunctionConfig, Manifest, ModuleGraph } from '@netlify/edge-bundler' @@ -48,8 +49,10 @@ interface EdgeFunctionsRegistryOptions { getUpdatedConfig: () => Promise importMapFromTOML?: string projectDir: string + publishDir: string runIsolate: RunIsolate servePath: string + watchIgnore: string[] deployEnvironment: { key: string; value: string; isSecret: boolean }[] } @@ -143,6 +146,8 @@ export class EdgeFunctionsRegistryImpl implements EdgeFunctionsRegistry { private routes: Route[] = [] private runIsolate: RunIsolate private servePath: string + private publishDir: string + private watchIgnore: string[] private projectDir: string private command: BaseCommand @@ -157,8 +162,10 @@ export class EdgeFunctionsRegistryImpl implements EdgeFunctionsRegistry { getUpdatedConfig, importMapFromTOML, projectDir, + publishDir, runIsolate, servePath, + watchIgnore, deployEnvironment, }: EdgeFunctionsRegistryOptions) { this.aiGatewayContext = aiGatewayContext @@ -169,6 +176,8 @@ export class EdgeFunctionsRegistryImpl implements EdgeFunctionsRegistry { this.getUpdatedConfig = getUpdatedConfig this.runIsolate = runIsolate this.servePath = servePath + this.publishDir = resolve(projectDir, publishDir) + this.watchIgnore = watchIgnore.map((p) => resolve(projectDir, p)) this.projectDir = projectDir this.importMapFromTOML = importMapFromTOML @@ -719,7 +728,29 @@ export class EdgeFunctionsRegistryImpl implements EdgeFunctionsRegistry { } private async setupWatcherForDirectory() { - const ignored = [`${this.servePath}/**`, this.internalImportMapPath] + const toIgnoredRegex = (dir: string) => new RegExp(`^${dir.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}(/|$)`) + + const toIgnoredEntry = (p: string): string | RegExp => { + try { + if (statSync(p).isFile()) return p + } catch { + // path doesn't exist yet (e.g. publish dir before first build) — treat as directory + } + return toIgnoredRegex(p) + } + + const ignored: (string | RegExp)[] = [ + toIgnoredRegex(this.servePath), + toIgnoredRegex(this.publishDir), + ...this.watchIgnore.map(toIgnoredEntry), + this.internalImportMapPath, + // Edge functions can only import JS/TS/JSON/WASM files, so there is no + // need to watch markdown, images, CSS, and other static assets. The regex + // matches paths ending in a non-importable file extension. The negative + // lookahead prevents matching the allowed extensions; [^./]+ prevents + // inadvertently matching directories whose names contain dots (e.g. "v2.0"). + /\.(?!(js|mjs|cjs|ts|tsx|jsx|json|wasm)$)[^./]+$/i, + ] const watcher = await watchDebounced(this.projectDir, { ignored, onAdd: () => this.checkForAddedOrDeletedFunctions(), diff --git a/src/utils/proxy-server.ts b/src/utils/proxy-server.ts index 1a1a274d7a7..3946666848b 100644 --- a/src/utils/proxy-server.ts +++ b/src/utils/proxy-server.ts @@ -65,6 +65,7 @@ export const startProxyServer = async ({ site, siteInfo, state, + watchIgnore, deployEnvironment, }: { accountId: string | undefined @@ -90,6 +91,7 @@ export const startProxyServer = async ({ projectDir: string repositoryRoot?: string state: LocalState + watchIgnore: string[] functionsRegistry?: FunctionsRegistry deployEnvironment: { key: string; value: string; isSecret: boolean; scopes: string[] }[] }) => { @@ -116,6 +118,7 @@ export const startProxyServer = async ({ accountId, repositoryRoot, api, + watchIgnore, deployEnvironment, }) if (!url) { diff --git a/src/utils/proxy.ts b/src/utils/proxy.ts index 9bbd0b84289..23450828303 100644 --- a/src/utils/proxy.ts +++ b/src/utils/proxy.ts @@ -947,6 +947,7 @@ export const startProxy = async function ({ settings, siteInfo, state, + watchIgnore, deployEnvironment, }: { command: BaseCommand @@ -955,6 +956,7 @@ export const startProxy = async function ({ disableEdgeFunctions: boolean getUpdatedConfig: () => Promise aiGatewayContext?: AIGatewayContext | null + watchIgnore: string[] deployEnvironment: { key: string; value: string; isSecret: boolean; scopes: string[] }[] } & Record) { const secondaryServerPort = settings.https ? await getAvailablePort() : null @@ -988,6 +990,7 @@ export const startProxy = async function ({ repositoryRoot, siteInfo, state, + watchIgnore, deployEnvironment, }) } From e3316422ee43b6acc5eb62d8414217c6c3a5ea81 Mon Sep 17 00:00:00 2001 From: Hrishikesh Kokate Date: Thu, 21 May 2026 19:44:44 +0530 Subject: [PATCH 02/10] update docs --- docs/commands/dev.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/commands/dev.md b/docs/commands/dev.md index cba068156ce..f57c5b8aa3a 100644 --- a/docs/commands/dev.md +++ b/docs/commands/dev.md @@ -38,6 +38,7 @@ netlify dev - `port` (*string*) - port of netlify dev - `skip-gitignore` (*boolean*) - skip adding .netlify to .gitignore file - `target-port` (*string*) - port of target app server +- `watch-ignore` (*string*) - exclude a path from file watching. accepts a file path or a directory; when a directory is ignored its entire subtree is excluded and cannot be partially re-included. can be specified multiple times. | Subcommand | description | |:--------------------------- |:-----| @@ -58,6 +59,8 @@ netlify dev --edge-inspect=127.0.0.1:9229 netlify dev --edge-inspect-brk netlify dev --edge-inspect-brk=127.0.0.1:9229 netlify dev --skip-gitignore # skip adding .netlify to .gitignore +netlify dev --watch-ignore src/posts # exclude a content directory from file watching +netlify dev --watch-ignore src/posts --watch-ignore content # exclude multiple directories BROWSER=none netlify dev # disable browser auto opening ``` From 57a6057182f996849b60c19e083314f00cea4f5d Mon Sep 17 00:00:00 2001 From: Hrishikesh Kokate Date: Thu, 21 May 2026 19:52:14 +0530 Subject: [PATCH 03/10] coderabbit 1 --- docs/commands/dev.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/commands/dev.md b/docs/commands/dev.md index f57c5b8aa3a..40e6349a495 100644 --- a/docs/commands/dev.md +++ b/docs/commands/dev.md @@ -38,7 +38,7 @@ netlify dev - `port` (*string*) - port of netlify dev - `skip-gitignore` (*boolean*) - skip adding .netlify to .gitignore file - `target-port` (*string*) - port of target app server -- `watch-ignore` (*string*) - exclude a path from file watching. accepts a file path or a directory; when a directory is ignored its entire subtree is excluded and cannot be partially re-included. can be specified multiple times. +- `watch-ignore` (*string*) - exclude a path from file watching. Accepts a file path or a directory; when a directory is ignored its entire subtree is excluded and cannot be partially re-included. Can be specified multiple times. | Subcommand | description | |:--------------------------- |:-----| From a7fdf28599d74338f2d820006ef6ecea5b7de835 Mon Sep 17 00:00:00 2001 From: Hrishikesh Kokate Date: Thu, 21 May 2026 19:53:57 +0530 Subject: [PATCH 04/10] coderabbit 2 --- src/lib/edge-functions/registry.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/lib/edge-functions/registry.ts b/src/lib/edge-functions/registry.ts index 5021da4533e..026cb482eb9 100644 --- a/src/lib/edge-functions/registry.ts +++ b/src/lib/edge-functions/registry.ts @@ -739,17 +739,14 @@ export class EdgeFunctionsRegistryImpl implements EdgeFunctionsRegistry { return toIgnoredRegex(p) } + const NON_IMPORTABLE_EDGE_ASSET_EXTENSIONS = /\.(?!(js|mjs|cjs|ts|tsx|jsx|json|wasm)$)[^./]+$/i + const ignored: (string | RegExp)[] = [ toIgnoredRegex(this.servePath), toIgnoredRegex(this.publishDir), ...this.watchIgnore.map(toIgnoredEntry), this.internalImportMapPath, - // Edge functions can only import JS/TS/JSON/WASM files, so there is no - // need to watch markdown, images, CSS, and other static assets. The regex - // matches paths ending in a non-importable file extension. The negative - // lookahead prevents matching the allowed extensions; [^./]+ prevents - // inadvertently matching directories whose names contain dots (e.g. "v2.0"). - /\.(?!(js|mjs|cjs|ts|tsx|jsx|json|wasm)$)[^./]+$/i, + NON_IMPORTABLE_EDGE_ASSET_EXTENSIONS, ] const watcher = await watchDebounced(this.projectDir, { ignored, From 917a1c24de0ee8179f92ab71899f3eeac2959fd4 Mon Sep 17 00:00:00 2001 From: Hrishikesh Kokate Date: Thu, 21 May 2026 20:07:05 +0530 Subject: [PATCH 05/10] add tests --- .../lib/edge-functions/watch-ignore.test.ts | 284 ++++++++++++++++++ 1 file changed, 284 insertions(+) create mode 100644 tests/unit/lib/edge-functions/watch-ignore.test.ts diff --git a/tests/unit/lib/edge-functions/watch-ignore.test.ts b/tests/unit/lib/edge-functions/watch-ignore.test.ts new file mode 100644 index 00000000000..3178fb865f8 --- /dev/null +++ b/tests/unit/lib/edge-functions/watch-ignore.test.ts @@ -0,0 +1,284 @@ +import { statSync } from 'fs' +import { join } from 'path' + +import { beforeEach, describe, expect, test, vi } from 'vitest' + +import type BaseCommand from '../../../../src/commands/base-command.js' +import { EdgeFunctionsRegistryImpl } from '../../../../src/lib/edge-functions/registry.js' +import type { NormalizedCachedConfigConfig } from '../../../../src/utils/command-helpers.js' + +vi.mock('fs', async (importOriginal) => { + const actual = await importOriginal() + return { ...actual, statSync: vi.fn(actual.statSync) } +}) + +vi.mock('@netlify/dev-utils', async (importOriginal) => { + const actual = await importOriginal() + return { + ...actual, + watchDebounced: vi.fn().mockResolvedValue({ close: vi.fn(), add: vi.fn(), unwatch: vi.fn() }), + } +}) + +// Creates a partial registry via Object.create so the constructor is bypassed, +// then populates the private fields needed by setupWatcherForDirectory. +const makeRegistry = (fields: { + projectDir: string + servePath: string + publishDir: string + watchIgnore: string[] +}) => { + const registry = Object.create(EdgeFunctionsRegistryImpl.prototype) as EdgeFunctionsRegistryImpl + Object.assign(registry, { + ...fields, + directoryWatchers: new Map(), + checkForAddedOrDeletedFunctions: vi.fn(), + handleFileChange: vi.fn(), + }) + return registry +} + +const captureIgnored = async (registry: EdgeFunctionsRegistryImpl): Promise<(string | RegExp)[]> => { + const { watchDebounced } = await import('@netlify/dev-utils') + vi.mocked(watchDebounced).mockClear() + await (registry as unknown as { setupWatcherForDirectory: () => Promise }).setupWatcherForDirectory() + const [, options] = vi.mocked(watchDebounced).mock.calls[0] + return (options as { ignored: (string | RegExp)[] }).ignored +} + +describe('toIgnoredRegex', () => { + // The regex is defined inline in setupWatcherForDirectory. We test it by + // capturing the ignored array passed to watchDebounced. + + test('matches the directory path itself', async () => { + const registry = makeRegistry({ + projectDir: '/project', + servePath: '/project/.netlify/edge-functions-serve', + publishDir: '/project/_site', + watchIgnore: [], + }) + const ignored = await captureIgnored(registry) + const servePathRegex = ignored[0] as RegExp + expect(servePathRegex.test('/project/.netlify/edge-functions-serve')).toBe(true) + }) + + test('matches paths under the directory', async () => { + const registry = makeRegistry({ + projectDir: '/project', + servePath: '/project/.netlify/edge-functions-serve', + publishDir: '/project/_site', + watchIgnore: [], + }) + const ignored = await captureIgnored(registry) + const publishDirRegex = ignored[1] as RegExp + expect(publishDirRegex.test('/project/_site/posts/2024/index.html')).toBe(true) + }) + + test('does not match a sibling path that shares a prefix', async () => { + const registry = makeRegistry({ + projectDir: '/project', + servePath: '/project/.netlify/edge-functions-serve', + publishDir: '/project/_site', + watchIgnore: [], + }) + const ignored = await captureIgnored(registry) + const publishDirRegex = ignored[1] as RegExp + expect(publishDirRegex.test('/project/_site-backup/index.html')).toBe(false) + }) + + test('escapes special regex characters in the path', async () => { + const registry = makeRegistry({ + projectDir: '/project', + servePath: '/project/.netlify/edge-functions-serve', + publishDir: '/project/my.build (v2)', + watchIgnore: [], + }) + const ignored = await captureIgnored(registry) + const publishDirRegex = ignored[1] as RegExp + expect(publishDirRegex.test('/project/my.build (v2)/index.html')).toBe(true) + expect(publishDirRegex.test('/projectXmyYbuild-v2/index.html')).toBe(false) + }) +}) + +describe('toIgnoredEntry (watchIgnore entries)', () => { + beforeEach(() => { + vi.mocked(statSync).mockReset() + }) + + test('returns a plain string for an existing file', async () => { + vi.mocked(statSync).mockReturnValue({ isFile: () => true } as ReturnType) + const registry = makeRegistry({ + projectDir: '/project', + servePath: '/project/.netlify/edge-functions-serve', + publishDir: '/project/_site', + watchIgnore: ['/project/large-data.json'], + }) + const ignored = await captureIgnored(registry) + expect(ignored[2]).toBe('/project/large-data.json') + }) + + test('returns a regex for an existing directory', async () => { + vi.mocked(statSync).mockReturnValue({ isFile: () => false } as ReturnType) + const registry = makeRegistry({ + projectDir: '/project', + servePath: '/project/.netlify/edge-functions-serve', + publishDir: '/project/_site', + watchIgnore: ['/project/src/posts'], + }) + const ignored = await captureIgnored(registry) + expect(ignored[2]).toBeInstanceOf(RegExp) + expect((ignored[2] as RegExp).test('/project/src/posts/2024/my-post.md')).toBe(true) + }) + + test('returns a regex when the path does not exist yet', async () => { + vi.mocked(statSync).mockImplementation(() => { + throw new Error('ENOENT') + }) + const registry = makeRegistry({ + projectDir: '/project', + servePath: '/project/.netlify/edge-functions-serve', + publishDir: '/project/_site', + watchIgnore: ['/project/content'], + }) + const ignored = await captureIgnored(registry) + expect(ignored[2]).toBeInstanceOf(RegExp) + expect((ignored[2] as RegExp).test('/project/content/page.md')).toBe(true) + }) +}) + +describe('internalImportMapPath is kept as a plain string', () => { + test('import map path is passed as an exact-match string, not a regex', async () => { + const registry = makeRegistry({ + projectDir: '/project', + servePath: '/project/.netlify/edge-functions-serve', + publishDir: '/project/_site', + watchIgnore: [], + }) + const ignored = await captureIgnored(registry) + // servePath regex, publishDir regex, internalImportMapPath string, extension regex + const importMapEntry = ignored[2] + expect(typeof importMapEntry).toBe('string') + expect(importMapEntry).toBe(join('/project', '.netlify', 'edge-functions-import-map.json')) + }) +}) + +describe('extension filter regex', () => { + test('ignores non-importable file extensions', async () => { + const registry = makeRegistry({ + projectDir: '/project', + servePath: '/project/.netlify/edge-functions-serve', + publishDir: '/project/_site', + watchIgnore: [], + }) + const ignored = await captureIgnored(registry) + const extensionFilter = ignored[ignored.length - 1] as RegExp + for (const ext of ['md', 'html', 'css', 'scss', 'jpg', 'png', 'svg', 'txt']) { + expect(extensionFilter.test(`/project/src/file.${ext}`), `.${ext}`).toBe(true) + } + }) + + test('does not ignore importable file extensions', async () => { + const registry = makeRegistry({ + projectDir: '/project', + servePath: '/project/.netlify/edge-functions-serve', + publishDir: '/project/_site', + watchIgnore: [], + }) + const ignored = await captureIgnored(registry) + const extensionFilter = ignored[ignored.length - 1] as RegExp + for (const ext of ['js', 'mjs', 'cjs', 'ts', 'tsx', 'jsx', 'json', 'wasm']) { + expect(extensionFilter.test(`/project/src/file.${ext}`), `.${ext}`).toBe(false) + } + }) + + test('matches a path whose last segment looks like a non-importable extension (known limitation)', async () => { + // A bare path like "/project/v2.0" ends with ".0" which the regex cannot + // distinguish from a file extension — it will match and be ignored. + // Directories with dotted names (e.g. "v2.0") should be excluded via + // --watch-ignore if this causes problems. + const registry = makeRegistry({ + projectDir: '/project', + servePath: '/project/.netlify/edge-functions-serve', + publishDir: '/project/_site', + watchIgnore: [], + }) + const ignored = await captureIgnored(registry) + const extensionFilter = ignored[ignored.length - 1] as RegExp + expect(extensionFilter.test('/project/v2.0')).toBe(true) + }) + + test('does not ignore a file nested under a dotted directory name', async () => { + // Even though "/project/v2.0" itself matches, a .ts file inside it does not. + const registry = makeRegistry({ + projectDir: '/project', + servePath: '/project/.netlify/edge-functions-serve', + publishDir: '/project/_site', + watchIgnore: [], + }) + const ignored = await captureIgnored(registry) + const extensionFilter = ignored[ignored.length - 1] as RegExp + expect(extensionFilter.test('/project/v2.0/handler.ts')).toBe(false) + }) +}) + +describe('constructor path resolution', () => { + const makeOptions = (overrides: { publishDir?: string; watchIgnore?: string[] } = {}) => ({ + aiGatewayContext: null, + bundler: { find: vi.fn().mockResolvedValue([]) } as unknown as typeof import('@netlify/edge-bundler'), + command: { netlify: { config: { build: {} } }, workingDir: '/project' } as unknown as BaseCommand, + config: { edge_functions: [], functions: { '*': {} } } as unknown as NormalizedCachedConfigConfig, + configPath: '/project/netlify.toml', + debug: false, + env: {}, + featureFlags: {}, + getUpdatedConfig: vi.fn(), + projectDir: '/project', + publishDir: overrides.publishDir ?? '_site', + runIsolate: vi.fn() as unknown as Awaited>, + servePath: '/project/.netlify/edge-functions-serve', + watchIgnore: overrides.watchIgnore ?? [], + deployEnvironment: [], + }) + + beforeEach(() => { + vi.spyOn( + EdgeFunctionsRegistryImpl.prototype as unknown as { doInitialScan: () => Promise }, + 'doInitialScan', + ).mockResolvedValue(undefined) + vi.spyOn( + EdgeFunctionsRegistryImpl.prototype as unknown as { setupWatchers: () => Promise }, + 'setupWatchers', + ).mockResolvedValue(undefined) + }) + + type RegistryPrivateState = { publishDir: string; watchIgnore: string[] } + + test('resolves a relative publishDir against projectDir', () => { + const registry = new EdgeFunctionsRegistryImpl(makeOptions({ publishDir: '_site' })) + expect((registry as unknown as RegistryPrivateState).publishDir).toBe('/project/_site') + }) + + test('keeps an absolute publishDir unchanged', () => { + const registry = new EdgeFunctionsRegistryImpl(makeOptions({ publishDir: '/other/_site' })) + expect((registry as unknown as RegistryPrivateState).publishDir).toBe('/other/_site') + }) + + test('resolves relative watchIgnore paths against projectDir', () => { + const registry = new EdgeFunctionsRegistryImpl(makeOptions({ watchIgnore: ['src/posts', 'content'] })) + expect((registry as unknown as RegistryPrivateState).watchIgnore).toEqual(['/project/src/posts', '/project/content']) + }) + + test('keeps absolute watchIgnore paths unchanged', () => { + const registry = new EdgeFunctionsRegistryImpl( + makeOptions({ watchIgnore: ['/absolute/src/posts', '/other/content'] }), + ) + expect((registry as unknown as RegistryPrivateState).watchIgnore).toEqual(['/absolute/src/posts', '/other/content']) + }) + + test('handles a mix of relative and absolute watchIgnore paths', () => { + const registry = new EdgeFunctionsRegistryImpl( + makeOptions({ watchIgnore: ['src/posts', '/absolute/content'] }), + ) + expect((registry as unknown as RegistryPrivateState).watchIgnore).toEqual(['/project/src/posts', '/absolute/content']) + }) +}) From 2ab7a8a609ebaf868d57b7073a3db743ed3a6152 Mon Sep 17 00:00:00 2001 From: Hrishikesh Kokate Date: Thu, 21 May 2026 20:29:54 +0530 Subject: [PATCH 06/10] remove extensions regex --- src/lib/edge-functions/registry.ts | 3 - .../lib/edge-functions/watch-ignore.test.ts | 60 +------------------ 2 files changed, 1 insertion(+), 62 deletions(-) diff --git a/src/lib/edge-functions/registry.ts b/src/lib/edge-functions/registry.ts index 026cb482eb9..cef6bada380 100644 --- a/src/lib/edge-functions/registry.ts +++ b/src/lib/edge-functions/registry.ts @@ -739,14 +739,11 @@ export class EdgeFunctionsRegistryImpl implements EdgeFunctionsRegistry { return toIgnoredRegex(p) } - const NON_IMPORTABLE_EDGE_ASSET_EXTENSIONS = /\.(?!(js|mjs|cjs|ts|tsx|jsx|json|wasm)$)[^./]+$/i - const ignored: (string | RegExp)[] = [ toIgnoredRegex(this.servePath), toIgnoredRegex(this.publishDir), ...this.watchIgnore.map(toIgnoredEntry), this.internalImportMapPath, - NON_IMPORTABLE_EDGE_ASSET_EXTENSIONS, ] const watcher = await watchDebounced(this.projectDir, { ignored, diff --git a/tests/unit/lib/edge-functions/watch-ignore.test.ts b/tests/unit/lib/edge-functions/watch-ignore.test.ts index 3178fb865f8..26f7e27590b 100644 --- a/tests/unit/lib/edge-functions/watch-ignore.test.ts +++ b/tests/unit/lib/edge-functions/watch-ignore.test.ts @@ -155,71 +155,13 @@ describe('internalImportMapPath is kept as a plain string', () => { watchIgnore: [], }) const ignored = await captureIgnored(registry) - // servePath regex, publishDir regex, internalImportMapPath string, extension regex + // servePath regex, publishDir regex, internalImportMapPath string const importMapEntry = ignored[2] expect(typeof importMapEntry).toBe('string') expect(importMapEntry).toBe(join('/project', '.netlify', 'edge-functions-import-map.json')) }) }) -describe('extension filter regex', () => { - test('ignores non-importable file extensions', async () => { - const registry = makeRegistry({ - projectDir: '/project', - servePath: '/project/.netlify/edge-functions-serve', - publishDir: '/project/_site', - watchIgnore: [], - }) - const ignored = await captureIgnored(registry) - const extensionFilter = ignored[ignored.length - 1] as RegExp - for (const ext of ['md', 'html', 'css', 'scss', 'jpg', 'png', 'svg', 'txt']) { - expect(extensionFilter.test(`/project/src/file.${ext}`), `.${ext}`).toBe(true) - } - }) - - test('does not ignore importable file extensions', async () => { - const registry = makeRegistry({ - projectDir: '/project', - servePath: '/project/.netlify/edge-functions-serve', - publishDir: '/project/_site', - watchIgnore: [], - }) - const ignored = await captureIgnored(registry) - const extensionFilter = ignored[ignored.length - 1] as RegExp - for (const ext of ['js', 'mjs', 'cjs', 'ts', 'tsx', 'jsx', 'json', 'wasm']) { - expect(extensionFilter.test(`/project/src/file.${ext}`), `.${ext}`).toBe(false) - } - }) - - test('matches a path whose last segment looks like a non-importable extension (known limitation)', async () => { - // A bare path like "/project/v2.0" ends with ".0" which the regex cannot - // distinguish from a file extension — it will match and be ignored. - // Directories with dotted names (e.g. "v2.0") should be excluded via - // --watch-ignore if this causes problems. - const registry = makeRegistry({ - projectDir: '/project', - servePath: '/project/.netlify/edge-functions-serve', - publishDir: '/project/_site', - watchIgnore: [], - }) - const ignored = await captureIgnored(registry) - const extensionFilter = ignored[ignored.length - 1] as RegExp - expect(extensionFilter.test('/project/v2.0')).toBe(true) - }) - - test('does not ignore a file nested under a dotted directory name', async () => { - // Even though "/project/v2.0" itself matches, a .ts file inside it does not. - const registry = makeRegistry({ - projectDir: '/project', - servePath: '/project/.netlify/edge-functions-serve', - publishDir: '/project/_site', - watchIgnore: [], - }) - const ignored = await captureIgnored(registry) - const extensionFilter = ignored[ignored.length - 1] as RegExp - expect(extensionFilter.test('/project/v2.0/handler.ts')).toBe(false) - }) -}) describe('constructor path resolution', () => { const makeOptions = (overrides: { publishDir?: string; watchIgnore?: string[] } = {}) => ({ From 31886ebd7cb22e6a821a998a4a41e1c2f698a776 Mon Sep 17 00:00:00 2001 From: Hrishikesh Kokate Date: Thu, 21 May 2026 20:34:13 +0530 Subject: [PATCH 07/10] run prettier --- .../lib/edge-functions/watch-ignore.test.ts | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/tests/unit/lib/edge-functions/watch-ignore.test.ts b/tests/unit/lib/edge-functions/watch-ignore.test.ts index 26f7e27590b..927bca54213 100644 --- a/tests/unit/lib/edge-functions/watch-ignore.test.ts +++ b/tests/unit/lib/edge-functions/watch-ignore.test.ts @@ -22,12 +22,7 @@ vi.mock('@netlify/dev-utils', async (importOriginal) => { // Creates a partial registry via Object.create so the constructor is bypassed, // then populates the private fields needed by setupWatcherForDirectory. -const makeRegistry = (fields: { - projectDir: string - servePath: string - publishDir: string - watchIgnore: string[] -}) => { +const makeRegistry = (fields: { projectDir: string; servePath: string; publishDir: string; watchIgnore: string[] }) => { const registry = Object.create(EdgeFunctionsRegistryImpl.prototype) as EdgeFunctionsRegistryImpl Object.assign(registry, { ...fields, @@ -162,7 +157,6 @@ describe('internalImportMapPath is kept as a plain string', () => { }) }) - describe('constructor path resolution', () => { const makeOptions = (overrides: { publishDir?: string; watchIgnore?: string[] } = {}) => ({ aiGatewayContext: null, @@ -207,7 +201,10 @@ describe('constructor path resolution', () => { test('resolves relative watchIgnore paths against projectDir', () => { const registry = new EdgeFunctionsRegistryImpl(makeOptions({ watchIgnore: ['src/posts', 'content'] })) - expect((registry as unknown as RegistryPrivateState).watchIgnore).toEqual(['/project/src/posts', '/project/content']) + expect((registry as unknown as RegistryPrivateState).watchIgnore).toEqual([ + '/project/src/posts', + '/project/content', + ]) }) test('keeps absolute watchIgnore paths unchanged', () => { @@ -218,9 +215,10 @@ describe('constructor path resolution', () => { }) test('handles a mix of relative and absolute watchIgnore paths', () => { - const registry = new EdgeFunctionsRegistryImpl( - makeOptions({ watchIgnore: ['src/posts', '/absolute/content'] }), - ) - expect((registry as unknown as RegistryPrivateState).watchIgnore).toEqual(['/project/src/posts', '/absolute/content']) + const registry = new EdgeFunctionsRegistryImpl(makeOptions({ watchIgnore: ['src/posts', '/absolute/content'] })) + expect((registry as unknown as RegistryPrivateState).watchIgnore).toEqual([ + '/project/src/posts', + '/absolute/content', + ]) }) }) From 30722e365e6598ac8404a55513b21fc8bb21e73d Mon Sep 17 00:00:00 2001 From: Hrishikesh Kokate Date: Thu, 21 May 2026 20:45:03 +0530 Subject: [PATCH 08/10] skip setting publish dir in ignored if it's the same as project dir --- src/lib/edge-functions/registry.ts | 2 +- .../lib/edge-functions/watch-ignore.test.ts | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/lib/edge-functions/registry.ts b/src/lib/edge-functions/registry.ts index cef6bada380..f08e0ee76f4 100644 --- a/src/lib/edge-functions/registry.ts +++ b/src/lib/edge-functions/registry.ts @@ -741,7 +741,7 @@ export class EdgeFunctionsRegistryImpl implements EdgeFunctionsRegistry { const ignored: (string | RegExp)[] = [ toIgnoredRegex(this.servePath), - toIgnoredRegex(this.publishDir), + ...(this.publishDir !== this.projectDir ? [toIgnoredRegex(this.publishDir)] : []), ...this.watchIgnore.map(toIgnoredEntry), this.internalImportMapPath, ] diff --git a/tests/unit/lib/edge-functions/watch-ignore.test.ts b/tests/unit/lib/edge-functions/watch-ignore.test.ts index 927bca54213..f73db96d056 100644 --- a/tests/unit/lib/edge-functions/watch-ignore.test.ts +++ b/tests/unit/lib/edge-functions/watch-ignore.test.ts @@ -93,6 +93,23 @@ describe('toIgnoredRegex', () => { expect(publishDirRegex.test('/project/my.build (v2)/index.html')).toBe(true) expect(publishDirRegex.test('/projectXmyYbuild-v2/index.html')).toBe(false) }) + + test('omits publishDir from ignored when it equals projectDir', async () => { + // When no publish dir is configured, settings.dist falls back to the working + // directory itself. Ignoring projectDir would block all file watching, so we + // skip adding it in that case. + const registry = makeRegistry({ + projectDir: '/project', + servePath: '/project/.netlify/edge-functions-serve', + publishDir: '/project', + watchIgnore: [], + }) + const ignored = await captureIgnored(registry) + // Only servePath regex + internalImportMapPath — no publishDir entry + expect(ignored).toHaveLength(2) + expect(ignored[0]).toBeInstanceOf(RegExp) + expect(typeof ignored[1]).toBe('string') + }) }) describe('toIgnoredEntry (watchIgnore entries)', () => { From fb383aeff7741697f285c0be36a091a96b2e7d76 Mon Sep 17 00:00:00 2001 From: Hrishikesh Kokate Date: Thu, 21 May 2026 21:18:10 +0530 Subject: [PATCH 09/10] update docs --- docs/commands/dev.md | 2 +- package-lock.json | 1451 ++++++++++++++++++++++++++++++++++++++++-- package.json | 1 + 3 files changed, 1409 insertions(+), 45 deletions(-) diff --git a/docs/commands/dev.md b/docs/commands/dev.md index 40e6349a495..011ca013e03 100644 --- a/docs/commands/dev.md +++ b/docs/commands/dev.md @@ -38,7 +38,7 @@ netlify dev - `port` (*string*) - port of netlify dev - `skip-gitignore` (*boolean*) - skip adding .netlify to .gitignore file - `target-port` (*string*) - port of target app server -- `watch-ignore` (*string*) - exclude a path from file watching. Accepts a file path or a directory; when a directory is ignored its entire subtree is excluded and cannot be partially re-included. Can be specified multiple times. +- `watch-ignore` (*string*) - exclude a path from file watching. accepts a file path or a directory path; ignored directories exclude their entire subtree and cannot be partially re-included. can be specified multiple times. | Subcommand | description | |:--------------------------- |:-----| diff --git a/package-lock.json b/package-lock.json index bfae83c43ac..313866cc375 100644 --- a/package-lock.json +++ b/package-lock.json @@ -161,6 +161,7 @@ "eslint-plugin-n": "^17.23.1", "form-data": "^4.0.4", "lodash.shuffle": "^4.2.0", + "markdown-magic": "^2.6.1", "memfs": "^4.56.10", "nock": "^14.0.10", "npm-run-all2": "^8.0.4", @@ -6372,7 +6373,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6386,7 +6386,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6400,7 +6399,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6414,7 +6412,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6428,7 +6425,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6442,7 +6438,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6456,7 +6451,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6470,7 +6464,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6484,7 +6477,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6498,7 +6490,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6512,7 +6503,6 @@ "cpu": [ "loong64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6526,7 +6516,6 @@ "cpu": [ "loong64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6540,7 +6529,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6554,7 +6542,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6568,7 +6555,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6582,7 +6568,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6596,7 +6581,6 @@ "cpu": [ "s390x" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6610,7 +6594,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6624,7 +6607,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6638,7 +6620,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6652,7 +6633,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6666,7 +6646,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6680,7 +6659,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6694,7 +6672,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6708,7 +6685,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6801,6 +6777,120 @@ "node": ">=10" } }, + "node_modules/@technote-space/anchor-markdown-header": { + "version": "1.1.42", + "resolved": "https://registry.npmjs.org/@technote-space/anchor-markdown-header/-/anchor-markdown-header-1.1.42.tgz", + "integrity": "sha512-iJ5qu1EO3kZDthq9zbMQ9ufB4jd0XwhHJ+4RNpTUEVTIZFitCV++IUfH1YCACGasct41pQRxGmWQNoaRZmn7EQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.2.1" + } + }, + "node_modules/@technote-space/doctoc": { + "version": "2.4.7", + "resolved": "https://registry.npmjs.org/@technote-space/doctoc/-/doctoc-2.4.7.tgz", + "integrity": "sha512-F4oyUpf2e29p3tNH0oTW0a3eOgd3wek2vz1urNalSKCFY8U0hzkFqwU+89rmXGLzzz8WMcLtEXb90CCgqnDQqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@technote-space/anchor-markdown-header": "^1.1.21", + "@textlint/markdown-to-ast": "^12.0.0", + "htmlparser2": "^6.1.0", + "update-section": "^0.3.3" + } + }, + "node_modules/@technote-space/doctoc/node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dev": true, + "license": "MIT", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/@technote-space/doctoc/node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/@technote-space/doctoc/node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/@technote-space/doctoc/node_modules/htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" + } + }, + "node_modules/@textlint/ast-node-types": { + "version": "12.6.1", + "resolved": "https://registry.npmjs.org/@textlint/ast-node-types/-/ast-node-types-12.6.1.tgz", + "integrity": "sha512-uzlJ+ZsCAyJm+lBi7j0UeBbj+Oy6w/VWoGJ3iHRHE5eZ8Z4iK66mq+PG/spupmbllLtz77OJbY89BYqgFyjXmA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@textlint/markdown-to-ast": { + "version": "12.6.1", + "resolved": "https://registry.npmjs.org/@textlint/markdown-to-ast/-/markdown-to-ast-12.6.1.tgz", + "integrity": "sha512-T0HO+VrU9VbLRiEx/kH4+gwGMHNMIGkp0Pok+p0I33saOOLyhfGvwOKQgvt2qkxzQEV2L5MtGB8EnW4r5d3CqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@textlint/ast-node-types": "^12.6.1", + "debug": "^4.3.4", + "mdast-util-gfm-autolink-literal": "^0.1.3", + "remark-footnotes": "^3.0.0", + "remark-frontmatter": "^3.0.0", + "remark-gfm": "^1.0.0", + "remark-parse": "^9.0.0", + "traverse": "^0.6.7", + "unified": "^9.2.2" + } + }, "node_modules/@tsconfig/node10": { "version": "1.0.11", "license": "MIT" @@ -6854,6 +6944,16 @@ "@types/deep-eql": "*" } }, + "node_modules/@types/concat-stream": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@types/concat-stream/-/concat-stream-1.6.1.tgz", + "integrity": "sha512-eHE4cQPoj6ngxBZMvVf6Hw7Mh4jMW4U9lpGmS5GBPB9RYxlFg+CHaVN7ErNY4W9XfLIEn20b4VDYaIrbq0q4uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/configstore": { "version": "6.0.2", "dev": true, @@ -6936,11 +7036,32 @@ "@types/node": "*" } }, + "node_modules/@types/form-data": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-0.0.33.tgz", + "integrity": "sha512-8BSvG1kGm83cyJITQMZSulnl6QV8jqAGreJsc5tPu1Jq0vTSOiY/k24Wx82JRpWwZSqrala6sd5rWi6aNXvqcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/gitconfiglocal": { "version": "2.0.3", "dev": true, "license": "MIT" }, + "node_modules/@types/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, "node_modules/@types/http-errors": { "version": "2.0.5", "dev": true, @@ -7004,6 +7125,23 @@ "@types/lodash": "*" } }, + "node_modules/@types/mdast": { + "version": "3.0.15", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.15.tgz", + "integrity": "sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^2" + } + }, + "node_modules/@types/minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/ms": { "version": "2.1.0", "dev": true, @@ -7146,6 +7284,13 @@ "version": "1.3.5", "license": "MIT" }, + "node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/update-notifier": { "version": "6.0.8", "dev": true, @@ -8758,6 +8903,16 @@ "version": "1.0.3", "license": "MIT" }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/array.prototype.flatmap": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", @@ -8797,6 +8952,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "dev": true, + "license": "MIT" + }, "node_modules/ascii-table": { "version": "0.0.9", "license": "MIT" @@ -8949,6 +9111,17 @@ "node": ">= 0.6" } }, + "node_modules/bail": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.5.tgz", + "integrity": "sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "license": "MIT" @@ -9612,6 +9785,17 @@ "dev": true, "license": "Apache-2.0" }, + "node_modules/ccount": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-1.1.0.tgz", + "integrity": "sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/chai": { "version": "5.3.3", "dev": true, @@ -9637,6 +9821,39 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/character-entities": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz", + "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", + "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz", + "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/chardet": { "version": "2.1.0", "license": "MIT" @@ -10196,6 +10413,55 @@ "dev": true, "license": "MIT" }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "engines": [ + "node >= 0.8" + ], + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/concat-stream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/concat-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/confbox": { "version": "0.1.8", "license": "MIT" @@ -10908,6 +11174,29 @@ "node": ">=0.3.1" } }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dir-glob/node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/dom-serializer": { "version": "2.0.0", "license": "MIT", @@ -12478,6 +12767,20 @@ "reusify": "^1.0.4" } }, + "node_modules/fault": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/fault/-/fault-1.0.4.tgz", + "integrity": "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==", + "dev": true, + "license": "MIT", + "dependencies": { + "format": "^0.2.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/fd-slicer": { "version": "1.1.0", "license": "MIT", @@ -12780,6 +13083,15 @@ "dev": true, "license": "MIT" }, + "node_modules/format": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", + "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", + "dev": true, + "engines": { + "node": ">=0.4.x" + } + }, "node_modules/formdata-polyfill": { "version": "4.0.10", "license": "MIT", @@ -12837,9 +13149,15 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, "node_modules/fsevents": { "version": "2.3.3", - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -13447,6 +13765,22 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/http-basic": { + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/http-basic/-/http-basic-8.1.3.tgz", + "integrity": "sha512-/EcDMwJZh3mABI2NhGfHOGOeOZITqfkEO4p/xK+l3NpyncIHUQBoMvCSF/b5GqvKtySC2srL/GGG3+EtlqlmCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "caseless": "^0.12.0", + "concat-stream": "^1.6.2", + "http-response-object": "^3.0.1", + "parse-cache-control": "^1.0.1" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/http-cache-semantics": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", @@ -13495,6 +13829,23 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/http-response-object": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/http-response-object/-/http-response-object-3.0.2.tgz", + "integrity": "sha512-bqX0XTF6fnXSQcEJ2Iuyr75yVakyjIDCqroJQ/aHfSdlM743Cwqoi2nDYMzLGWUcuTWGWy8AAvOKXTfiv6q9RA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "^10.0.3" + } + }, + "node_modules/http-response-object/node_modules/@types/node": { + "version": "10.17.60", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz", + "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==", + "dev": true, + "license": "MIT" + }, "node_modules/http-shutdown": { "version": "1.2.2", "license": "MIT", @@ -13688,8 +14039,20 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/inherits": { - "version": "2.0.4", + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", "license": "ISC" }, "node_modules/ini": { @@ -14036,6 +14399,32 @@ "url": "https://github.com/sponsors/brc-dd" } }, + "node_modules/is-alphabetical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", + "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", + "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-array-buffer": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", @@ -14107,6 +14496,30 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", @@ -14165,6 +14578,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-decimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", + "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-deflate": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-deflate/-/is-deflate-1.0.0.tgz", @@ -14268,6 +14692,17 @@ "node": ">=0.10.0" } }, + "node_modules/is-hexadecimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz", + "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-in-ci": { "version": "1.0.0", "license": "MIT", @@ -14343,6 +14778,13 @@ "node": ">=8" } }, + "node_modules/is-local-path": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-local-path/-/is-local-path-0.1.6.tgz", + "integrity": "sha512-VPRTy+0cYi1+X7hOTngxwXGfek1I6YItNwqsqjFPfH+8bXcGNP17Zx7D3nsfiLsJF3fISsUJq8kBRCZ0yMNeAg==", + "dev": true, + "license": "MIT" + }, "node_modules/is-map": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", @@ -14799,6 +15241,13 @@ "node": ">=6" } }, + "node_modules/json-alexander": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/json-alexander/-/json-alexander-0.1.13.tgz", + "integrity": "sha512-s84SuqZwnb7mGJB03sGlcdx458FR91Uwwy2lKJBDN+TBJ7ARbyP/ssGnjW9t5QyxwG/b23W22Dz9Ga6nuyHqnA==", + "dev": true, + "license": "MIT" + }, "node_modules/json-buffer": { "version": "3.0.1", "dev": true, @@ -15437,6 +15886,17 @@ "node": ">= 12.0.0" } }, + "node_modules/longest-streak": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-2.0.4.tgz", + "integrity": "sha512-vM6rUVCVUJJt33bnmHiZEvr7wPT78ztX7rojL+LW51bHtLh6HTjx84LA5W4+oa6aKEJA7jJu5LR6vQRBpA5DVg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/loupe": { "version": "3.2.1", "dev": true, @@ -15538,6 +15998,216 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/markdown-magic": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/markdown-magic/-/markdown-magic-2.6.1.tgz", + "integrity": "sha512-i+wPC9bAGzFVftF1P5ItooOCvX+TTD3v504WpupsJz+3B0wRZMuPxeFAE7uZXEZYjsiQYskoMgpypoJTWerpVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@technote-space/doctoc": "2.4.7", + "commander": "^7.2.0", + "deepmerge": "^4.2.2", + "find-up": "^5.0.0", + "globby": "^10.0.2", + "is-local-path": "^0.1.6", + "json-alexander": "^0.1.8", + "mkdirp": "^1.0.4", + "sync-request": "^6.1.0" + }, + "bin": { + "markdown": "cli.js", + "md-magic": "cli.js" + } + }, + "node_modules/markdown-magic/node_modules/brace-expansion": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/markdown-magic/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/markdown-magic/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/markdown-magic/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/markdown-magic/node_modules/globby": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.2.tgz", + "integrity": "sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/glob": "^7.1.1", + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.0.3", + "glob": "^7.1.3", + "ignore": "^5.1.1", + "merge2": "^1.2.3", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/markdown-magic/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/markdown-magic/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/markdown-magic/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/markdown-magic/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/markdown-magic/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/markdown-magic/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/markdown-magic/node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/markdown-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-2.0.0.tgz", + "integrity": "sha512-Ezda85ToJUBhM6WGaG6veasyym+Tbs3cMAw/ZhOPqXiYsr0jgocBV3j3nx+4lk47plLlIqjwuTm/ywVI+zjJ/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "repeat-string": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "license": "MIT", @@ -15567,6 +16237,189 @@ "once": "^1.3.1" } }, + "node_modules/mdast-util-find-and-replace": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-1.1.1.tgz", + "integrity": "sha512-9cKl33Y21lyckGzpSmEQnIDjEfeeWelN5s1kUW1LwdB0Fkuq2u+4GdqcGEygYxJE8GVqCl0741bYXHgamfWAZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^4.0.0", + "unist-util-is": "^4.0.0", + "unist-util-visit-parents": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mdast-util-footnote": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/mdast-util-footnote/-/mdast-util-footnote-0.1.7.tgz", + "integrity": "sha512-QxNdO8qSxqbO2e3m09KwDKfWiLgqyCurdWTQ198NpbZ2hxntdc+VKS4fDJCmNWbAroUdYnSthu+XbZ8ovh8C3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdast-util-to-markdown": "^0.6.0", + "micromark": "~2.11.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-from-markdown": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-0.8.5.tgz", + "integrity": "sha512-2hkTXtYYnr+NubD/g6KGBS/0mFmBcifAsI0yIWRiRo0PjVs6SSOSOdtzbp6kSGnShDN6G5aWZpKQ2lWRy27mWQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdast": "^3.0.0", + "mdast-util-to-string": "^2.0.0", + "micromark": "~2.11.0", + "parse-entities": "^2.0.0", + "unist-util-stringify-position": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-frontmatter": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-frontmatter/-/mdast-util-frontmatter-0.2.0.tgz", + "integrity": "sha512-FHKL4w4S5fdt1KjJCwB0178WJ0evnyyQr5kXTM3wrOVpytD0hrkvd+AOOjU9Td8onOejCkmZ+HQRT3CZ3coHHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "micromark-extension-frontmatter": "^0.2.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-0.1.2.tgz", + "integrity": "sha512-NNkhDx/qYcuOWB7xHUGWZYVXvjPFFd6afg6/e2g+SV4r9q5XUcCbV4Wfa3DLYIiD+xAEZc6K4MGaE/m0KDcPwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdast-util-gfm-autolink-literal": "^0.1.0", + "mdast-util-gfm-strikethrough": "^0.2.0", + "mdast-util-gfm-table": "^0.1.0", + "mdast-util-gfm-task-list-item": "^0.1.0", + "mdast-util-to-markdown": "^0.6.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-autolink-literal": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-0.1.3.tgz", + "integrity": "sha512-GjmLjWrXg1wqMIO9+ZsRik/s7PLwTaeCHVB7vRxUwLntZc8mzmTsLVr6HW1yLokcnhfURsn5zmSVdi3/xWWu1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ccount": "^1.0.0", + "mdast-util-find-and-replace": "^1.1.0", + "micromark": "^2.11.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-strikethrough": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-0.2.3.tgz", + "integrity": "sha512-5OQLXpt6qdbttcDG/UxYY7Yjj3e8P7X16LzvpX8pIQPYJ/C2Z1qFGMmcw+1PZMUM3Z8wt8NRfYTvCni93mgsgA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdast-util-to-markdown": "^0.6.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-table": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-0.1.6.tgz", + "integrity": "sha512-j4yDxQ66AJSBwGkbpFEp9uG/LS1tZV3P33fN1gkyRB2LoRL+RR3f76m0HPHaby6F4Z5xr9Fv1URmATlRRUIpRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "markdown-table": "^2.0.0", + "mdast-util-to-markdown": "~0.6.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-task-list-item": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-0.1.6.tgz", + "integrity": "sha512-/d51FFIfPsSmCIRNp7E6pozM9z1GYPIkSy1urQ8s/o4TC22BZ7DqfHFWiqBD23bc7J3vV1Fc9O4QIHBlfuit8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdast-util-to-markdown": "~0.6.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-0.6.5.tgz", + "integrity": "sha512-XeV9sDE7ZlOQvs45C9UKMtfTcctcaj/pGwH8YLbMHoMOXNNCn2LsqVQOqrF1+/NU8lKDAqozme9SCXWyo9oAcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "longest-streak": "^2.0.0", + "mdast-util-to-string": "^2.0.0", + "parse-entities": "^2.0.0", + "repeat-string": "^1.0.0", + "zwitch": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz", + "integrity": "sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/mdn-data": { "version": "2.27.1", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", @@ -15645,31 +16498,166 @@ "node": ">=8" } }, - "node_modules/merge-stream": { - "version": "2.0.0", - "license": "MIT" - }, - "node_modules/merge2": { - "version": "1.4.1", + "node_modules/merge-stream": { + "version": "2.0.0", + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micro-memoize": { + "version": "5.1.1", + "license": "MIT", + "dependencies": { + "fast-equals": "^5.3.3", + "fast-stringify": "^4.0.0" + } + }, + "node_modules/micromark": { + "version": "2.11.4", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-2.11.4.tgz", + "integrity": "sha512-+WoovN/ppKolQOFIAajxi7Lu9kInbPxFuTBVEavFcL8eAfVstoc5MocPmqBeAdBOJV00uaVjegzH4+MA0DN/uA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "debug": "^4.0.0", + "parse-entities": "^2.0.0" + } + }, + "node_modules/micromark-extension-footnote": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/micromark-extension-footnote/-/micromark-extension-footnote-0.3.2.tgz", + "integrity": "sha512-gr/BeIxbIWQoUm02cIfK7mdMZ/fbroRpLsck4kvFtjbzP4yi+OPVbnukTc/zy0i7spC2xYE/dbX1Sur8BEDJsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "micromark": "~2.11.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-frontmatter": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/micromark-extension-frontmatter/-/micromark-extension-frontmatter-0.2.2.tgz", + "integrity": "sha512-q6nPLFCMTLtfsctAuS0Xh4vaolxSFUWUWR6PZSrXXiRy+SANGllpcqdXFv2z07l0Xz/6Hl40hK0ffNCJPH2n1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "fault": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-0.3.3.tgz", + "integrity": "sha512-oVN4zv5/tAIA+l3GbMi7lWeYpJ14oQyJ3uEim20ktYFAcfX1x3LNlFGGlmrZHt7u9YlKExmyJdDGaTt6cMSR/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "micromark": "~2.11.0", + "micromark-extension-gfm-autolink-literal": "~0.5.0", + "micromark-extension-gfm-strikethrough": "~0.6.5", + "micromark-extension-gfm-table": "~0.4.0", + "micromark-extension-gfm-tagfilter": "~0.3.0", + "micromark-extension-gfm-task-list-item": "~0.3.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-0.5.7.tgz", + "integrity": "sha512-ePiDGH0/lhcngCe8FtH4ARFoxKTUelMp4L7Gg2pujYD5CSMb9PbblnyL+AAMud/SNMyusbS2XDSiPIRcQoNFAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "micromark": "~2.11.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-strikethrough": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-0.6.5.tgz", + "integrity": "sha512-PpOKlgokpQRwUesRwWEp+fHjGGkZEejj83k9gU5iXCbDG+XBA92BqnRKYJdfqfkrRcZRgGuPuXb7DaK/DmxOhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "micromark": "~2.11.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-table": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-0.4.3.tgz", + "integrity": "sha512-hVGvESPq0fk6ALWtomcwmgLvH8ZSVpcPjzi0AjPclB9FsVRgMtGZkUcpE0zgjOCFAznKepF4z3hX8z6e3HODdA==", + "dev": true, "license": "MIT", - "engines": { - "node": ">= 8" + "dependencies": { + "micromark": "~2.11.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/methods": { - "version": "1.1.2", + "node_modules/micromark-extension-gfm-tagfilter": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-0.3.0.tgz", + "integrity": "sha512-9GU0xBatryXifL//FJH+tAZ6i240xQuFrSL7mYi8f4oZSbc+NvXjkrHemeYP0+L4ZUT+Ptz3b95zhUZnMtoi/Q==", "dev": true, "license": "MIT", - "engines": { - "node": ">= 0.6" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/micro-memoize": { - "version": "5.1.1", + "node_modules/micromark-extension-gfm-task-list-item": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-0.3.3.tgz", + "integrity": "sha512-0zvM5iSLKrc/NQl84pZSjGo66aTGd57C1idmlWmE87lkMcXrTxg1uXa/nXomxJytoje9trP0NDLvw4bZ/Z/XCQ==", + "dev": true, "license": "MIT", "dependencies": { - "fast-equals": "^5.3.3", - "fast-stringify": "^4.0.0" + "micromark": "~2.11.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, "node_modules/micromatch": { @@ -16740,12 +17728,37 @@ "node": ">=6" } }, + "node_modules/parse-cache-control": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-cache-control/-/parse-cache-control-1.0.1.tgz", + "integrity": "sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg==", + "dev": true + }, "node_modules/parse-duration": { "version": "2.1.6", "resolved": "https://registry.npmjs.org/parse-duration/-/parse-duration-2.1.6.tgz", "integrity": "sha512-1/A2Exg3NcJGcYdgV/dn4frR7vO2hOW/ohQ4KIgbT4W3raVcpYSszPWiL6I6cKufi4jQM5NbGRXLBj8AoLM4iQ==", "license": "MIT" }, + "node_modules/parse-entities": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz", + "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "character-entities": "^1.0.0", + "character-entities-legacy": "^1.0.0", + "character-reference-invalid": "^1.0.0", + "is-alphanumerical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-hexadecimal": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/parse-github-url": { "version": "1.0.3", "license": "MIT", @@ -16858,6 +17871,16 @@ "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/path-key": { "version": "4.0.0", "license": "MIT", @@ -17356,6 +18379,16 @@ ], "license": "MIT" }, + "node_modules/promise": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz", + "integrity": "sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==", + "dev": true, + "license": "MIT", + "dependencies": { + "asap": "~2.0.6" + } + }, "node_modules/propagate": { "version": "2.0.1", "dev": true, @@ -17880,10 +18913,79 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/remark-footnotes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/remark-footnotes/-/remark-footnotes-3.0.0.tgz", + "integrity": "sha512-ZssAvH9FjGYlJ/PBVKdSmfyPc3Cz4rTWgZLI4iE/SX8Nt5l3o3oEjv3wwG5VD7xOjktzdwp5coac+kJV9l4jgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdast-util-footnote": "^0.1.0", + "micromark-extension-footnote": "^0.3.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-frontmatter": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/remark-frontmatter/-/remark-frontmatter-3.0.0.tgz", + "integrity": "sha512-mSuDd3svCHs+2PyO29h7iijIZx4plX0fheacJcAoYAASfgzgVIcXGYSq9GFyYocFLftQs8IOmmkgtOovs6d4oA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdast-util-frontmatter": "^0.2.0", + "micromark-extension-frontmatter": "^0.2.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-gfm": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-1.0.0.tgz", + "integrity": "sha512-KfexHJCiqvrdBZVbQ6RopMZGwaXz6wFJEfByIuEwGf0arvITHjiKKZ1dpXujjH9KZdm1//XJQwgfnJ3lmXaDPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdast-util-gfm": "^0.1.0", + "micromark-extension-gfm": "^0.3.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-parse": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-9.0.0.tgz", + "integrity": "sha512-geKatMwSzEXKHuzBNU1z676sGcDcFoChMK38TgdHJNAYfFtsfHDQG7MoJAjs6sgYMqyLduCYWDIWZIxiPeafEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdast-util-from-markdown": "^0.8.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/remove-trailing-separator": { "version": "1.1.0", "license": "ISC" }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, "node_modules/require-directory": { "version": "2.1.1", "license": "MIT", @@ -19054,6 +20156,41 @@ "node": ">=16" } }, + "node_modules/sync-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/sync-request/-/sync-request-6.1.0.tgz", + "integrity": "sha512-8fjNkrNlNCrVc/av+Jn+xxqfCjYaBoHqCsDz6mt030UMxJGr+GSfCV1dQt2gRtlL63+VPidwDVLr7V2OcTSdRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "http-response-object": "^3.0.1", + "sync-rpc": "^1.2.1", + "then-request": "^6.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/sync-rpc": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/sync-rpc/-/sync-rpc-1.3.6.tgz", + "integrity": "sha512-J8jTXuZzRlvU7HemDgHi3pGnh/rkoqR/OZSjhTyyZrEkkYQbk7Z33AXp37mkPfPpfdOuj7Ex3H/TJM1z48uPQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-port": "^3.1.0" + } + }, + "node_modules/sync-rpc/node_modules/get-port": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-3.2.0.tgz", + "integrity": "sha512-x5UJKlgeUiNT8nyo/AcnwLnZuZNcSjSw0kogRB+Whd1fjjFq4B1hySFxSFWWSn4mIBzg3sRNUDFYc4g5gjPoLg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/system-architecture": { "version": "0.1.0", "license": "MIT", @@ -19188,6 +20325,54 @@ "version": "1.0.0", "license": "MIT" }, + "node_modules/then-request": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/then-request/-/then-request-6.0.2.tgz", + "integrity": "sha512-3ZBiG7JvP3wbDzA9iNY5zJQcHL4jn/0BWtXIkagfz7QgOL/LqjCEOBQuJNZfu0XYnv5JhKh+cDxCPM4ILrqruA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/concat-stream": "^1.6.0", + "@types/form-data": "0.0.33", + "@types/node": "^8.0.0", + "@types/qs": "^6.2.31", + "caseless": "~0.12.0", + "concat-stream": "^1.6.0", + "form-data": "^2.2.0", + "http-basic": "^8.1.1", + "http-response-object": "^3.0.1", + "promise": "^8.0.0", + "qs": "^6.4.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/then-request/node_modules/@types/node": { + "version": "8.10.66", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.66.tgz", + "integrity": "sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/then-request/node_modules/form-data": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.5.tgz", + "integrity": "sha512-jqdObeR2rxZZbPSGL+3VckHMYtu+f9//KXBsVny6JSX/pa38Fy+bGjuG8eW/H6USNQWhLi8Num++cU2yOCNz4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.35", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.12" + } + }, "node_modules/thingies": { "version": "2.5.0", "dev": true, @@ -19374,6 +20559,24 @@ "version": "0.0.3", "license": "MIT" }, + "node_modules/traverse": { + "version": "0.6.11", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.11.tgz", + "integrity": "sha512-vxXDZg8/+p3gblxB6BhhG5yWVn1kGRlaL8O78UDXc3wRnPizB5g83dcvWV1jpDMIPnjZjOFuxlMmE82XJ4407w==", + "dev": true, + "license": "MIT", + "dependencies": { + "gopd": "^1.2.0", + "typedarray.prototype.slice": "^1.0.5", + "which-typed-array": "^1.1.18" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/tree-dump": { "version": "1.1.0", "dev": true, @@ -19404,6 +20607,17 @@ "node": ">= 14.0.0" } }, + "node_modules/trough": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.5.tgz", + "integrity": "sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/ts-api-utils": { "version": "2.1.0", "license": "MIT", @@ -19629,6 +20843,36 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/typedarray.prototype.slice": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/typedarray.prototype.slice/-/typedarray.prototype.slice-1.0.5.tgz", + "integrity": "sha512-q7QNVDGTdl702bVFiI5eY4l/HkgCM6at9KhcFbgUAzezHFbOVy4+0O/lCjsABEQwbZPravVfBIiBVGo89yzHFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "math-intrinsics": "^1.1.0", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-offset": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/typescript": { "version": "5.8.3", "license": "Apache-2.0", @@ -19743,6 +20987,75 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/unified": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/unified/-/unified-9.2.2.tgz", + "integrity": "sha512-Sg7j110mtefBD+qunSLO1lqOEKdrwBFBrR6Qd8f4uwkhWNlbkaqwHse6e7QvD3AP/MNoJdEDLaf8OxYyoWgorQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bail": "^1.0.0", + "extend": "^3.0.0", + "is-buffer": "^2.0.0", + "is-plain-obj": "^2.0.0", + "trough": "^1.0.0", + "vfile": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unified/node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/unist-util-is": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.1.0.tgz", + "integrity": "sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz", + "integrity": "sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz", + "integrity": "sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/universal-user-agent": { "version": "7.0.3", "license": "ISC" @@ -19996,6 +21309,13 @@ "url": "https://github.com/yeoman/update-notifier?sponsor=1" } }, + "node_modules/update-section": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/update-section/-/update-section-0.3.3.tgz", + "integrity": "sha512-BpRZMZpgXLuTiKeiu7kK0nIPwGdyrqrs6EDSaXtjD/aQ2T+qVo9a5hRC3HN3iJjCMxNT/VxoLGQ7E/OzE5ucnw==", + "dev": true, + "license": "MIT" + }, "node_modules/uqr": { "version": "0.1.2", "license": "MIT" @@ -20454,6 +21774,38 @@ "extsprintf": "^1.2.0" } }, + "node_modules/vfile": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-4.2.1.tgz", + "integrity": "sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "is-buffer": "^2.0.0", + "unist-util-stringify-position": "^2.0.0", + "vfile-message": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-2.0.4.tgz", + "integrity": "sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-stringify-position": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/vite": { "version": "6.4.1", "dev": true, @@ -21322,6 +22674,17 @@ "funding": { "url": "https://github.com/sponsors/colinhacks" } + }, + "node_modules/zwitch": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-1.0.5.tgz", + "integrity": "sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } } } } diff --git a/package.json b/package.json index 47b932334e8..e5420ecdf14 100644 --- a/package.json +++ b/package.json @@ -202,6 +202,7 @@ "eslint-plugin-n": "^17.23.1", "form-data": "^4.0.4", "lodash.shuffle": "^4.2.0", + "markdown-magic": "^2.6.1", "memfs": "^4.56.10", "nock": "^14.0.10", "npm-run-all2": "^8.0.4", From f0ca6a92a3add840b9ae08fc552e52b258ab882c Mon Sep 17 00:00:00 2001 From: Hrishikesh Kokate Date: Tue, 26 May 2026 16:44:30 +0530 Subject: [PATCH 10/10] move the flag to toml --- docs/commands/dev.md | 3 --- src/commands/dev/dev.ts | 2 +- src/commands/dev/index.ts | 8 -------- src/commands/dev/types.d.ts | 1 + 4 files changed, 2 insertions(+), 12 deletions(-) diff --git a/docs/commands/dev.md b/docs/commands/dev.md index 011ca013e03..cba068156ce 100644 --- a/docs/commands/dev.md +++ b/docs/commands/dev.md @@ -38,7 +38,6 @@ netlify dev - `port` (*string*) - port of netlify dev - `skip-gitignore` (*boolean*) - skip adding .netlify to .gitignore file - `target-port` (*string*) - port of target app server -- `watch-ignore` (*string*) - exclude a path from file watching. accepts a file path or a directory path; ignored directories exclude their entire subtree and cannot be partially re-included. can be specified multiple times. | Subcommand | description | |:--------------------------- |:-----| @@ -59,8 +58,6 @@ netlify dev --edge-inspect=127.0.0.1:9229 netlify dev --edge-inspect-brk netlify dev --edge-inspect-brk=127.0.0.1:9229 netlify dev --skip-gitignore # skip adding .netlify to .gitignore -netlify dev --watch-ignore src/posts # exclude a content directory from file watching -netlify dev --watch-ignore src/posts --watch-ignore content # exclude multiple directories BROWSER=none netlify dev # disable browser auto opening ``` diff --git a/src/commands/dev/dev.ts b/src/commands/dev/dev.ts index ad62ddd9407..0710f238120 100644 --- a/src/commands/dev/dev.ts +++ b/src/commands/dev/dev.ts @@ -319,7 +319,7 @@ export const dev = async (options: OptionValues, command: BaseCommand) => { accountId, functionsRegistry, repositoryRoot, - watchIgnore: options.watchIgnore ?? [], + watchIgnore: devConfig.watchIgnore ?? [], deployEnvironment, }) diff --git a/src/commands/dev/index.ts b/src/commands/dev/index.ts index 49643940d31..24a00228e6a 100644 --- a/src/commands/dev/index.ts +++ b/src/commands/dev/index.ts @@ -94,12 +94,6 @@ export const createDevCommand = (program: BaseCommand) => { .argParser(validateShortFlagArgs), ) .addOption(new Option('--skip-gitignore', 'skip adding .netlify to .gitignore file')) - .option( - '--watch-ignore ', - 'exclude a path from file watching. accepts a file path or a directory path; ignored directories exclude their entire subtree and cannot be partially re-included. can be specified multiple times.', - (value: string, previous: string[]) => [...previous, value], - [] as string[], - ) .addExamples([ 'netlify dev', 'netlify dev -d public', @@ -112,8 +106,6 @@ export const createDevCommand = (program: BaseCommand) => { 'netlify dev --edge-inspect-brk', 'netlify dev --edge-inspect-brk=127.0.0.1:9229', 'netlify dev --skip-gitignore # skip adding .netlify to .gitignore', - 'netlify dev --watch-ignore src/posts # exclude a content directory from file watching', - 'netlify dev --watch-ignore src/posts --watch-ignore content # exclude multiple directories', 'BROWSER=none netlify dev # disable browser auto opening', ]) .addHelpText('after', () => { diff --git a/src/commands/dev/types.d.ts b/src/commands/dev/types.d.ts index 54b5ac6a15d..a6aada6efce 100644 --- a/src/commands/dev/types.d.ts +++ b/src/commands/dev/types.d.ts @@ -18,4 +18,5 @@ export type DevConfig = NonNullable & { jwtSecret?: string | undefined jwtRolePath?: string | undefined pollingStrategies?: string[] | undefined + watchIgnore?: string[] | undefined }