diff --git a/packages/notion-downloader/src/config/configuration.ts b/packages/notion-downloader/src/config/configuration.ts deleted file mode 100644 index f3ced08..0000000 --- a/packages/notion-downloader/src/config/configuration.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { exit } from "process" - -import { error, verbose } from "../log" -import { IPlugin } from "../plugins/pluginTypes" -import { handleError } from "../utils/handle-error" -import defaultConfig from "./default.plugin.config" - -export type IPluginsConfig = { - plugins: IPlugin[] -} - -// read the plugins from the config file -// and add them to the map -export async function loadConfigAsync(): Promise { - let config: IPluginsConfig = defaultConfig - try { - // for now, all we have is plugins - config = { - plugins: defaultConfig.plugins, - } - } catch (e: any) { - handleError(e.message) - } - verbose(`Active plugins: [${config.plugins.map((p) => p.name).join(", ")}]`) - return config -} diff --git a/packages/notion-downloader/src/config/default.plugin.config.ts b/packages/notion-downloader/src/config/default.plugin.config.ts deleted file mode 100644 index c0917ef..0000000 --- a/packages/notion-downloader/src/config/default.plugin.config.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { standardCalloutTransformer } from "../plugins/CalloutTransformer" -import { standardColumnListTransformer } from "../plugins/ColumnListTransformer" -import { standardColumnTransformer } from "../plugins/ColumnTransformer" -import { standardEscapeHtmlBlockModifier } from "../plugins/EscapeHtmlBlockModifier" -import { standardHeadingTransformer } from "../plugins/HeadingTransformer" -import { standardTableTransformer } from "../plugins/TableTransformer" -import { standardVideoTransformer } from "../plugins/VideoTransformer" -import { gifEmbed, imgurGifEmbed } from "../plugins/embedTweaks" -import { standardExternalLinkConversion } from "../plugins/externalLinks" -import { standardInternalLinkConversion } from "../plugins/internalLinks" -import { IPluginsConfig } from "./configuration" - -const defaultConfig: IPluginsConfig = { - plugins: [ - // Notion "Block" JSON modifiers - standardEscapeHtmlBlockModifier, - // TODO: Investigate if standardHeadingTransformer should be applied. It causes issues by crreating a non-standard block MD_heading_1, MD_heading_2, etc - // standardHeadingTransformer, // does operations on both the Notion JSON and then later, on the notion to markdown transform - - // Notion to Markdown transformers. Most things get transformed correctly by the notion-to-markdown library, - // but some things need special handling. - // standardColumnTransformer, // STandard column transformer uses notion unofficial API - standardColumnListTransformer, - standardCalloutTransformer, - standardTableTransformer, - standardVideoTransformer, - - // Link modifiers, which are special because they can read metadata from all the pages in order to figure out the correct url - standardInternalLinkConversion, - standardExternalLinkConversion, - - // Regexps plus javascript `import`s that operate on the Markdown output - imgurGifEmbed, - gifEmbed, - ], -} - -export default defaultConfig diff --git a/packages/notion-downloader/src/config/defaultPlugins.ts b/packages/notion-downloader/src/config/defaultPlugins.ts new file mode 100644 index 0000000..60cde77 --- /dev/null +++ b/packages/notion-downloader/src/config/defaultPlugins.ts @@ -0,0 +1,6 @@ +import { standardInternalLinkConversion } from "../plugins/internalLinks" +import { IPlugin } from "../plugins/pluginTypes" + +// TODO: Create a section in the documentation that explains all plugins. + +export const defaultPlugins: IPlugin[] = [standardInternalLinkConversion] diff --git a/packages/notion-downloader/src/config/pluginSchema.ts b/packages/notion-downloader/src/config/pluginSchema.ts new file mode 100644 index 0000000..89dc9c8 --- /dev/null +++ b/packages/notion-downloader/src/config/pluginSchema.ts @@ -0,0 +1,48 @@ +import { z } from "zod" + +import { IPlugin } from "../plugins/pluginTypes" + +const NotionBlockModification = z.object({ + modify: z.function().args(z.any()).returns(z.void()), +}) + +const NotionToMarkdownTransform = z.object({ + type: z.string(), + getStringFromBlock: z + .function() + .args(z.any(), z.any()) + .returns(z.union([z.string(), z.promise(z.string())])), +}) + +const LinkModifier = z.object({ + match: z.instanceof(RegExp), + convert: z.function().args(z.any(), z.string()).returns(z.string()), +}) + +const RegexMarkdownModification = z.object({ + regex: z.instanceof(RegExp), + replacementPattern: z.string().optional(), + getReplacement: z + .function() + .args(z.any(), z.any()) + .returns(z.promise(z.string())) + .optional(), + includeCodeBlocks: z.boolean().optional(), + imports: z.array(z.string()).optional(), +}) + +export const NotionToMdPlugin = z.object({ + name: z.string(), + notionBlockModifications: z.array(NotionBlockModification).optional(), + notionToMarkdownTransforms: z.array(NotionToMarkdownTransform).optional(), + linkModifier: LinkModifier.optional(), + regexMarkdownModifications: z.array(RegexMarkdownModification).optional(), + init: z.function().args(z.any()).returns(z.promise(z.void())).optional(), +}) + +export type NotionToMdPlugin = z.infer + +// Asserting that IPlugin satisfies NotionToMdPlugin +// NOTE: We maintain a zod type for verification and a typescript type for the interface +// this is because some of the arguments are too complex for a zod type, like `NotionBlock` +export const _: NotionToMdPlugin = {} as IPlugin diff --git a/packages/notion-downloader/src/config/plugins.ts b/packages/notion-downloader/src/config/plugins.ts new file mode 100644 index 0000000..7207881 --- /dev/null +++ b/packages/notion-downloader/src/config/plugins.ts @@ -0,0 +1,58 @@ +import { exit } from "process" +import * as Cosmic from "cosmiconfig" +import { TypeScriptLoader } from "cosmiconfig-typescript-loader" + +import { error, verbose } from "../log" +import { IPlugin } from "../plugins/pluginTypes" +import { standardPluginsDict } from "../plugins/standardPlugins" +import { handleError } from "../utils/handle-error" +import { defaultPlugins } from "./defaultPlugins" +import { NotionToMdPlugin } from "./pluginSchema" +import { PluginsConfig } from "./schema" + +// TODO: Remove IPluginsConfig +export type IPluginsConfig = { + plugins: IPlugin[] +} + +function loadOfficialPlugin(pluginName: string): NotionToMdPlugin { + if (!(pluginName in standardPluginsDict)) { + throw new Error(`Official plugin "${pluginName}" not found`) + } + return NotionToMdPlugin.parse(standardPluginsDict[pluginName]) +} + +async function initializePlugin( + plugin: NotionToMdPlugin +): Promise { + if (plugin.init) { + verbose(`Initializing plugin ${plugin.name}...`) + await plugin.init(plugin) + } + return plugin +} + +export async function loadPlugins( + configPlugins: PluginsConfig +): Promise { + let resultingPlugins: IPlugin[] = [] + + try { + const loadedPlugins: IPlugin[] = await Promise.all( + configPlugins.map(async (plugin) => { + if (typeof plugin === "string") { + return await initializePlugin(loadOfficialPlugin(plugin)) + } else { + return await initializePlugin(plugin) + } + }) + ) + + resultingPlugins = defaultPlugins.concat(loadedPlugins) + } catch (e: any) { + handleError(e.message) + } + + verbose(`Active plugins: [${resultingPlugins.map((p) => p.name).join(", ")}]`) + return resultingPlugins +} diff --git a/packages/notion-downloader/src/config/schema.ts b/packages/notion-downloader/src/config/schema.ts index eea23b5..b50526b 100644 --- a/packages/notion-downloader/src/config/schema.ts +++ b/packages/notion-downloader/src/config/schema.ts @@ -2,6 +2,8 @@ import { ObjectType, PageOrDatabase } from "notion-cache-client" import { cacheOptionsSchema } from "notion-tree" import { coerce, z } from "zod" +import { NotionToMdPlugin } from "./pluginSchema" + export const AssetType = z.enum(["image", "file", "video", "pdf", "audio"]) export type AssetType = z.infer @@ -161,6 +163,9 @@ const filterSchema = z.object({ export type Filter = z.infer +export const PluginsConfig = z.array(z.union([z.string(), NotionToMdPlugin])) +export type PluginsConfig = z.infer + export const conversionSchema = z.object({ skip: z.boolean().default(false), overwrite: z.boolean().default(false), @@ -177,6 +182,7 @@ export const conversionSchema = z.object({ namingStrategy: namingStrategyOptionsSchema.default( AllNamingSchemaName.enum.default ), + plugins: PluginsConfig.default([]), }) export const rootObjectTypeSchema = z.enum([ diff --git a/packages/notion-downloader/src/files/FilesManager.ts b/packages/notion-downloader/src/files/FilesManager.ts index 359e87d..2030cb3 100644 --- a/packages/notion-downloader/src/files/FilesManager.ts +++ b/packages/notion-downloader/src/files/FilesManager.ts @@ -1,15 +1,15 @@ -import { ObjectType, ObjectType } from "notion-cache-client" +import { ObjectType } from "notion-cache-client" import { FilepathGroup, mapToAssetType } from "../config/schema" import { NotionObject } from "../notionObjects/NotionObject" import { iNotionAssetObject } from "../notionObjects/objectTypes" import { FileRecord, FileRecordType, FilesMap, FilesMapData } from "./FilesMap" +import { convertMarkdownPath } from "./markdownPathUtils" import { recordMapWithPrefix, recordWithPrefix, toMapDataWithPrefix, } from "./recordPrefixUtils" -import { convertMarkdownPath } from "./markdownPathUtils" type PathType = "base" | "output" | "markdown" @@ -72,7 +72,10 @@ export class FilesManager { if (pathType === "output") { return recordWithPrefix(recordFromDirectory, this.outputDirectories[type]) } else if (pathType === "markdown") { - const record = recordWithPrefix(recordFromDirectory, this.markdownPrefixes[type]) + const record = recordWithPrefix( + recordFromDirectory, + this.markdownPrefixes[type] + ) return { ...record, path: convertMarkdownPath(record.path), diff --git a/packages/notion-downloader/src/index.ts b/packages/notion-downloader/src/index.ts index ff65041..97a18a8 100644 --- a/packages/notion-downloader/src/index.ts +++ b/packages/notion-downloader/src/index.ts @@ -12,6 +12,10 @@ process.on("SIGTERM", () => process.exit(0)) export { NotionPullOptionsInput as Config } +// TODO: Cleanup unnecesasry exports +export { IPlugin } from "./plugins/pluginTypes" +export { NotionBlock } from "./types" + async function main() { const packageInfo = await getPackageInfo() diff --git a/packages/notion-downloader/src/notionPull.ts b/packages/notion-downloader/src/notionPull.ts index 151399b..96c07f0 100644 --- a/packages/notion-downloader/src/notionPull.ts +++ b/packages/notion-downloader/src/notionPull.ts @@ -13,7 +13,7 @@ import { import { NotionToMarkdown } from "notion-to-md" import { NotionObjectTree, downloadNotionObjectTree } from "notion-tree" -import { IPluginsConfig, loadConfigAsync } from "./config/configuration" +import { loadPlugins } from "./config/plugins" import { FilepathGroup, NotionPullOptions, @@ -37,7 +37,7 @@ import { endGroup, error, group, info } from "./log" import { NotionPage } from "./notionObjects/NotionPage" import { filterTree } from "./objectTree/filterTree" import { convertInternalUrl } from "./plugins/internalLinks" -import { IPluginContext } from "./plugins/pluginTypes" +import { IPlugin, IPluginContext } from "./plugins/pluginTypes" import { readOrDownloadNewAssets, saveNewAssets, @@ -253,13 +253,11 @@ export async function notionPull(options: NotionPullOptions): Promise { info(`Found ${objectsTree.getPages().length} pages`) info(`Found ${pagesToOutput.length} to output`) - const pluginsConfig = await loadConfigAsync() const notionToMarkdown = new NotionToMarkdown({ notionClient: cachedNotionClient, }) await outputPages( options, - pluginsConfig, pagesToOutput, cachedNotionClient, notionToMarkdown, @@ -399,7 +397,6 @@ function getPagesToOutput( async function outputPages( options: NotionPullOptions, - config: IPluginsConfig, pages: Array, client: Client, notionToMarkdown: NotionToMarkdown, @@ -412,6 +409,7 @@ async function outputPages( notionToMarkdown, filesManager ) + const plugins = await loadPlugins(options.conversion.plugins) for (const page of pages) { const mdPathWithRoot = filesManager.get( @@ -419,7 +417,7 @@ async function outputPages( ObjectType.enum.page, page.id )?.path - const markdown = await getMarkdownForPage(config, context, page) + const markdown = await getMarkdownForPage(plugins, context, page) writePage(markdown, mdPathWithRoot) } diff --git a/packages/notion-downloader/src/plugins/externalLinks.ts b/packages/notion-downloader/src/plugins/externalLinks.ts index a116d3c..15ac377 100644 --- a/packages/notion-downloader/src/plugins/externalLinks.ts +++ b/packages/notion-downloader/src/plugins/externalLinks.ts @@ -19,7 +19,7 @@ export const standardExternalLinkConversion: IPlugin = { if (label === "bookmark") { const replacement = `[${url}](${url})` warning( - `[standardExternalLinkConversion] Found Notion "Bookmark" link. In Notion this would show as an embed. The best docu-notion can do at the moment is replace "Bookmark" with the actual URL: ${replacement}` + `[standardExternalLinkConversion] Found Notion "Bookmark" link. In Notion this would show as an embed. We replace "Bookmark" with the actual URL: ${replacement}` ) return replacement } diff --git a/packages/notion-downloader/src/plugins/pluginTestRun.ts b/packages/notion-downloader/src/plugins/pluginTestRun.ts index 42ea638..20888ce 100644 --- a/packages/notion-downloader/src/plugins/pluginTestRun.ts +++ b/packages/notion-downloader/src/plugins/pluginTestRun.ts @@ -2,7 +2,7 @@ import { Client } from "@notionhq/client" import { PageObjectResponse } from "@notionhq/client/build/src/api-endpoints" import { NotionToMarkdown } from "notion-to-md" -import { IPluginsConfig } from "../config/configuration" +import { IPluginsConfig } from "../config/plugins" import { defaultPullOptions, parsePathFileOptions } from "../config/schema" import { FilesManager } from "../files/FilesManager" import { FilesMap } from "../files/FilesMap" diff --git a/packages/notion-downloader/src/plugins/pluginTypes.ts b/packages/notion-downloader/src/plugins/pluginTypes.ts index afc5b12..577c5ee 100644 --- a/packages/notion-downloader/src/plugins/pluginTypes.ts +++ b/packages/notion-downloader/src/plugins/pluginTypes.ts @@ -3,8 +3,8 @@ import { ListBlockChildrenResponseResult } from "notion-to-md/build/types" import { NotionPullOptions } from "../config/schema" import { FilesManager } from "../files/FilesManager" -import { ICounts, NotionBlock } from "../index" import { NotionPage } from "../notionObjects/NotionPage" +import { ICounts, NotionBlock } from "../types" type linkConversionFunction = ( context: IPluginContext, diff --git a/packages/notion-downloader/src/plugins/standardPlugins.ts b/packages/notion-downloader/src/plugins/standardPlugins.ts new file mode 100644 index 0000000..01d92e3 --- /dev/null +++ b/packages/notion-downloader/src/plugins/standardPlugins.ts @@ -0,0 +1,40 @@ +import { standardCalloutTransformer } from "./CalloutTransformer" +import { standardColumnListTransformer } from "./ColumnListTransformer" +import { standardColumnTransformer } from "./ColumnTransformer" +import { standardEscapeHtmlBlockModifier } from "./EscapeHtmlBlockModifier" +import { standardHeadingTransformer } from "./HeadingTransformer" +import { standardTableTransformer } from "./TableTransformer" +import { standardVideoTransformer } from "./VideoTransformer" +import { gifEmbed, imgurGifEmbed } from "./embedTweaks" +import { standardExternalLinkConversion } from "./externalLinks" +import { standardInternalLinkConversion } from "./internalLinks" +import { IPlugin } from "./pluginTypes" + +// TODO: Create a section in the documentation that explains all plugins. + +const standardPlugins = [ + // Notion "Block" JSON modifiers + standardEscapeHtmlBlockModifier, + standardHeadingTransformer, // does operations on both the Notion JSON and then later, on the notion to markdown transform + + // Notion to Markdown transformers. Most things get transformed correctly by the notion-to-markdown library, + // but some things need special handling. + standardColumnTransformer, // STandard column transformer uses notion unofficial API + standardColumnListTransformer, + standardCalloutTransformer, + standardTableTransformer, + + standardVideoTransformer, + + // Link modifiers, which are special because they can read metadata from all the pages in order to figure out the correct url + standardInternalLinkConversion, + standardExternalLinkConversion, + + // Regexps plus javascript `import`s that operate on the Markdown output + imgurGifEmbed, + gifEmbed, +] + +export const standardPluginsDict: Record = Object.fromEntries( + standardPlugins.map((plugin) => [plugin.name, plugin]) +) diff --git a/packages/notion-downloader/src/transformMarkdown.ts b/packages/notion-downloader/src/transformMarkdown.ts index 463da5b..1916fed 100644 --- a/packages/notion-downloader/src/transformMarkdown.ts +++ b/packages/notion-downloader/src/transformMarkdown.ts @@ -1,17 +1,18 @@ import chalk from "chalk" -import { IPluginsConfig } from "./config/configuration" +import { IPluginsConfig } from "./config/plugins" import { error, info, logDebug, logDebugFn, verbose, warning } from "./log" import { getFileUrl } from "./notionObjects/NotionFile" import { NotionPage } from "./notionObjects/NotionPage" import { + IPlugin, IPluginContext, IRegexMarkdownModification, } from "./plugins/pluginTypes" import { NotionBlock } from "./types" export async function getMarkdownForPage( - config: IPluginsConfig, + plugins: IPlugin[], context: IPluginContext, page: NotionPage ): Promise { @@ -21,7 +22,7 @@ export async function getMarkdownForPage( logDebugFn("markdown from page", () => JSON.stringify(blocks, null, 2)) - const body = await getMarkdownFromNotionBlocks(context, config, blocks) + const body = await getMarkdownFromNotionBlocks(context, plugins, blocks) const frontmatter = getFrontMatter(page) // todo should be a plugin return `${frontmatter}\n${body}` } @@ -29,26 +30,26 @@ export async function getMarkdownForPage( // this is split off from getMarkdownForPage so that unit tests can provide the block contents export async function getMarkdownFromNotionBlocks( context: IPluginContext, - config: IPluginsConfig, + plugins: IPlugin[], blocks: Array ): Promise { // changes to the blocks we get from notion API - doNotionBlockTransforms(blocks, config) + doNotionBlockTransforms(blocks, plugins) // overrides for the default notion-to-markdown conversions - registerNotionToMarkdownCustomTransforms(config, context) + registerNotionToMarkdownCustomTransforms(plugins, context) // the main conversion to markdown, using the notion-to-md library let markdown = await doNotionToMarkdown(context, blocks) // ? // corrections to links after they are converted to markdown, // with access to all the pages we've seen - markdown = doLinkFixes(context, markdown, config) + markdown = doLinkFixes(context, markdown, plugins) //console.log("markdown after link fixes", markdown); // simple regex-based tweaks. These are usually related to docusaurus - const body = await doTransformsOnMarkdown(context, config, markdown) + const body = await doTransformsOnMarkdown(context, plugins, markdown) // console.log("markdown after regex fixes", markdown); // console.log("body after regex", body); @@ -62,10 +63,10 @@ export async function getMarkdownFromNotionBlocks( // operations on notion blocks before they are converted to markdown function doNotionBlockTransforms( blocks: Array, - config: IPluginsConfig + plugins: IPlugin[] ) { for (const block of blocks) { - config.plugins.forEach((plugin) => { + plugins.forEach((plugin) => { if (plugin.notionBlockModifications) { plugin.notionBlockModifications.forEach((transform) => { logDebug("transforming block with plugin", plugin.name) @@ -78,10 +79,10 @@ function doNotionBlockTransforms( async function doTransformsOnMarkdown( context: IPluginContext, - config: IPluginsConfig, + plugins: IPlugin[], input: string ) { - const regexMods: IRegexMarkdownModification[] = config.plugins + const regexMods: IRegexMarkdownModification[] = plugins .filter((plugin) => !!plugin.regexMarkdownModifications) .map((plugin) => { const mods = plugin.regexMarkdownModifications! @@ -172,7 +173,7 @@ async function doNotionToMarkdown( function doLinkFixes( context: IPluginContext, markdown: string, - config: IPluginsConfig + plugins: IPlugin[] ): string { // This regex matches markdown links that are not preceded by a ! character (not images) const linkRegExp = getLinkMatchingRegex() @@ -196,7 +197,7 @@ function doLinkFixes( // We only use the first plugin that matches and makes a change to the link. // Enhance: we could take the time to see if multiple plugins match, and // and point this out in verbose logging mode. - config.plugins.some((plugin) => { + plugins.some((plugin) => { if (!plugin.linkModifier) return false if (plugin.linkModifier.match.exec(originalLinkMarkdown) === null) { verbose(`plugin "${plugin.name}" did not match this url`) @@ -228,10 +229,10 @@ export function getLinkMatchingRegex() { // overrides for the conversions that notion-to-md does function registerNotionToMarkdownCustomTransforms( - config: IPluginsConfig, + plugins: IPlugin[], pluginContext: IPluginContext ) { - config.plugins.forEach((plugin) => { + plugins.forEach((plugin) => { if (plugin.notionToMarkdownTransforms) { plugin.notionToMarkdownTransforms.forEach((transform) => { logDebug( diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5cf1bc6..b78491f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -419,6 +419,9 @@ importers: cosmiconfig: specifier: 9.0.0 version: 9.0.0(typescript@4.9.5) + cosmiconfig-typescript-loader: + specifier: ^5.0.0 + version: 5.0.0(@types/node@20.14.10)(cosmiconfig@9.0.0(typescript@4.9.5))(typescript@4.9.5) diff: specifier: ^5.1.0 version: 5.2.0 @@ -512,7 +515,7 @@ importers: version: 4.4.1 tsup: specifier: ^6.6.3 - version: 6.7.0(postcss@8.4.39)(ts-node@10.9.2(@types/node@20.12.12)(typescript@4.9.5))(typescript@4.9.5) + version: 6.7.0(postcss@8.4.39)(ts-node@10.9.2(@types/node@20.14.10)(typescript@4.9.5))(typescript@4.9.5) type-fest: specifier: ^3.8.0 version: 3.13.1 @@ -3393,6 +3396,14 @@ packages: core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + cosmiconfig-typescript-loader@5.0.0: + resolution: {integrity: sha512-+8cK7jRAReYkMwMiG+bxhcNKiHJDM6bR9FD/nGBXOWdMLuYawjF5cGrtLilJ+LGd3ZjCXnJjR5DkfWPoIVlqJA==} + engines: {node: '>=v16'} + peerDependencies: + '@types/node': '*' + cosmiconfig: '>=8.2' + typescript: '>=4' + cosmiconfig@9.0.0: resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==} engines: {node: '>=14'} @@ -9916,6 +9927,13 @@ snapshots: core-util-is@1.0.3: {} + cosmiconfig-typescript-loader@5.0.0(@types/node@20.14.10)(cosmiconfig@9.0.0(typescript@4.9.5))(typescript@4.9.5): + dependencies: + '@types/node': 20.14.10 + cosmiconfig: 9.0.0(typescript@4.9.5) + jiti: 1.21.0 + typescript: 4.9.5 + cosmiconfig@9.0.0(typescript@4.9.5): dependencies: env-paths: 2.2.1 @@ -12706,6 +12724,14 @@ snapshots: postcss: 8.4.39 ts-node: 10.9.2(@types/node@20.12.12)(typescript@4.9.5) + postcss-load-config@3.1.4(postcss@8.4.39)(ts-node@10.9.2(@types/node@20.14.10)(typescript@4.9.5)): + dependencies: + lilconfig: 2.1.0 + yaml: 1.10.2 + optionalDependencies: + postcss: 8.4.39 + ts-node: 10.9.2(@types/node@20.14.10)(typescript@4.9.5) + postcss-load-config@4.0.2(postcss@8.4.39)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2)): dependencies: lilconfig: 3.1.1 @@ -13798,6 +13824,25 @@ snapshots: yn: 3.1.1 optional: true + ts-node@10.9.2(@types/node@20.14.10)(typescript@4.9.5): + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.11 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 20.14.10 + acorn: 8.12.1 + acorn-walk: 8.3.4 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 4.9.5 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + optional: true + ts-pattern@5.4.0: {} tsconfck@3.0.3(typescript@5.6.2): @@ -13844,6 +13889,29 @@ snapshots: - supports-color - ts-node + tsup@6.7.0(postcss@8.4.39)(ts-node@10.9.2(@types/node@20.14.10)(typescript@4.9.5))(typescript@4.9.5): + dependencies: + bundle-require: 4.1.0(esbuild@0.17.19) + cac: 6.7.14 + chokidar: 3.6.0 + debug: 4.3.4 + esbuild: 0.17.19 + execa: 5.1.1 + globby: 11.1.0 + joycon: 3.1.1 + postcss-load-config: 3.1.4(postcss@8.4.39)(ts-node@10.9.2(@types/node@20.14.10)(typescript@4.9.5)) + resolve-from: 5.0.0 + rollup: 3.29.4 + source-map: 0.8.0-beta.0 + sucrase: 3.35.0 + tree-kill: 1.2.2 + optionalDependencies: + postcss: 8.4.39 + typescript: 4.9.5 + transitivePeerDependencies: + - supports-color + - ts-node + tsutils@3.21.0(typescript@5.6.2): dependencies: tslib: 1.14.1