UNPKG

@sanity/pkg-utils

Version:

Simple utilities for modern npm packages.

816 lines (814 loc) 36.4 kB
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