UNPKG

@darkobits/ts

Version:

Vite-based toolchain for Node projects.

152 lines (151 loc) 6.3 kB
import path from "node:path"; import { interopImportDefault } from "@darkobits/interop-import-default"; import typescriptPlugin from "@rollup/plugin-typescript"; import chalk from "chalk"; import glob from "fast-glob"; import preserveShebangPlugin from "rollup-plugin-preserve-shebang"; import { viteStaticCopy } from "vite-plugin-static-copy"; import tsconfigPathsPluginExport from "vite-tsconfig-paths"; import { TEST_FILE_PATTERNS, BARE_EXTENSIONS } from "../etc/constants.js"; import cleanupPlugin from "../lib/cleanup-plugin.js"; import executablePlugin from "../lib/executable-plugin.js"; import log from "../lib/log.js"; import { nodeExternalPlugin } from "../lib/node-external-plugin.js"; import tscAliasPlugin from "../lib/tsc-alias-plugin.js"; import { createViteConfigurationPreset } from "../lib/utils.js"; const tsconfigPathsPlugin = interopImportDefault(tsconfigPathsPluginExport); const isWatchMode = process.argv.includes("--watch") || process.argv.includes("-w"); const node = createViteConfigurationPreset(async (context) => { const prefix = chalk.dim.cyan("preset:node"); log.isReady(); const { command, mode, root, srcDir, outDir, patterns: { SOURCE_FILES, TEST_FILES } } = context; if (command === "serve" && mode !== "test") { log.error(prefix, `The ${chalk.white("serve")} command is not available when using the ${chalk.cyan("node")} preset.`); process.exit(1); } const [ // Array of all source files. To prevent bundling, we tell Rollup that each // file in the project should be treated as its own entrypoint. entry, // Array of all files in the source directory other than source code. filesToCopy // Whether the user has an ESLint configuration file. If not, we skip adding // the ESLint plugin to the build. // eslintConfigResult ] = await Promise.all([ glob(SOURCE_FILES, { cwd: root, ignore: [TEST_FILES] }), glob([`${srcDir}/**/*`, `!${SOURCE_FILES}`, `!${TEST_FILES}`], { cwd: root }) ]); if (entry.length === 0) { log.error(prefix, `No entry files found in ${chalk.green(srcDir)}.`); process.exit(1); } const { config, packageJson } = context; const packageType = packageJson.type === "module" ? "EXPLICIT_ESM" : packageJson.type === "commonjs" ? "EXPLICIT_CJS" : !packageJson.type ? "IMPLICIT_CJS" : "INVALID"; config.build = { // Use the inferred output directory defined in tsconfig.json. outDir, emptyOutDir: true, // We don't need to minify this kind of project. minify: false, sourcemap: true, lib: { entry, fileName: "[name]", // Infer output format based on the "type" setting in package.json. formats: packageType === "EXPLICIT_ESM" ? ["es"] : ["cjs"] }, rollupOptions: { logLevel: "silent", output: { preserveModules: true, preserveModulesRoot: srcDir } } }; const packageTypeLabel = { IMPLICIT_CJS: "cjs", EXPLICIT_CJS: "cjs", EXPLICIT_ESM: "esm", INVALID: "" }; if (command === "build") { process.stdout.write("\n"); await log.info(prefix, [ chalk.green(path.parse(srcDir).name), chalk.whiteBright("→"), chalk.green(path.parse(outDir).name), `${chalk.gray("as")} ${chalk.green(packageTypeLabel[packageType])}`, isWatchMode && `${chalk.gray("watch:")}${chalk.green("true")}` ].filter(Boolean).join(" ")); process.stdout.write("\n"); } config.test = { name: packageJson.name, root, include: [TEST_FILES], deps: { // Automatically fixes default imports of modules where the intended value // is on an object on the `default` property. interopDefault: true }, coverage: { all: true, // Files to be considered as source when computing coverage. Vitest // requires paths relative to the configured root, and entry files are // already resolved to absolute paths. This un-resolves them to relative // paths. include: entry.map((entry2) => path.relative(root, entry2)) } }; config.plugins.push(typescriptPlugin({ // Ensures we don't emit declarations for test files. exclude: [`**/*.{${TEST_FILE_PATTERNS.join(",")}}.{${BARE_EXTENSIONS.join(",")}}`], // If TypeScript sees .ts files in the project root (configuration files, // for example) it will assume that they need to be compiled and use the // project root as a reference for the directory structure it needs to // create in the output folder. To prevent this, specify the configured // source folder as the root of the compilation. filterRoot: srcDir, compilerOptions: { baseUrl: srcDir, // Ensure we only emit declaration files; all other source should be // processed by Vite/Rollup. emitDeclarationOnly: true, // Only fail the compilation on type-errors when building for production. // This prevents things like Vitest from failing when in watch mode due to // trivial errors like a variable not being used, etc. noEmitOnError: !isWatchMode && mode === "production", // This MUST be set to the same value as config.build.sourcemap or the // plugin will throw an error. sourceMap: config.build.sourcemap, // The plugin will issue a warning if this is set a value other than // 'ESNext'. Because we are only using it to emit declaration files, this // setting has no effect on Rollup's output. module: "ESNext" } })); config.plugins.push(tsconfigPathsPlugin({ root })); const { tsConfigPath } = context; config.plugins.push(tscAliasPlugin({ configFile: tsConfigPath })); config.plugins.push(nodeExternalPlugin({ root })); config.plugins.push(preserveShebangPlugin()); config.plugins.push(executablePlugin()); if (filesToCopy.length > 0) { config.plugins.push(viteStaticCopy({ targets: filesToCopy.map((filePath) => { const src = path.relative(root, filePath); const dest = path.dirname(src).split(path.sep).slice(1).join(path.sep); return { src, dest }; }) })); } config.plugins.push(cleanupPlugin({ // Removes empty .js files created when .ts files only export types. removeEmptyChunks: true })); }); export { node }; //# sourceMappingURL=vite.js.map