@ignsg/vite-build-tools
Advanced tools
Comparing version 0.0.1 to 0.1.0
{ | ||
"name": "@ignsg/vite-build-tools", | ||
"version": "0.0.1", | ||
"version": "0.1.0", | ||
"type": "module", | ||
@@ -20,17 +20,18 @@ "publishConfig": { | ||
"./vite-plugin-entries": { | ||
"types": "./dist/vite-plugin-entries.d.ts", | ||
"import": "./dist/vite-plugin-entries.es.js", | ||
"default": "./dist/vite-plugin-entries.es.js" | ||
"types": "./dist/vite-plugin-entries-049bea89.d.ts", | ||
"import": "./dist/vite-plugin-entries-049bea89.es.js", | ||
"default": "./dist/vite-plugin-entries-049bea89.es.js" | ||
}, | ||
"./vite-plugin-dts": { | ||
"types": "./dist/vite-plugin-dts.d.ts", | ||
"import": "./dist/vite-plugin-dts.es.js", | ||
"default": "./dist/vite-plugin-dts.es.js" | ||
"types": "./dist/vite-plugin-dts-90a5d06f.d.ts", | ||
"import": "./dist/vite-plugin-dts-90a5d06f.es.js", | ||
"default": "./dist/vite-plugin-dts-90a5d06f.es.js" | ||
} | ||
}, | ||
"devDependencies": { | ||
"rollup": "3.2.5", | ||
"vite": "3.2.2", | ||
"vitest": "0.25.0" | ||
"@workspace/root": "workspace:*", | ||
"rollup": "3.3.0", | ||
"vite": "3.2.3", | ||
"vitest": "0.25.2" | ||
} | ||
} |
@@ -5,2 +5,3 @@ import { LibraryFormats } from "vite"; | ||
| { | ||
/** If true will mark this entry as the main entry - it can be imported using just you library's package name */ | ||
isMain: boolean; | ||
@@ -11,2 +12,16 @@ exportPath?: never; | ||
isMain?: never; | ||
/** Relative path (with or without `./` prefix) that will be used as the path inside package.json's exports field and will be available for import by your library's users. | ||
* | ||
* For example given the default `outDir: "./dist"` and an `outputPath: "./file" would generate: | ||
* ```json | ||
* { | ||
* "exports": { | ||
* "./file": { | ||
* "import": "./dist/file.js", | ||
* ... | ||
* } | ||
* } | ||
* } | ||
* ``` | ||
*/ | ||
exportPath: string; | ||
@@ -16,18 +31,60 @@ }; | ||
export interface EntryOptions { | ||
/** The path to the source code of the entry */ | ||
sourcePath: string; | ||
/** Relative path ending with the name of the output (without an extension) that will be generated inside of the directory specified by `outDir`. | ||
* | ||
* **Default: `sourcePath`** | ||
* | ||
* For example, given the default `outDir: "./dist"`: | ||
* - An `outputPath: "./file"` would generate `./dist/file.js` | ||
* - An `outputPath: "./folder/file"` would generate `./dist/folder/file.js` | ||
*/ | ||
outputPath?: string; | ||
/** Contains either `{ isMain: true }` or `{ exportPath: "..." }` | ||
* | ||
* **Default: `{ exportPath: outputPath }`** | ||
* | ||
* @see {@linkcode EntryOptionsDotExports.exportPath} | ||
*/ | ||
exports?: EntryOptionsDotExports; | ||
} | ||
export interface Entry { | ||
sourcePath: string; | ||
outputPath: string; | ||
exportPath: string; | ||
} | ||
export interface PluginOptions { | ||
/** If true supresses the log messages about updating the user's package.json. | ||
* | ||
* **Default: `false`** */ | ||
silent?: boolean; | ||
/** Array of package.json keys before which the plugin should place the generated entries. First match will be used. | ||
* | ||
* **Default: `["export", "dependencies", "devDependencies"]`** | ||
* | ||
* For example to generate "exports" before "scripts" you can specify ["scripts"] and the plugin will adjust the package.json to be: | ||
* ```json | ||
* { | ||
* ..., | ||
* "exports": ..., | ||
* "scripts": ..., | ||
* ..., | ||
* } | ||
* ``` | ||
*/ | ||
packageJsonInjectionPoints?: string[]; | ||
/** If provided and an entry in `entries` does not contain an explicit `outputPath` will strip this prefix when generating the output files in your output folder. | ||
* | ||
* For example if you have your source files in `src` and your `outDir` set to `./dist` an entry such as: | ||
* ```json | ||
* { | ||
* sourcePath: "./src/index.ts" | ||
* } | ||
* ``` | ||
* would generate its output as `./dist/src/index.es.js`. With `sourceRoot: "./src"` it will instead generate an output as `./dist/index.es.js`. | ||
*/ | ||
sourceRoot?: string; | ||
/** List of formats you want to have generated */ | ||
formats: LibraryFormats[]; | ||
/** List of {@linkcode EntryOptions}'s that should be used an entry points - each represents a file/export that can be consumed by your library's user */ | ||
entries: EntryOptions[]; | ||
} | ||
/** @private */ | ||
export interface PackageJsonExports { | ||
@@ -40,1 +97,8 @@ [exportPath: string]: { | ||
} | ||
/** @private */ | ||
export interface Entry { | ||
sourcePath: string; | ||
outputPath: string; | ||
exportPath: string; | ||
} |
import path from "node:path"; | ||
import { cwd } from "node:process"; | ||
import { UserConfig } from "vite"; | ||
import { ResolvedConfig, UserConfig } from "vite"; | ||
export function getPackageJSONPath(config: UserConfig) { | ||
if (config.root) return path.join(config.root, "package.json"); | ||
import { Entry, EntryOptions, PluginOptions } from "./types.js"; | ||
return "./package.json"; | ||
export function getPackageJSONPath({ config }: { config: ResolvedConfig | UserConfig }) { | ||
return path.join(config.root ?? cwd(), "package.json"); | ||
} | ||
export function addRelativeDot(path: string) { | ||
if (path.startsWith("./")) return path; | ||
return `./${path}`; | ||
} | ||
export function removeRelativeDot(path: string) { | ||
if (!path.startsWith("./")) return path; | ||
return path.replace(/^\.\//, ""); | ||
} | ||
/** Normalize input entry options and their paths into a standardized format | ||
* | ||
* sourcePath: absolute path to the entry's source file | ||
* | ||
* - outputPath: | ||
* - if `entryOptions.outputPath` not provided an extensionless path based on the sourcePath relative to the root directory | ||
* - if provided must be a relative extensionless path | ||
* | ||
* - exportPath: | ||
* - if `entryOptions.exports.isMain` is true, "." | ||
* - if `entryOptions.exports.exportPath` not provided the `./${outputPath}` | ||
* - if provided must be a relative extensionless path | ||
*/ | ||
export function createReduceEntryOptionsToEntries({ | ||
config, | ||
options, | ||
}: { | ||
config: UserConfig; | ||
options: PluginOptions; | ||
}) { | ||
return function reduceEntryOptionsToEntries( | ||
result: Record<string, Entry>, | ||
entryOptions: EntryOptions, | ||
index: number, | ||
): Record<string, Entry> { | ||
const sourcePath = path.isAbsolute(entryOptions.sourcePath) | ||
? entryOptions.sourcePath | ||
: path.join(config.root ?? cwd(), entryOptions.sourcePath); | ||
const sourcePathChunks = path.parse(sourcePath); | ||
const outputPath = | ||
entryOptions.outputPath ?? | ||
path.join( | ||
path.relative( | ||
path.join(config.root ?? cwd(), options.sourceRoot ?? ""), | ||
sourcePathChunks.dir, | ||
), | ||
sourcePathChunks.name, | ||
); | ||
const exportPath = entryOptions.exports?.exportPath ?? outputPath; | ||
const entry: Entry = { | ||
sourcePath, | ||
outputPath: removeRelativeDot(outputPath), | ||
exportPath: entryOptions.exports?.isMain ? "." : addRelativeDot(exportPath), | ||
}; | ||
return { | ||
...result, | ||
[index.toString()]: entry, | ||
}; | ||
}; | ||
} |
import * as fs from "node:fs"; | ||
import * as path from "node:path"; | ||
import { cwd } from "node:process"; | ||
import { Plugin, UserConfig } from "vite"; | ||
import { Plugin, ResolvedConfig } from "vite"; | ||
import { Entry, PackageJsonExports, PluginOptions } from "./types.js"; | ||
import { getPackageJSONPath } from "./utilities.js"; | ||
import { reduceEntryOptionsToEntries } from "./vite-plugin-entries.js"; | ||
import { createReduceEntryOptionsToEntries, getPackageJSONPath } from "./utilities.js"; | ||
export type { PluginOptions }; | ||
type InputToOutput = Map<string, string>; | ||
interface PackageJsonTypeExports { | ||
@@ -18,3 +20,13 @@ [exportPath: string]: { | ||
function createReduceEntriesToPackageExports({ outDir }: { outDir?: string }) { | ||
function createReduceEntriesToPackageExports({ | ||
inputToOutput, | ||
buildConfig, | ||
}: { | ||
inputToOutput: InputToOutput; | ||
buildConfig: ResolvedConfig["build"]; | ||
}) { | ||
function outputToExportPath(output: string) { | ||
return "./" + path.join(buildConfig.outDir, output); | ||
} | ||
return function reduceEntriesToPackageExports( | ||
@@ -24,2 +36,8 @@ result: PackageJsonTypeExports, | ||
): PackageJsonTypeExports { | ||
const output = inputToOutput.get(entry.sourcePath); | ||
if (!output) { | ||
throw new Error(`Cannot find actual .dts output path for entry source "${entry.sourcePath}"`); | ||
} | ||
return { | ||
@@ -29,3 +47,3 @@ ...result, | ||
[entry.exportPath]: { | ||
types: "./" + path.join(outDir ?? "", `${entry.outputPath}.d.ts`), | ||
types: outputToExportPath(output), | ||
}, | ||
@@ -36,5 +54,7 @@ }; | ||
function createReduceExistingExportsEntriesToTypedPackageExports( | ||
entryTypeExports: Map<string, { types: string }>, | ||
) { | ||
function createReduceExistingExportsEntriesToTypedPackageExports({ | ||
entryTypeExports, | ||
}: { | ||
entryTypeExports: Map<string, { types: string }>; | ||
}) { | ||
return function reduceExistingExportsEntriesToTypedPackageExports( | ||
@@ -62,7 +82,7 @@ result: PackageJsonExports, | ||
export default async function dtsPlugin(opts: PluginOptions): Promise<Plugin> { | ||
export default async function dtsPlugin(options: PluginOptions): Promise<Plugin> { | ||
let entries: Map<string, Entry>; | ||
let config: UserConfig; | ||
let config: ResolvedConfig; | ||
let bundleGenerated = false; | ||
const inputToOutput: InputToOutput = new Map(); | ||
@@ -72,44 +92,60 @@ return { | ||
config(userConfig) { | ||
entries = new Map(Object.entries(opts.entries.reduce(reduceEntryOptionsToEntries, {}))); | ||
entries = new Map( | ||
Object.entries( | ||
options.entries.reduce( | ||
createReduceEntryOptionsToEntries({ config: userConfig, options }), | ||
{}, | ||
), | ||
), | ||
); | ||
}, | ||
configResolved(userConfig) { | ||
config = userConfig; | ||
}, | ||
generateBundle(options) { | ||
if (bundleGenerated) return; | ||
generateBundle(outputOptions, bundle) { | ||
if (outputOptions.format !== options.formats[0]) return; | ||
for (const { outputPath, sourcePath } of entries.values()) { | ||
if (!options.dir) { | ||
throw new Error(""); | ||
} | ||
for (const assetOrChunk of Object.values(bundle)) { | ||
if (assetOrChunk.type !== "chunk") continue; | ||
if (!assetOrChunk.facadeModuleId) continue; | ||
if (!assetOrChunk.isEntry) continue; | ||
const file = fs.readFileSync(sourcePath).toString(); | ||
const outputDir = path.parse(outputPath).dir; | ||
const file = fs.readFileSync(assetOrChunk.facadeModuleId).toString(); | ||
const hasDefaultExport = /^(export default |export \{[^}]+? as default\s*[,}])/m.test(file); | ||
const relativePath = path.relative( | ||
path.resolve(path.join(options.dir, outputDir)), | ||
path.join(path.parse(sourcePath).dir, `${path.parse(sourcePath).name}.js`), | ||
path.join(config.root ?? cwd(), assetOrChunk.fileName), | ||
assetOrChunk.facadeModuleId, | ||
); | ||
const hasDefaultExport = /^(export default |export \{[^}]+? as default\s*[,}])/m.test(file); | ||
const relativeJsPath = relativePath.replace(/\.ts(x)?$/, ".js$1"); | ||
const source = | ||
`export * from "${relativePath}"` + | ||
(hasDefaultExport ? `\nexport {default} from "${relativePath}"` : ``); | ||
`export * from "${relativeJsPath}"` + | ||
(hasDefaultExport ? `\nexport {default} from "${relativeJsPath}"` : ``); | ||
const output = path.join( | ||
path.parse(assetOrChunk.fileName).dir, | ||
`${path.parse(assetOrChunk.fileName).name.replace(/\.(es|cjs)$/, "")}.d.ts`, | ||
); | ||
inputToOutput.set(assetOrChunk.facadeModuleId, output); | ||
this.emitFile({ | ||
type: "asset", | ||
fileName: path.join(outputDir, `${path.parse(outputPath).name}.d.ts`), | ||
fileName: output, | ||
source, | ||
}); | ||
} | ||
bundleGenerated = true; | ||
}, | ||
closeBundle() { | ||
const packageDetails = JSON.parse(fs.readFileSync(getPackageJSONPath(config)).toString()); | ||
const packageDetails = JSON.parse(fs.readFileSync(getPackageJSONPath({ config })).toString()); | ||
const entryTypeExports = new Map( | ||
Object.entries( | ||
Array.from(entries.values()).reduce( | ||
createReduceEntriesToPackageExports({ ...config.build }), | ||
createReduceEntriesToPackageExports({ inputToOutput, buildConfig: config.build }), | ||
{}, | ||
@@ -120,11 +156,24 @@ ), | ||
this.warn("Adding type definitions to the `exports` field in your package.json ✅"); | ||
if ( | ||
!packageDetails["#exports"] || | ||
!packageDetails["#exports"].startsWith("Generated automatically") | ||
) { | ||
throw new Error( | ||
`Couldn't find the auto-generated marker #exports - add the vite-plugin-entries plugin before vite-plugin-dts (it must run after it)`, | ||
); | ||
} | ||
if (!options.silent) | ||
this.warn("Adding type definitions to the `exports` field in your package.json ✅"); | ||
packageDetails.exports = Array.from( | ||
Object.entries(packageDetails.exports as PackageJsonExports), | ||
).reduce(createReduceExistingExportsEntriesToTypedPackageExports(entryTypeExports), {}); | ||
).reduce(createReduceExistingExportsEntriesToTypedPackageExports({ entryTypeExports }), {}); | ||
fs.writeFileSync(getPackageJSONPath(config), JSON.stringify(packageDetails, undefined, 4)); | ||
fs.writeFileSync( | ||
getPackageJSONPath({ config }), | ||
JSON.stringify(packageDetails, undefined, 4), | ||
); | ||
}, | ||
}; | ||
} |
import * as fs from "node:fs"; | ||
import * as path from "node:path"; | ||
import { BuildOptions, LibraryFormats, Plugin, UserConfig } from "vite"; | ||
import { BuildOptions, LibraryFormats, Plugin, ResolvedConfig } from "vite"; | ||
import { Entry, EntryOptions, PackageJsonExports, PluginOptions } from "./types.js"; | ||
import { getPackageJSONPath } from "./utilities.js"; | ||
import { Entry, PackageJsonExports, PluginOptions } from "./types.js"; | ||
import { createReduceEntryOptionsToEntries, getPackageJSONPath } from "./utilities.js"; | ||
@@ -16,2 +16,4 @@ export type { PluginOptions }; | ||
type InputToOutput = Map<string, Partial<Record<LibraryFormats, string>>>; | ||
function reduceEntryMapToInput(result: InputOptions, [id, entry]: [string, Entry]): InputOptions { | ||
@@ -26,8 +28,18 @@ return { | ||
function createReduceEntriesToPackageExports({ | ||
outDir, | ||
formats, | ||
config, | ||
options, | ||
inputToOutput, | ||
}: { | ||
outDir?: string; | ||
formats: LibraryFormats[]; | ||
config: ResolvedConfig; | ||
options: PluginOptions; | ||
inputToOutput: InputToOutput; | ||
}) { | ||
function outputToExportPath(output: string | undefined, entry: Entry) { | ||
if (!output) { | ||
throw new Error(`Cannot find actual output path for entry source "${entry.sourcePath}"`); | ||
} | ||
return "./" + path.join(config.build.outDir, output); | ||
} | ||
return function reduceEntriesToPackageExports( | ||
@@ -37,5 +49,10 @@ result: PackageJsonExports, | ||
): PackageJsonExports { | ||
const esmExport = "./" + path.join(outDir ?? "", `${entry.outputPath}.es.js`); | ||
const cjsExport = "./" + path.join(outDir ?? "", "cjs", `${entry.outputPath}.cjs.js`); | ||
const esmExport = options.formats.includes("es") | ||
? outputToExportPath(inputToOutput.get(entry.sourcePath)?.es, entry) | ||
: undefined; | ||
const cjsExport = options.formats.includes("cjs") | ||
? outputToExportPath(inputToOutput.get(entry.sourcePath)?.cjs, entry) | ||
: undefined; | ||
return { | ||
@@ -45,8 +62,8 @@ ...result, | ||
[entry.exportPath]: { | ||
...(formats.includes("es") ? { import: esmExport } : undefined), | ||
...(formats.includes("cjs") ? { require: cjsExport } : undefined), | ||
...(esmExport ? { import: esmExport } : undefined), | ||
...(cjsExport ? { require: cjsExport } : undefined), | ||
...(formats[0] === "es" | ||
...(options.formats[0] === "es" | ||
? { default: esmExport } | ||
: formats[0] === "cjs" | ||
: options.formats[0] === "cjs" | ||
? { default: cjsExport } | ||
@@ -59,31 +76,14 @@ : undefined), | ||
export function reduceEntryOptionsToEntries( | ||
result: Record<string, Entry>, | ||
entryOptions: EntryOptions, | ||
index: number, | ||
): Record<string, Entry> { | ||
const outputPath = entryOptions.outputPath ?? path.parse(entryOptions.sourcePath).name; | ||
const entry: Entry = { | ||
sourcePath: entryOptions.sourcePath, | ||
outputPath, | ||
exportPath: entryOptions.exports?.isMain | ||
? "." | ||
: `./${entryOptions.exports?.exportPath ?? outputPath}`, | ||
}; | ||
return { | ||
...result, | ||
[index.toString()]: entry, | ||
}; | ||
} | ||
function createMapFormatToOutputOptions( | ||
isEsModule: boolean, | ||
outputOptions: RollupOutputOptions, | ||
entries: Map<string, Entry>, | ||
) { | ||
function createMapFormatToOutputOptions({ | ||
isEsModule, | ||
outputConfig, | ||
entries, | ||
}: { | ||
isEsModule: boolean; | ||
outputConfig: RollupOutputOptions; | ||
entries: Map<string, Entry>; | ||
}) { | ||
return function mapFormatToOutputOptions(format: LibraryFormats): RollupSingleOutputOptions { | ||
return { | ||
...outputOptions, | ||
...outputConfig, | ||
format, | ||
@@ -101,3 +101,3 @@ interop: isEsModule && format === "cjs", | ||
const outputFilename = `${entry.outputPath}.[format].js`; | ||
const outputFilename = `${entry.outputPath}-[hash].[format].js`; | ||
@@ -111,10 +111,56 @@ if (format === "cjs") return `cjs/${outputFilename}`; | ||
export default async function entriesPlugin(opts: PluginOptions): Promise<Plugin> { | ||
let config: UserConfig; | ||
function verifyBuildOptions(opts: PluginOptions) { | ||
opts.entries.forEach((entry) => { | ||
if (entry.outputPath && path.isAbsolute(entry.outputPath)) { | ||
throw new Error( | ||
`entry.outputPath "${entry.outputPath}" cannot be absolute - use build.outDir to specify the output directory instead`, | ||
); | ||
} | ||
if (entry.exports?.exportPath) { | ||
if (path.isAbsolute(entry.exports.exportPath)) { | ||
throw new Error( | ||
`entry.exports.exportPath "${entry.exports.exportPath}" cannot be absolute - it's used inside of package.json's exports field (consult the documentation at https://nodejs.org/api/packages.html#conditional-exports)`, | ||
); | ||
} | ||
if (entry.exports.exportPath === ".") { | ||
throw new Error( | ||
`entry.exports.exportPath "${entry.exports.exportPath}" cannot be "." - set entry.exports.isMain to true instead`, | ||
); | ||
} | ||
} | ||
}); | ||
} | ||
function createPackageJsonModuleFields({ | ||
options, | ||
exports, | ||
}: { | ||
options: PluginOptions; | ||
exports: PackageJsonExports; | ||
}) { | ||
return { | ||
module: options.formats.includes("es") ? exports["."]?.import : undefined, | ||
main: options.formats.includes("cjs") ? exports["."]?.require : undefined, | ||
"#exports": "Generated automatically by @ignsg/vite-build-tools", | ||
exports, | ||
}; | ||
} | ||
export default async function entriesPlugin(options: PluginOptions): Promise<Plugin> { | ||
let config: ResolvedConfig; | ||
let entries: Map<string, Entry>; | ||
verifyBuildOptions(options); | ||
const inputToOutput: InputToOutput = new Map(); | ||
return { | ||
name: "vite:entries", | ||
config(userConfig) { | ||
const packageDetails = JSON.parse(fs.readFileSync(getPackageJSONPath(userConfig)).toString()); | ||
const packageDetails = JSON.parse( | ||
fs.readFileSync(getPackageJSONPath({ config: userConfig })).toString(), | ||
); | ||
@@ -125,3 +171,10 @@ const isEsModule = packageDetails.type === "module"; | ||
entries = new Map(Object.entries(opts.entries.reduce(reduceEntryOptionsToEntries, {}))); | ||
entries = new Map( | ||
Object.entries( | ||
options.entries.reduce( | ||
createReduceEntryOptionsToEntries({ config: userConfig, options }), | ||
{}, | ||
), | ||
), | ||
); | ||
@@ -133,15 +186,29 @@ userConfig.build.rollupOptions = { | ||
output: opts.formats.map( | ||
createMapFormatToOutputOptions( | ||
output: options.formats.map( | ||
createMapFormatToOutputOptions({ | ||
isEsModule, | ||
userConfig.build.rollupOptions?.output, | ||
outputConfig: userConfig.build.rollupOptions?.output, | ||
entries, | ||
), | ||
}), | ||
), | ||
}; | ||
}, | ||
configResolved(userConfig) { | ||
config = userConfig; | ||
}, | ||
generateBundle(outputOptions) { | ||
generateBundle(outputOptions, bundle) { | ||
for (const assetOrChunk of Object.values(bundle)) { | ||
if (assetOrChunk.type !== "chunk") continue; | ||
if (!assetOrChunk.facadeModuleId) continue; | ||
if (!assetOrChunk.isEntry) continue; | ||
// Gather the mapping from source file paths (facadeModuleId) to the generated output files (fileName) | ||
inputToOutput.set(assetOrChunk.facadeModuleId, { | ||
...inputToOutput.get(assetOrChunk.facadeModuleId), | ||
[outputOptions.format]: assetOrChunk.fileName, | ||
}); | ||
} | ||
if (outputOptions.format === "cjs") { | ||
@@ -165,34 +232,69 @@ this.emitFile({ | ||
closeBundle() { | ||
const packageDetails = JSON.parse(fs.readFileSync(getPackageJSONPath(config)).toString()); | ||
const generatedKeys = ["#exports", "exports", "main", "module"]; | ||
const { dependencies, peerDependencies, devDependencies, ...restPackageDetails } = | ||
packageDetails; | ||
const injectionPoints = options.packageJsonInjectionPoints ?? [ | ||
"exports", | ||
"dependencies", | ||
"devDependencies", | ||
]; | ||
delete restPackageDetails.exports; | ||
const packageDetails = JSON.parse(fs.readFileSync(getPackageJSONPath({ config })).toString()); | ||
this.warn( | ||
"Adding an `exports` field to your package.json based on your build configuration ✅", | ||
); | ||
// Protect against overwriting existing user package.json configuration | ||
if ( | ||
!packageDetails["#exports"] || | ||
!packageDetails["#exports"].startsWith("Generated automatically") | ||
) { | ||
const conflictingKeys = generatedKeys.filter( | ||
(generatedKey) => generatedKey in packageDetails, | ||
); | ||
if (conflictingKeys.length > 0) { | ||
const serializedConflictingKeys = conflictingKeys | ||
.map((conflict) => `"${conflict}"`) | ||
.join(", "); | ||
throw new Error( | ||
`Detected that the keys: ${serializedConflictingKeys} already exist in the package.json but the auto-generated marker "#exports" does not. Aborting in order not to overwrite these keys - add an "#exports": "Generated automatically" anywhere in your package.json to confirm these keys can be overwritten`, | ||
); | ||
} | ||
} | ||
if (!options.silent) | ||
this.warn( | ||
"Adding an `exports` field to your package.json based on your build configuration ✅", | ||
); | ||
const exports = Array.from(entries.values()).reduce( | ||
createReduceEntriesToPackageExports({ ...config.build, ...opts }), | ||
createReduceEntriesToPackageExports({ config, options, inputToOutput }), | ||
{}, | ||
); | ||
let exportsGenerated = false; | ||
let adjustedPackageJson = Object.entries(packageDetails).reduce((result, [key, value]) => { | ||
const exportsInjection = injectionPoints.includes(key) | ||
? createPackageJsonModuleFields({ options, exports }) | ||
: undefined; | ||
if (exportsInjection) exportsGenerated = true; | ||
return { | ||
...result, | ||
...exportsInjection, | ||
...(generatedKeys.includes(key) ? {} : { [key]: value }), | ||
}; | ||
}, {} as Record<string, unknown>); | ||
if (!exportsGenerated) { | ||
// No injection point was found, attach the exports to the end instead | ||
adjustedPackageJson = { | ||
...adjustedPackageJson, | ||
...createPackageJsonModuleFields({ options, exports }), | ||
}; | ||
} | ||
fs.writeFileSync( | ||
getPackageJSONPath(config), | ||
JSON.stringify( | ||
{ | ||
...restPackageDetails, | ||
module: opts.formats.includes("es") ? exports["."]?.import : undefined, | ||
main: opts.formats.includes("cjs") ? exports["."]?.require : undefined, | ||
"#exports": "Generated automatically by @ignsg/vite-build-tools", | ||
exports, | ||
dependencies, | ||
peerDependencies, | ||
devDependencies, | ||
}, | ||
undefined, | ||
4, | ||
), | ||
getPackageJSONPath({ config }), | ||
JSON.stringify(adjustedPackageJson, undefined, 4), | ||
); | ||
@@ -199,0 +301,0 @@ }, |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
No README
QualityPackage does not have a README. This may indicate a failed publish or a low quality package.
Found 1 instance in 1 package
60279
14
927
1
120
4
1