From 25008df5bfc9eda459d58247f2ad180e6c428c97 Mon Sep 17 00:00:00 2001 From: Val Alexander Date: Sun, 5 Apr 2026 19:51:20 -0500 Subject: [PATCH 1/3] Harden package audit and mark packages side-effect free - Add `sideEffects: false` to published package manifests - Improve security audit tsconfig strictness detection --- packages/core/package.json | 1 + packages/create-markdown/package.json | 1 + packages/mdx/package.json | 1 + packages/preview/package.json | 1 + packages/react/package.json | 1 + scripts/security-audit.sh | 136 ++++++++++++++++++++++++++ 6 files changed, 141 insertions(+) diff --git a/packages/core/package.json b/packages/core/package.json index 84181ab..49f3744 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -21,6 +21,7 @@ "typescript" ], "type": "module", + "sideEffects": false, "main": "./dist/index.cjs", "module": "./dist/index.js", "types": "./dist/index.d.ts", diff --git a/packages/create-markdown/package.json b/packages/create-markdown/package.json index de92a53..caf545a 100644 --- a/packages/create-markdown/package.json +++ b/packages/create-markdown/package.json @@ -25,6 +25,7 @@ "typescript" ], "type": "module", + "sideEffects": false, "main": "./dist/index.cjs", "module": "./dist/index.js", "types": "./dist/index.d.ts", diff --git a/packages/mdx/package.json b/packages/mdx/package.json index bf6d17a..e9ed226 100644 --- a/packages/mdx/package.json +++ b/packages/mdx/package.json @@ -22,6 +22,7 @@ "react" ], "type": "module", + "sideEffects": false, "main": "./dist/index.cjs", "module": "./dist/index.js", "types": "./dist/index.d.ts", diff --git a/packages/preview/package.json b/packages/preview/package.json index 93683c6..03325ee 100644 --- a/packages/preview/package.json +++ b/packages/preview/package.json @@ -23,6 +23,7 @@ "typescript" ], "type": "module", + "sideEffects": false, "main": "./dist/index.cjs", "module": "./dist/index.js", "types": "./dist/index.d.ts", diff --git a/packages/react/package.json b/packages/react/package.json index f0ac4a6..c829891 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -21,6 +21,7 @@ "typescript" ], "type": "module", + "sideEffects": false, "main": "./dist/index.cjs", "module": "./dist/index.js", "types": "./dist/index.d.ts", diff --git a/scripts/security-audit.sh b/scripts/security-audit.sh index 3337959..c53664f 100755 --- a/scripts/security-audit.sh +++ b/scripts/security-audit.sh @@ -464,7 +464,143 @@ step "7/8 TypeScript strict mode" for pkg_dir in packages/*/; do pkg_name="$(basename "$pkg_dir")" +<<<<<<< Updated upstream tsconfig="$pkg_dir/tsconfig.json" +======= + IS_STRICT="$(node - "$tsconfig" <<'NODE' +const fs = require('fs'); +const path = require('path'); + +function stripJsonComments(input) { + const source = input.replace(/^\uFEFF/, ''); + let result = ''; + let inString = false; + let stringQuote = ''; + let escaping = false; + let inLineComment = false; + let inBlockComment = false; + + for (let i = 0; i < source.length; i += 1) { + const char = source[i]; + const next = source[i + 1]; + + if (inLineComment) { + if (char === '\n') { + inLineComment = false; + result += char; + } + continue; + } + + if (inBlockComment) { + if (char === '*' && next === '/') { + inBlockComment = false; + i += 1; + } + continue; + } + + if (inString) { + result += char; + if (escaping) { + escaping = false; + } else if (char === '\\') { + escaping = true; + } else if (char === stringQuote) { + inString = false; + stringQuote = ''; + } + continue; + } + + if ((char === '"' || char === "'")) { + inString = true; + stringQuote = char; + result += char; + continue; + } + + if (char === '/' && next === '/') { + inLineComment = true; + i += 1; + continue; + } + + if (char === '/' && next === '*') { + inBlockComment = true; + i += 1; + continue; + } + + result += char; + } + + return result; +} + +function loadTsconfig(tsconfigPath, visited = new Set()) { + const resolvedPath = path.resolve(tsconfigPath); + if (visited.has(resolvedPath)) { + throw new Error(`Circular tsconfig extends detected: ${resolvedPath}`); + } + + visited.add(resolvedPath); + + const raw = fs.readFileSync(resolvedPath, 'utf8'); + const parsed = JSON.parse(stripJsonComments(raw)); + const baseConfig = parsed.extends + ? loadExtendedTsconfig(parsed.extends, path.dirname(resolvedPath), visited) + : {}; + + return { + ...baseConfig, + ...parsed, + compilerOptions: { + ...(baseConfig.compilerOptions || {}), + ...(parsed.compilerOptions || {}), + }, + }; +} + +function loadExtendedTsconfig(extendsValue, configDir, visited) { + const candidates = []; + + if (extendsValue.startsWith('.')) { + candidates.push(path.resolve(configDir, extendsValue)); + } else if (path.isAbsolute(extendsValue)) { + candidates.push(extendsValue); + } else { + candidates.push(path.resolve(configDir, extendsValue)); + } + + for (const candidate of [...candidates]) { + if (!candidate.endsWith('.json')) { + candidates.push(`${candidate}.json`); + } + } + + for (const candidate of candidates) { + if (fs.existsSync(candidate)) { + return loadTsconfig(candidate, visited); + } + } + + throw new Error(`Unable to resolve extended tsconfig: ${extendsValue}`); +} + +try { + const config = loadTsconfig(process.argv[2]); + if (config.compilerOptions?.strict === true) { + process.stdout.write('true'); + } else { + process.stdout.write('false'); + } +} catch { + process.stdout.write('unknown'); +} +NODE +)" +>>>>>>> Stashed changes if [ ! -f "$tsconfig" ]; then record_warn "$pkg_name: no tsconfig.json found" From ea7257e13a22509562ef6b0477e88a80f4a17de3 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 6 Apr 2026 00:59:22 +0000 Subject: [PATCH 2/3] fix: resolve security-audit merge conflict; mark preview CSS as side effects Remove accidental conflict markers and duplicate strict-check block from security-audit.sh, keeping tsconfig assignment and TypeScript API parsing. Set preview package sideEffects to **/*.css so theme CSS imports are not tree-shaken by bundlers. --- packages/preview/package.json | 2 +- scripts/security-audit.sh | 136 ---------------------------------- 2 files changed, 1 insertion(+), 137 deletions(-) diff --git a/packages/preview/package.json b/packages/preview/package.json index 03325ee..b935f5c 100644 --- a/packages/preview/package.json +++ b/packages/preview/package.json @@ -23,7 +23,7 @@ "typescript" ], "type": "module", - "sideEffects": false, + "sideEffects": ["**/*.css"], "main": "./dist/index.cjs", "module": "./dist/index.js", "types": "./dist/index.d.ts", diff --git a/scripts/security-audit.sh b/scripts/security-audit.sh index c53664f..3337959 100755 --- a/scripts/security-audit.sh +++ b/scripts/security-audit.sh @@ -464,143 +464,7 @@ step "7/8 TypeScript strict mode" for pkg_dir in packages/*/; do pkg_name="$(basename "$pkg_dir")" -<<<<<<< Updated upstream tsconfig="$pkg_dir/tsconfig.json" -======= - IS_STRICT="$(node - "$tsconfig" <<'NODE' -const fs = require('fs'); -const path = require('path'); - -function stripJsonComments(input) { - const source = input.replace(/^\uFEFF/, ''); - let result = ''; - let inString = false; - let stringQuote = ''; - let escaping = false; - let inLineComment = false; - let inBlockComment = false; - - for (let i = 0; i < source.length; i += 1) { - const char = source[i]; - const next = source[i + 1]; - - if (inLineComment) { - if (char === '\n') { - inLineComment = false; - result += char; - } - continue; - } - - if (inBlockComment) { - if (char === '*' && next === '/') { - inBlockComment = false; - i += 1; - } - continue; - } - - if (inString) { - result += char; - if (escaping) { - escaping = false; - } else if (char === '\\') { - escaping = true; - } else if (char === stringQuote) { - inString = false; - stringQuote = ''; - } - continue; - } - - if ((char === '"' || char === "'")) { - inString = true; - stringQuote = char; - result += char; - continue; - } - - if (char === '/' && next === '/') { - inLineComment = true; - i += 1; - continue; - } - - if (char === '/' && next === '*') { - inBlockComment = true; - i += 1; - continue; - } - - result += char; - } - - return result; -} - -function loadTsconfig(tsconfigPath, visited = new Set()) { - const resolvedPath = path.resolve(tsconfigPath); - if (visited.has(resolvedPath)) { - throw new Error(`Circular tsconfig extends detected: ${resolvedPath}`); - } - - visited.add(resolvedPath); - - const raw = fs.readFileSync(resolvedPath, 'utf8'); - const parsed = JSON.parse(stripJsonComments(raw)); - const baseConfig = parsed.extends - ? loadExtendedTsconfig(parsed.extends, path.dirname(resolvedPath), visited) - : {}; - - return { - ...baseConfig, - ...parsed, - compilerOptions: { - ...(baseConfig.compilerOptions || {}), - ...(parsed.compilerOptions || {}), - }, - }; -} - -function loadExtendedTsconfig(extendsValue, configDir, visited) { - const candidates = []; - - if (extendsValue.startsWith('.')) { - candidates.push(path.resolve(configDir, extendsValue)); - } else if (path.isAbsolute(extendsValue)) { - candidates.push(extendsValue); - } else { - candidates.push(path.resolve(configDir, extendsValue)); - } - - for (const candidate of [...candidates]) { - if (!candidate.endsWith('.json')) { - candidates.push(`${candidate}.json`); - } - } - - for (const candidate of candidates) { - if (fs.existsSync(candidate)) { - return loadTsconfig(candidate, visited); - } - } - - throw new Error(`Unable to resolve extended tsconfig: ${extendsValue}`); -} - -try { - const config = loadTsconfig(process.argv[2]); - if (config.compilerOptions?.strict === true) { - process.stdout.write('true'); - } else { - process.stdout.write('false'); - } -} catch { - process.stdout.write('unknown'); -} -NODE -)" ->>>>>>> Stashed changes if [ ! -f "$tsconfig" ]; then record_warn "$pkg_name: no tsconfig.json found" From 08f3d67e4048fed2fee22f087831be2008d8ecac Mon Sep 17 00:00:00 2001 From: Val Alexander Date: Sun, 5 Apr 2026 20:10:05 -0500 Subject: [PATCH 3/3] Resolve merge conflict in security-audit.sh TypeScript strict-mode check Keep the tsconfig variable assignment and the TypeScript API-based parser (getParsedCommandLineOfConfigFile) which natively handles extends resolution. Remove the conflicting custom Node.js tsconfig resolver that was accidentally committed with merge-conflict markers. Co-Authored-By: Claude Opus 4.6 --- scripts/security-audit.sh | 136 -------------------------------------- 1 file changed, 136 deletions(-) diff --git a/scripts/security-audit.sh b/scripts/security-audit.sh index c53664f..3337959 100755 --- a/scripts/security-audit.sh +++ b/scripts/security-audit.sh @@ -464,143 +464,7 @@ step "7/8 TypeScript strict mode" for pkg_dir in packages/*/; do pkg_name="$(basename "$pkg_dir")" -<<<<<<< Updated upstream tsconfig="$pkg_dir/tsconfig.json" -======= - IS_STRICT="$(node - "$tsconfig" <<'NODE' -const fs = require('fs'); -const path = require('path'); - -function stripJsonComments(input) { - const source = input.replace(/^\uFEFF/, ''); - let result = ''; - let inString = false; - let stringQuote = ''; - let escaping = false; - let inLineComment = false; - let inBlockComment = false; - - for (let i = 0; i < source.length; i += 1) { - const char = source[i]; - const next = source[i + 1]; - - if (inLineComment) { - if (char === '\n') { - inLineComment = false; - result += char; - } - continue; - } - - if (inBlockComment) { - if (char === '*' && next === '/') { - inBlockComment = false; - i += 1; - } - continue; - } - - if (inString) { - result += char; - if (escaping) { - escaping = false; - } else if (char === '\\') { - escaping = true; - } else if (char === stringQuote) { - inString = false; - stringQuote = ''; - } - continue; - } - - if ((char === '"' || char === "'")) { - inString = true; - stringQuote = char; - result += char; - continue; - } - - if (char === '/' && next === '/') { - inLineComment = true; - i += 1; - continue; - } - - if (char === '/' && next === '*') { - inBlockComment = true; - i += 1; - continue; - } - - result += char; - } - - return result; -} - -function loadTsconfig(tsconfigPath, visited = new Set()) { - const resolvedPath = path.resolve(tsconfigPath); - if (visited.has(resolvedPath)) { - throw new Error(`Circular tsconfig extends detected: ${resolvedPath}`); - } - - visited.add(resolvedPath); - - const raw = fs.readFileSync(resolvedPath, 'utf8'); - const parsed = JSON.parse(stripJsonComments(raw)); - const baseConfig = parsed.extends - ? loadExtendedTsconfig(parsed.extends, path.dirname(resolvedPath), visited) - : {}; - - return { - ...baseConfig, - ...parsed, - compilerOptions: { - ...(baseConfig.compilerOptions || {}), - ...(parsed.compilerOptions || {}), - }, - }; -} - -function loadExtendedTsconfig(extendsValue, configDir, visited) { - const candidates = []; - - if (extendsValue.startsWith('.')) { - candidates.push(path.resolve(configDir, extendsValue)); - } else if (path.isAbsolute(extendsValue)) { - candidates.push(extendsValue); - } else { - candidates.push(path.resolve(configDir, extendsValue)); - } - - for (const candidate of [...candidates]) { - if (!candidate.endsWith('.json')) { - candidates.push(`${candidate}.json`); - } - } - - for (const candidate of candidates) { - if (fs.existsSync(candidate)) { - return loadTsconfig(candidate, visited); - } - } - - throw new Error(`Unable to resolve extended tsconfig: ${extendsValue}`); -} - -try { - const config = loadTsconfig(process.argv[2]); - if (config.compilerOptions?.strict === true) { - process.stdout.write('true'); - } else { - process.stdout.write('false'); - } -} catch { - process.stdout.write('unknown'); -} -NODE -)" ->>>>>>> Stashed changes if [ ! -f "$tsconfig" ]; then record_warn "$pkg_name: no tsconfig.json found"