@sanity/pkg-utils
Version:
Simple utilities for modern npm packages.
816 lines (814 loc) • 36.4 kB
JavaScript
import fs, { readFile, writeFile } from "node:fs/promises";
import path, { basename } from "node:path";
import { isRecord } from "./handleError.js";
import chalk from "chalk";
import { from, Observable } from "rxjs";
import { printExtractMessages } from "./printExtractMessages.js";
import { ExtractorConfig, Extractor, ExtractorLogLevel } from "@microsoft/api-extractor";
import * as rimraf from "rimraf";
import { rimraf as rimraf$1 } from "rimraf";
import ts from "@typescript/typescript6";
import { mkdirp } from "mkdirp";
import * as prettier from "prettier";
import { createApiExtractorConfig } from "./createApiExtractorConfig.js";
import { createTSDocConfig } from "./createTSDocConfig.js";
import { getExtractMessagesConfig } from "./getExtractMessagesConfig.js";
import { createConsoleSpy, pkgExtMap, resolveConfigProperty } from "./resolveBuildContext.js";
import { rollup, watch } from "rollup";
import { optimizeLodashImports } from "@optimize-lodash/rollup-plugin";
import alias from "@rollup/plugin-alias";
import { babel, getBabelOutputPlugin } from "@rollup/plugin-babel";
import commonjs from "@rollup/plugin-commonjs";
import json from "@rollup/plugin-json";
import { nodeResolve } from "@rollup/plugin-node-resolve";
import replace from "@rollup/plugin-replace";
import terser from "@rollup/plugin-terser";
import { vanillaExtractPlugin } from "@vanilla-extract/rollup-plugin";
import esbuild from "rollup-plugin-esbuild";
import { DEFAULT_BROWSERSLIST_QUERY } from "./defaults.js";
import browserslist from "browserslist";
import { browserslistToTargets, transform } from "lightningcss";
const DEFAULT_VANILLA_EXTRACT_CSS_NAME = "bundle.css";
function resolveVanillaExtract(config) {
const value = config?.rollup?.vanillaExtract, enabled = !!value, options = value === !0 || !value ? {} : value, compatMode = enabled ? options.extract?.compatMode ?? !0 : !1;
return { enabled, options, compatMode };
}
function resolveVanillaExtractCssName(options, context) {
return options.extract?.name ? options.extract.name : context.compatMode ? DEFAULT_VANILLA_EXTRACT_CSS_NAME : context.runtime === "node" ? "bundle.node.css" : context.runtime === "browser" ? "bundle.browser.css" : DEFAULT_VANILLA_EXTRACT_CSS_NAME;
}
function createConditionalCssExport(cssFile, shimFile) {
return { browser: cssFile, style: cssFile, node: shimFile, default: shimFile };
}
function hasMatchingExport(value, expected) {
if (typeof value != "object" || value === null) return !1;
const actual = Object.fromEntries(Object.entries(value)), keys = Object.keys(expected);
return keys.length === Object.keys(actual).length && keys.every((key) => actual[key] === expected[key]);
}
function insertCssExport(exports, exportKey, conditionalExport) {
const nextExports = {};
let inserted = !1;
for (const [key, value] of Object.entries(exports))
key !== exportKey && (key === "./package.json" && !inserted && (nextExports[exportKey] = conditionalExport, inserted = !0), nextExports[key] = value);
return inserted || (nextExports[exportKey] = conditionalExport), nextExports;
}
function detectIndent(source) {
const match = source.match(/\n([ \t]+)\S/);
if (!match) return 2;
const indent = match[1];
return indent.includes(" ") ? " " : indent.length;
}
async function writeBundleCssExports(options) {
const { cwd, distPath, cssName, logger } = options, pkgPath = path.resolve(cwd, "package.json"), source = await readFile(pkgPath, "utf8"), pkg = JSON.parse(source), distRel = (path.relative(cwd, distPath) || "dist").split(path.sep).join("/"), exportKey = `./${cssName}`, cssFile = `./${path.posix.join(distRel, cssName)}`, shimFile = `./${path.posix.join(distRel, `${cssName}.js`)}`, conditionalExport = createConditionalCssExport(cssFile, shimFile), publishConfig = pkg.publishConfig, publishConfigExports = isRecord(publishConfig?.exports) ? publishConfig.exports : void 0, exportsMatch = hasMatchingExport(pkg.exports?.[exportKey], conditionalExport), publishConfigExportsMatch = !publishConfigExports || hasMatchingExport(publishConfigExports[exportKey], conditionalExport);
exportsMatch && publishConfigExportsMatch || (pkg.exports = insertCssExport(pkg.exports ?? {}, exportKey, conditionalExport), publishConfig && publishConfigExports && (publishConfig.exports = insertCssExport(publishConfigExports, exportKey, conditionalExport)), await writeFile(pkgPath, `${JSON.stringify(pkg, null, detectIndent(source))}
`), logger.log(
`Updated package.json: added \`exports["${exportKey}"]\`${publishConfigExports ? ` and \`publishConfig.exports["${exportKey}"]\`` : ""} for vanilla-extract compat mode`
));
}
function printDiagnostic(options) {
const { cwd, logger, diagnostic } = options;
if (diagnostic.file && diagnostic.start !== void 0) {
const { line, character } = ts.getLineAndCharacterOfPosition(diagnostic.file, diagnostic.start), message = ts.flattenDiagnosticMessageText(diagnostic.messageText, `
`), file = path.relative(cwd, diagnostic.file.fileName), output = [
`${chalk.yellow(file)}:${chalk.blue(line + 1)}:${chalk.blue(character + 1)} - `,
`${chalk.gray(`TS${diagnostic.code}:`)} ${message}`
].join("");
diagnostic.category === ts.DiagnosticCategory.Error && logger.error(output), diagnostic.category === ts.DiagnosticCategory.Warning && logger.warn(output), diagnostic.category === ts.DiagnosticCategory.Message && logger.log(output), diagnostic.category === ts.DiagnosticCategory.Suggestion && logger.log(output);
} else
logger.log(ts.flattenDiagnosticMessageText(diagnostic.messageText, `
`));
}
async function buildTypes(options) {
const { cwd, logger, outDir, tsconfig, strict, checkTypes } = options, compilerOptions = {
...tsconfig.options,
declaration: !0,
declarationDir: outDir,
emitDeclarationOnly: !0,
noEmit: !1,
noEmitOnError: strict ? !0 : tsconfig.options.noEmitOnError ?? !0,
noCheck: checkTypes === !1 ? !0 : tsconfig.options.noCheck ?? tsconfig.options.isolatedDeclarations,
outDir
}, program = ts.createProgram(tsconfig.fileNames, compilerOptions), emitResult = program.emit(), allDiagnostics = ts.getPreEmitDiagnostics(program).concat(emitResult.diagnostics);
for (const diagnostic of allDiagnostics)
printDiagnostic({ cwd, logger, diagnostic });
if (emitResult.emitSkipped && allDiagnostics.filter((diag) => diag.category === ts.DiagnosticCategory.Error).length)
throw new Error("failed to compile TypeScript definitions");
}
class DtsError extends Error {
messages;
constructor(message, messages) {
super(message), this.messages = messages;
}
}
async function extractTypes(options) {
const {
bundledPackages,
customTags,
distPath,
exportPath,
files,
filePaths,
projectPath,
rules,
sourceTypesPath,
tmpPath,
tsconfig,
tsconfigPath,
extractorDisabled
} = options, tsdocConfigFile = await createTSDocConfig({
customTags: customTags || []
}), filePath = filePaths[0].replace(/\.d\.[mc]ts$/, ".d.ts"), shouldCleanUpDts = !filePaths.includes(filePath), extractorConfig = ExtractorConfig.prepare({
configObject: createApiExtractorConfig({
bundledPackages,
distPath,
exportPath,
filePath,
messages: getExtractMessagesConfig({ rules, disabled: extractorDisabled }),
projectFolder: projectPath,
mainEntryPointFilePath: sourceTypesPath,
tsconfig,
tsconfigPath,
dtsRollupEnabled: !0
}),
configObjectFullPath: void 0,
tsdocConfigFile,
packageJsonFullPath: path.resolve(projectPath, "package.json")
}), messages = [], extractorResult = Extractor.invoke(extractorConfig, {
// Equivalent to the "--local" command-line parameter
localBuild: !0,
// Equivalent to the "--verbose" command-line parameter
showVerboseMessages: !0,
// handle messages
messageCallback(message) {
messages.push(message), message.handled = !0;
}
}), typesPath = path.resolve(distPath, filePath), typesBuf = await fs.readFile(typesPath), prettierConfig = await prettier.resolveConfig(typesPath);
await mkdirp(path.dirname(typesPath));
const { extractModuleBlocksFromTypes } = await import("./extractModuleBlocks.js"), moduleBlocks = extractModuleBlocksFromTypes({
extractResult: extractorResult,
tsOutDir: tmpPath
}), code = [typesBuf.toString(), ...moduleBlocks].join(`
`), prettyCode = await prettier.format(code, {
...prettierConfig,
filepath: typesPath
});
for (const expFilePath of filePaths) {
const expTypesPath = path.resolve(distPath, expFilePath);
await fs.writeFile(expTypesPath, prettyCode), files.push({
type: "types",
path: expTypesPath
});
}
return shouldCleanUpDts && await fs.unlink(typesPath), { extractorResult, messages };
}
async function doExtract(ctx, task) {
const { config, cwd, files, logger, strict, ts: ts2, bundledPackages } = ctx;
if (!ts2.config || !ts2.configPath)
return { type: "dts", messages: [], results: [] };
const { outDir, rootDir = cwd } = ts2.config.options;
if (!outDir)
throw new Error("tsconfig.json is missing `compilerOptions.outDir`");
const tmpPath = path.resolve(outDir, "__tmp__");
await buildTypes({
cwd,
logger,
outDir: tmpPath,
strict,
tsconfig: ts2.config,
checkTypes: config?.extract?.checkTypes
});
const messages = [], results = [];
for (const entry of task.entries) {
const exportPath = entry.exportPath === "." ? "./index" : entry.exportPath, sourceTypesPath = path.resolve(
tmpPath,
path.relative(rootDir, path.resolve(cwd, entry.sourcePath)).replace(/\.ts$/, ".d.ts")
), targetPaths = entry.targetPaths.map((targetPath) => path.resolve(cwd, targetPath)), filePaths = targetPaths.map((targetPath) => path.relative(outDir, targetPath)), result = await extractTypes({
bundledPackages: bundledPackages || [],
customTags: config?.extract?.customTags,
distPath: outDir,
exportPath,
files,
filePaths,
projectPath: cwd,
rules: config?.extract?.rules,
sourceTypesPath,
tsconfig: ts2.config,
tmpPath,
tsconfigPath: path.resolve(cwd, ts2.configPath || "tsconfig.json"),
extractorDisabled: config?.extract?.enabled === !1
});
messages.push(...result.messages);
const errors = result.messages.filter((msg) => msg.logLevel === ExtractorLogLevel.Error);
if (errors.length > 0)
throw await rimraf$1(tmpPath), new DtsError(`encountered ${errors.length} errors when extracting types`, errors);
results.push({ sourcePath: path.resolve(cwd, entry.sourcePath), filePaths: targetPaths });
}
return await rimraf$1(tmpPath), { type: "dts", messages, results };
}
const dtsTask = {
name: (ctx, task) => [
`Build type definitions with ${chalk.bold(ctx.dts)}...`,
" entries:",
...task.entries.map((entry) => entry.targetPaths.map((targetPath) => [
` - ${chalk.cyan(entry.importId)}: `,
`${chalk.yellow(entry.sourcePath)} ${chalk.gray("\u2192")} ${chalk.yellow(targetPath)}`
].join("")).join(`
`))
].join(`
`),
exec: (ctx, task) => from(doExtract(ctx, task)),
complete: (ctx, _task, result) => {
printExtractMessages(ctx, result.messages);
},
error: (ctx, _task, err) => {
const { logger } = ctx;
err instanceof DtsError ? printExtractMessages(ctx, err.messages) : err instanceof Error && logger.error(err);
}
}, dtsWatchTask = {
name: (_ctx, task) => [
"build type definitions",
...task.entries.map((entry) => entry.targetPaths.map((targetPath) => [
` - ${chalk.cyan(entry.importId)}: `,
`${chalk.yellow(entry.sourcePath)} ${chalk.gray("\u2192")} ${chalk.yellow(targetPath)}`
].join("")))
].join(`
`),
exec: (ctx, task) => {
const { config, cwd, files, logger, strict, ts: tsContext, bundledPackages } = ctx;
return new Observable((observer) => {
const { config: tsConfig, configPath: tsConfigPath } = tsContext;
if (!tsConfig || !tsConfigPath)
return observer.next({ type: "dts", messages: [], results: [] }), observer.complete(), () => {
};
const { outDir, rootDir = cwd } = tsConfig.options;
if (!outDir)
return observer.error(new Error("tsconfig.json is missing `compilerOptions.outDir`")), () => {
};
const tmpPath = path.resolve(outDir, "__tmp__");
buildTypes({
cwd,
logger,
outDir: tmpPath,
tsconfig: tsConfig,
strict,
checkTypes: config?.extract?.checkTypes
}).catch((err) => {
observer.error(err);
});
const host = ts.createWatchCompilerHost(
tsConfigPath,
{
...tsConfig.options,
declaration: !0,
declarationDir: tmpPath,
emitDeclarationOnly: !0,
noEmit: !1,
noEmitOnError: strict ? !0 : tsConfig.options.noEmitOnError ?? !0,
noCheck: config?.extract?.checkTypes === !1 ? !0 : tsConfig.options.noCheck ?? tsConfig.options.isolatedDeclarations,
outDir: tmpPath
},
ts.sys,
ts.createEmitAndSemanticDiagnosticsBuilderProgram,
(diagnostic) => {
logger.error(ts.flattenDiagnosticMessageText(diagnostic.messageText, `
`));
},
(diagnostic) => {
logger.info(ts.flattenDiagnosticMessageText(diagnostic.messageText, `
`));
}
), origPostProgramCreate = host.afterProgramCreate;
host.afterProgramCreate = async (program) => {
origPostProgramCreate?.(program);
const messages = [], results = [];
for (const entry of task.entries) {
const exportPath = entry.exportPath === "." ? "./index" : entry.exportPath, sourceTypesPath = path.resolve(
tmpPath,
path.relative(rootDir, path.resolve(cwd, entry.sourcePath)).replace(/\.ts$/, ".d.ts")
), targetPaths = entry.targetPaths.map((targetPath) => path.resolve(cwd, targetPath)), filePaths = targetPaths.map((targetPath) => path.relative(outDir, targetPath));
try {
const result = await extractTypes({
bundledPackages: bundledPackages || [],
customTags: config?.extract?.customTags,
cwd,
distPath: outDir,
exportPath,
files,
filePaths,
projectPath: cwd,
rules: config?.extract?.rules,
sourceTypesPath,
tsconfig: tsConfig,
tmpPath,
tsconfigPath: path.resolve(cwd, tsConfigPath),
extractorDisabled: config?.extract?.enabled === !1
});
messages.push(...result.messages), results.push({ sourcePath: path.resolve(cwd, entry.sourcePath), filePaths: targetPaths });
} catch (err) {
if (err instanceof DtsError)
messages.push(...err.messages);
else {
observer.error(err);
return;
}
}
}
observer.next({ type: "dts", messages, results });
};
const watchProgram = ts.createWatchProgram(host);
return () => {
watchProgram.close(), rimraf.sync(tmpPath);
};
});
},
complete: (ctx, task, result) => {
const { logger } = ctx;
printExtractMessages(ctx, result.messages), logger.success(
`build type definitions
${task.entries.map(
(entry) => ` - ${chalk.cyan(entry.importId)}: ${chalk.yellow(entry.sourcePath)} ${chalk.gray("\u2192")} ${chalk.yellow(entry.targetPaths.join(", "))}`
).join(`
`)}`
), logger.log("");
},
error: (ctx, _task, err) => {
const { logger } = ctx;
err instanceof DtsError ? printExtractMessages(ctx, err.messages) : err instanceof Error && logger.error(err);
}
}, rolldownDtsTask = {
name: (ctx, task) => {
const bundleEntries = task.entries.filter((e) => e.path.includes("__$$bundle_")), entries = task.entries.filter((e) => !e.path.includes("__$$bundle_")), targetLines = task.target.length ? [" target:", ...task.target.map((t) => ` - ${chalk.yellow(t)}`)] : [], bundlesLines = bundleEntries.length ? [
" bundles:",
...bundleEntries.map(
(e) => [
" - ",
`${chalk.yellow(e.source)} ${chalk.gray("\u2192")} ${chalk.yellow(replaceFileEnding(e.output))}`
].join("")
)
] : [], entriesLines = entries.length ? [
" entries:",
...entries.map(
(e) => [
" - ",
`${chalk.cyan(path.join(ctx.pkg.name, e.path))}: `,
`${chalk.yellow(e.source)} ${chalk.gray("\u2192")} ${chalk.yellow(replaceFileEnding(e.output))}`
].join("")
)
] : [];
return [
`Build type definitions with ${chalk.bold("rolldown")}...`,
` format: ${chalk.yellow(task.format)}`,
...targetLines,
...bundlesLines,
...entriesLines
].join(`
`);
},
exec: (ctx, task) => from(execPromise$1(ctx, task)),
complete: () => {
},
error: (_ctx, _task, err) => {
console.error(err);
}
};
async function execPromise$1(ctx, task) {
const [{ rolldown }, { resolveRolldownConfig }] = await Promise.all([
import("rolldown"),
import("./resolveRolldownConfig.js")
]), { distPath, files, logger } = ctx, outDir = path.relative(ctx.cwd, distPath), consoleSpy = createConsoleSpy({
onRestored: (messages) => {
for (const msg of messages) {
const text = String(msg.args[0]);
msg.code !== "CIRCULAR_DEPENDENCY" && (text.startsWith("Dynamic import can only") || text.startsWith("Sourcemap is likely to be incorrect") || (msg.type === "log" && logger.info(...msg.args), msg.type === "warn" && logger.warn(...msg.args), msg.type === "error" && logger.error(...msg.args)));
}
}
});
try {
const { inputOptions, outputOptions } = resolveRolldownConfig(ctx, task), bundle = await rolldown({
...inputOptions,
onwarn(warning) {
consoleSpy.messages.push({
type: "warn",
code: warning.code,
args: [warning.message]
});
}
}), { output } = await bundle.generate(outputOptions);
for (const chunkOrAsset of output)
chunkOrAsset.type === "asset" ? files.push({
type: "asset",
path: path.resolve(outDir, chunkOrAsset.fileName)
}) : files.push({
type: "chunk",
path: path.resolve(outDir, chunkOrAsset.fileName)
});
await bundle.write(outputOptions), await bundle.close(), consoleSpy.restore();
} catch (err) {
throw consoleSpy.restore(), err;
}
}
function replaceFileEnding(filePath) {
switch (!0) {
case filePath.endsWith(".mjs"):
return filePath.replace(/\.mjs$/, ".d.mts");
case filePath.endsWith(".cjs"):
return filePath.replace(/\.cjs$/, ".d.cts");
default:
return filePath.replace(/\.js$/, ".d.ts");
}
}
function bundleCssShim(options) {
const cssName = options.fileName.replace(/\.js$/, "");
return {
name: "pkg-utils:bundle-css-shim",
generateBundle() {
this.emitFile({
type: "asset",
fileName: options.fileName,
source: `// No-op shim for \`${cssName}\` in runtimes that cannot import \`.css\` files directly.
export default ""
`
}), this.emitFile({
type: "asset",
fileName: `${cssName}.d.ts`,
source: `// Type declarations for \`${cssName}\` and its no-op JS shim.
declare const _default: string
export default _default
`
});
}
};
}
function optimizeCss(options) {
return {
name: "optimize-css",
async generateBundle(_outputOptions, bundle, _isWrite) {
for (const [fileName, assetOrChunk] of Object.entries(bundle))
if (assetOrChunk.type === "asset") {
const asset = assetOrChunk;
if (asset.originalFileNames.includes(options.extractFileName)) {
const sourceMapFileName = `${fileName}.map`;
await transformCss(
asset,
bundle[sourceMapFileName]?.type === "asset" ? bundle[sourceMapFileName] : void 0,
options.browserslist,
options.minify ?? !0
);
}
}
}
};
}
async function transformCss(asset, sourceMapAsset, browserslistConfig, minify) {
const css = asset.source.toString(), file = asset.fileName, targets = browserslistToTargets(browserslist(browserslistConfig)), lightningCssResult = transform({
filename: file,
code: Buffer.from(css),
minify,
cssModules: !1,
targets,
sourceMap: !!sourceMapAsset,
inputSourceMap: sourceMapAsset ? sourceMapAsset.source.toString() : void 0
});
lightningCssResult.warnings.length && console.warn(lightningCssResult.warnings), asset.source = new TextDecoder().decode(lightningCssResult.code), sourceMapAsset && lightningCssResult.map && (sourceMapAsset.source = new TextDecoder().decode(lightningCssResult.map), asset.source += `
/*# sourceMappingURL=${basename(sourceMapAsset.fileName)}*/
`);
}
function isTruthy(value) {
return !!value;
}
function hasPeerDependency(pkg, packageName) {
return pkg.peerDependencies ? packageName in pkg.peerDependencies : !1;
}
function hasDevDependency(pkg, packageName) {
return pkg.devDependencies ? packageName in pkg.devDependencies : !1;
}
let styledComponentsLogged = !1;
function shouldEnableStyledComponents(config, pkg, logger) {
const hasStyledComponents = hasPeerDependency(pkg, "styled-components"), hasBabelPluginStyledComponents = hasDevDependency(pkg, "babel-plugin-styled-components");
return config?.babel?.styledComponents !== void 0 ? !!config?.babel?.styledComponents : (styledComponentsLogged || (styledComponentsLogged = !0, hasStyledComponents && (hasBabelPluginStyledComponents ? logger.log(
"Detected styled-components in peerDependencies and babel-plugin-styled-components in devDependencies. Automatically enabling babel.styledComponents. To disable this, set `babel: { styledComponents: false }` in package.config.ts."
) : logger.warn(
'Detected styled-components in peerDependencies. Consider installing babel-plugin-styled-components as a devDependency to enable better debugging and optimization. Add `"babel-plugin-styled-components": "^2.0.0"` to devDependencies and it will be automatically enabled, or set `babel: { styledComponents: false }` in package.config.ts to disable this warning.'
))), !!(hasStyledComponents && hasBabelPluginStyledComponents));
}
function resolveRollupConfig(ctx, buildTask) {
const { format, runtime, target } = buildTask, { config, cwd, exports: _exports, external, distPath, logger, pkg, ts: ts2 } = ctx, outputExt = pkgExtMap[pkg.type || "commonjs"][format], minify = config?.minify ?? !1, outDir = path.relative(cwd, distPath), pathAliases = Object.fromEntries(
Object.entries(ts2.config?.options.paths || {}).map(([key, val]) => [key, path.resolve(cwd, ts2.config?.options.baseUrl || ".", val[0])])
), entries = buildTask.entries.map((entry) => ({
...entry,
name: path.relative(outDir, entry.output).replace(/\.[^/.]+$/, "")
}), {}), exportIds = _exports && Object.keys(_exports).map((exportPath) => path.join(pkg.name, exportPath)), sourcePaths = _exports && Object.values(_exports).map((e) => path.resolve(cwd, e.source)), replacements = Object.fromEntries(
Object.entries(config?.define || {}).map(([key, val]) => [key, JSON.stringify(val)])
), { optimizeLodash: enableOptimizeLodash = hasDependency(pkg, "lodash") } = config?.rollup || {}, enableStyledComponents = shouldEnableStyledComponents(config, pkg, logger), vanillaExtract = resolveVanillaExtract(config), vanillaExtractCssName = resolveVanillaExtractCssName(vanillaExtract.options, {
compatMode: vanillaExtract.compatMode,
runtime
}), defaultPlugins = [
replace({
preventAssignment: !0,
values: pkg.name === "@sanity/pkg-utils" ? { ...replacements } : {
"process.env.PKG_FILE_PATH": (arg) => {
const sourcePath = `./${path.relative(cwd, arg)}`, entry = entries.find((e) => e.source === sourcePath);
return entry ? JSON.stringify(
path.relative(cwd, path.resolve(outDir, entry.name + outputExt))
) : (console.error(`could not find source entry: ${sourcePath}`), "null");
},
"process.env.PKG_FORMAT": JSON.stringify(format),
"process.env.PKG_RUNTIME": JSON.stringify(runtime),
"process.env.PKG_VERSION": JSON.stringify(process.env.PKG_VERSION || pkg.version),
...replacements
}
}),
alias({
entries: { ...pathAliases }
}),
nodeResolve({
browser: runtime === "browser",
extensions: [".cjs", ".mjs", ".js", ".jsx", ".json", ".node"],
preferBuiltins: !0
}),
commonjs(),
json(),
vanillaExtract.enabled && vanillaExtractPlugin({
identifiers: vanillaExtract.options.identifiers ?? "short",
cwd: vanillaExtract.options.cwd,
esbuildOptions: vanillaExtract.options.esbuildOptions,
unstable_injectFilescopes: vanillaExtract.options.unstable_injectFilescopes,
extract: {
name: vanillaExtractCssName,
sourcemap: vanillaExtract.options.extract?.sourcemap ?? !0
}
}),
vanillaExtract.enabled && optimizeCss({
extractFileName: vanillaExtractCssName,
browserslist: vanillaExtract.options.browserslist || DEFAULT_BROWSERSLIST_QUERY,
minify: vanillaExtract.options.minify ?? !0
}),
// In compat mode, emit the no-op JS shim that the `node`/`default` conditions of the
// `./<css>` export resolve to.
vanillaExtract.compatMode && bundleCssShim({ fileName: `${vanillaExtractCssName}.js` }),
(config?.babel?.reactCompiler || enableStyledComponents) && babel({
babelrc: !1,
presets: ["@babel/preset-typescript"],
babelHelpers: "bundled",
parallel: !0,
extensions: [".ts", ".tsx", ".js", ".jsx"],
plugins: [
// The styled-components plugin needs to run before the react-compiler plugin, in case the css prop is used
enableStyledComponents && [
"babel-plugin-styled-components",
{
// Unnecessary, as the way we use styled-components in Sanity is usually by wrapping `@sanity/ui` primitives, not declaring new ones like "const Button = styled.button``"
fileName: !1,
// Transpile `styled.button`...`` to `styled.button(["..."])` so that the `pure` option below
// can annotate a plain call expression. Pure annotations on tagged template expressions are
// not supported by any bundler (https://github.com/rollup/rollup/issues/4035), so without
// this transpilation unused styled components can't be tree-shaken at all.
transpileTemplateLiterals: !0,
// Massively helps dead code elimination and tree-shaking
pure: !0,
// disabled, as pkg-utils tends to be used for npm publishing, while other tooling, like `sanity dev`, `next dev`, etc are used for testing
cssProp: !1,
...typeof config?.babel?.styledComponents == "object" ? config.babel.styledComponents : {}
}
],
config?.babel?.reactCompiler && [
"babel-plugin-react-compiler",
config?.reactCompilerOptions || {}
]
].filter(isTruthy)
}),
esbuild({
jsx: config?.jsx ?? "automatic",
jsxFactory: config?.jsxFactory ?? "createElement",
jsxFragment: config?.jsxFragment ?? "Fragment",
jsxImportSource: config?.jsxImportSource ?? "react",
target,
tsconfig: ctx.ts.configPath || "tsconfig.json",
treeShaking: !0,
minifySyntax: config?.minify !== !1,
supported: {
"template-literal": !0
}
}),
Array.isArray(config?.babel?.plugins) && getBabelOutputPlugin({
babelrc: !1,
parallel: !0,
plugins: config.babel.plugins
}),
enableOptimizeLodash && optimizeLodashImports({
useLodashEs: format === "esm" && hasDependency(pkg, "lodash-es") ? !0 : void 0,
...typeof config?.rollup?.optimizeLodash == "boolean" ? {} : config?.rollup?.optimizeLodash
}),
minify && terser({
compress: { directives: !1, passes: 10 },
ecma: 2020,
output: {
comments: (_node, comment) => {
const text = comment.value;
return comment.type === "comment2" ? /@preserve|@license|@cc_on/i.test(text) : !1;
},
preserve_annotations: !0
}
})
].filter(isTruthy), userPlugins = config?.rollup?.plugins, plugins = Array.isArray(userPlugins) ? defaultPlugins.concat(userPlugins) : resolveConfigProperty(config?.rollup?.plugins, defaultPlugins), hashChunkFileNames = config?.rollup?.hashChunkFileNames ?? !1, chunkFileNames = `${hashChunkFileNames ? "_chunks" : "_chunks-[format]"}/${hashChunkFileNames ? "[name]-[hash]" : "[name]"}${outputExt}`, entryFileNames = `[name]${outputExt}`;
return {
inputOptions: {
context: cwd,
external: (id, importer) => {
if (exportIds?.includes(id))
return !0;
if (importer && (id.startsWith(".") || id.startsWith("/"))) {
const idPath = path.resolve(path.dirname(importer), id);
if (sourcePaths?.includes(idPath))
return logger.warn(
`detected self-referencing import \u2013 treating as external: ${path.relative(
cwd,
idPath
)}`
), !0;
}
const idParts = id.split("/"), name = idParts[0].startsWith("@") ? `${idParts[0]}/${idParts[1]}` : idParts[0];
return !!(name && external.includes(name));
},
input: entries.reduce(
(acc, entry) => Object.assign(acc, { [entry.name]: entry.source }),
{}
),
watch: {
chokidar: {
usePolling: !0
}
},
plugins,
// Rely on Rollup's `recommended` tree-shaking defaults instead of customizing
// `moduleSideEffects`/`propertyReadSideEffects` ourselves. Previously we set
// `moduleSideEffects` to the equivalent of `'no-external'` (with a `.css` exemption),
// which incorrectly stripped intentional side-effect-only imports of external packages
// (e.g. `import 'react-time-ago/locale/en'`) from the bundle. The `recommended` preset
// uses `moduleSideEffects: true`, so these imports are preserved while `package.json`
// `sideEffects` fields are still honored for bundled modules.
treeshake: {
preset: "recommended",
...config?.rollup?.treeshake
},
experimentalLogSideEffects: config?.rollup?.experimentalLogSideEffects
},
outputOptions: {
chunkFileNames,
compact: minify,
dir: outDir,
entryFileNames,
esModule: !0,
format,
interop: "compat",
sourcemap: config?.sourcemap ?? !0,
hoistTransitiveImports: !1,
minifyInternalExports: minify,
assetFileNames: "[name][extname]",
...config?.rollup?.output,
// In compat mode, inject the self-referential bundle.css import into the `index` entry chunk
// so userland does not need to set `rollup.output.intro` themselves. Use `require()` for CommonJS output
// (a top-level `import` would be invalid in a `.cjs` bundle).
...vanillaExtract.compatMode ? {
intro: composeIntro(
format === "commonjs" ? `require(${JSON.stringify(`${pkg.name}/${vanillaExtractCssName}`)})` : `import ${JSON.stringify(`${pkg.name}/${vanillaExtractCssName}`)}`,
config?.rollup?.output?.intro
)
} : {}
}
};
}
function composeIntro(autoImport, userIntro) {
return async (chunkInfo) => {
const auto = chunkInfo.isEntry && chunkInfo.name === "index" ? `${autoImport}
` : "", user = typeof userIntro == "function" ? await userIntro(chunkInfo) : typeof userIntro == "string" ? userIntro : "";
return `${auto}${user}`;
};
}
function hasDependency(pkg, packageName) {
return pkg.dependencies ? packageName in pkg.dependencies : pkg.peerDependencies ? packageName in pkg.peerDependencies : !1;
}
const rollupTask = {
name: (ctx, task) => {
const bundleEntries = task.entries.filter((e) => e.path.includes("__$$bundle_")), entries = task.entries.filter((e) => !e.path.includes("__$$bundle_")), targetLines = task.target.length ? [" target:", ...task.target.map((t) => ` - ${chalk.yellow(t)}`)] : [], bundlesLines = bundleEntries.length ? [
" bundles:",
...bundleEntries.map(
(e) => [
" - ",
`${chalk.yellow(e.source)} ${chalk.gray("\u2192")} ${chalk.yellow(e.output)}`
].join("")
)
] : [], entriesLines = entries.length ? [
" entries:",
...entries.map(
(e) => [
" - ",
`${chalk.cyan(path.join(ctx.pkg.name, e.path))}: `,
`${chalk.yellow(e.source)} ${chalk.gray("\u2192")} ${chalk.yellow(e.output)}`
].join("")
)
] : [];
return [
"Build javascript files...",
` format: ${chalk.yellow(task.format)}`,
...targetLines,
...bundlesLines,
...entriesLines
].join(`
`);
},
exec: (ctx, task) => from(execPromise(ctx, task)),
complete: () => {
},
error: (_ctx, _task, err) => {
console.error(err);
}
};
async function execPromise(ctx, task) {
const { distPath, files, logger } = ctx, outDir = path.relative(ctx.cwd, distPath), consoleSpy = createConsoleSpy({
onRestored: (messages) => {
for (const msg of messages) {
const text = String(msg.args[0]);
msg.code !== "CIRCULAR_DEPENDENCY" && (text.startsWith("Dynamic import can only") || text.startsWith("Sourcemap is likely to be incorrect") || (msg.type === "log" && logger.info(...msg.args), msg.type === "warn" && logger.warn(...msg.args), msg.type === "error" && logger.error(...msg.args)));
}
}
});
try {
const { inputOptions, outputOptions } = resolveRollupConfig(ctx, task), bundle = await rollup({
...inputOptions,
onwarn(warning) {
consoleSpy.messages.push({
type: "warn",
code: warning.code,
args: [warning.message]
});
}
}), { output } = await bundle.generate(outputOptions);
for (const chunkOrAsset of output)
chunkOrAsset.type === "asset" ? files.push({
type: "asset",
path: path.resolve(outDir, chunkOrAsset.fileName)
}) : files.push({
type: "chunk",
path: path.resolve(outDir, chunkOrAsset.fileName)
});
await bundle.write(outputOptions), await bundle.close(), consoleSpy.restore();
} catch (err) {
throw consoleSpy.restore(), err;
}
}
const rollupWatchTask = {
name: (ctx, task) => `build javascript files (target ${task.target.join(" + ")}, format ${task.format})
${task.entries.map((e) => `${chalk.blue(path.join(ctx.pkg.name, e.path))}: ${e.source} -> ${e.output}`).join(`
`)}`,
exec: (ctx, task) => {
const { inputOptions, outputOptions } = resolveRollupConfig(ctx, task);
return new Observable((observer) => {
const watchOptions = {
...inputOptions,
output: outputOptions
}, watcher = watch(watchOptions);
return watcher.on("event", (event) => {
observer.next(event);
}), () => {
watcher.close();
};
});
},
complete: (ctx, task, event) => {
const { logger } = ctx;
if (event.code === "BUNDLE_END") {
logger.success(
`build javascript files (target ${task.target.join(" + ")}, format ${task.format})
${task.entries.map((e) => `${chalk.blue(path.join(ctx.pkg.name, e.path))}: ${e.source} -> ${e.output}`).join(`
`)}`
), logger.log("");
return;
}
if (event.code !== "BUNDLE_START" && event.code !== "END") {
if (event.code === "ERROR") {
logger.error(event.code, event);
return;
}
event.code;
}
},
error: (ctx, _task, err) => {
const { logger } = ctx;
err instanceof Error && logger.log(err);
}
}, buildTaskHandlers = {
"build:dts": dtsTask,
"build:js": rollupTask,
"rolldown:dts": rolldownDtsTask
}, watchTaskHandlers = {
"watch:dts": dtsWatchTask,
"watch:js": rollupWatchTask
};
export {
buildTaskHandlers,
resolveVanillaExtract,
resolveVanillaExtractCssName,
watchTaskHandlers,
writeBundleCssExports
};
//# sourceMappingURL=index.js.map