@knightly/vite
Version:
Native-ESM powered web dev build tool. It's fast.
417 lines • 18.5 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ssrBuild = exports.build = exports.createBaseRollupPlugins = exports.onRollupWarning = void 0;
const path_1 = __importDefault(require("path"));
const fs_extra_1 = __importDefault(require("fs-extra"));
const chalk_1 = __importDefault(require("chalk"));
const utils_1 = require("../utils");
const resolver_1 = require("../resolver");
const buildPluginResolve_1 = require("./buildPluginResolve");
const buildPluginHtml_1 = require("./buildPluginHtml");
const buildPluginCss_1 = require("./buildPluginCss");
const buildPluginAsset_1 = require("./buildPluginAsset");
const buildPluginEsbuild_1 = require("./buildPluginEsbuild");
const buildPluginReplace_1 = require("./buildPluginReplace");
const esbuildService_1 = require("../esbuildService");
const config_1 = require("../config");
const transform_1 = require("../transform");
const hash_sum_1 = __importDefault(require("hash-sum"));
const cssUtils_1 = require("../utils/cssUtils");
const buildPluginWasm_1 = require("./buildPluginWasm");
const writeColors = {
[0 /* JS */]: chalk_1.default.cyan,
[1 /* CSS */]: chalk_1.default.magenta,
[2 /* ASSET */]: chalk_1.default.green,
[3 /* HTML */]: chalk_1.default.blue,
[4 /* SOURCE_MAP */]: chalk_1.default.gray
};
const warningIgnoreList = [`CIRCULAR_DEPENDENCY`, `THIS_IS_UNDEFINED`];
const dynamicImportWarningIgnoreList = [
`Unsupported expression`,
`statically analyzed`
];
const isBuiltin = require('isbuiltin');
function onRollupWarning(spinner, options) {
return (warning, warn) => {
if (warning.code === 'UNRESOLVED_IMPORT') {
let message;
const id = warning.source;
const importer = warning.importer;
if (isBuiltin(id)) {
let importingDep;
if (importer) {
const pkg = JSON.parse(utils_1.lookupFile(importer, ['package.json']) || `{}`);
if (pkg.name) {
importingDep = pkg.name;
}
}
const allowList = options && options.allowNodeBuiltins;
if (importingDep && allowList && allowList.includes(importingDep)) {
return;
}
const dep = importingDep
? `Dependency ${chalk_1.default.yellow(importingDep)}`
: `A dependency`;
message =
`${dep} is attempting to import Node built-in module ${chalk_1.default.yellow(id)}.\n` +
`This will not work in a browser environment.\n` +
`Imported by: ${chalk_1.default.gray(importer)}`;
}
else {
message =
`[vite]: Rollup failed to resolve import "${warning.source}" from "${warning.importer}".\n` +
`This is most likely unintended because it can break your application at runtime.\n` +
`If you do want to externalize this module explicitly add it to\n` +
`\`rollupInputOptions.external\``;
}
if (spinner) {
spinner.stop();
}
throw new Error(message);
}
if (warning.plugin === 'rollup-plugin-dynamic-import-variables' &&
dynamicImportWarningIgnoreList.some((msg) => warning.message.includes(msg))) {
return;
}
if (!warningIgnoreList.includes(warning.code)) {
// ora would swallow the console.warn if we let it keep running
// https://github.com/sindresorhus/ora/issues/90
if (spinner) {
spinner.stop();
}
warn(warning);
if (spinner) {
spinner.start();
}
}
};
}
exports.onRollupWarning = onRollupWarning;
/**
* Creates non-application specific plugins that are shared between the main
* app and the dependencies. This is used by the `optimize` command to
* pre-bundle dependencies.
*/
async function createBaseRollupPlugins(root, resolver, options) {
const { rollupInputOptions = {}, transforms = [], vueCustomBlockTransforms = {}, enableEsbuild = true, enableRollupPluginVue = true } = options;
const { nodeResolve } = require('@rollup/plugin-node-resolve');
const dynamicImport = require('rollup-plugin-dynamic-import-variables');
return [
// vite:resolve
buildPluginResolve_1.createBuildResolvePlugin(root, resolver),
// vite:esbuild
enableEsbuild ? await buildPluginEsbuild_1.createEsbuildPlugin(options.jsx) : null,
// vue
enableRollupPluginVue ? await createVuePlugin(root, options) : null,
require('@rollup/plugin-json')({
preferConst: true,
indent: ' ',
compact: false,
namedExports: true
}),
// user transforms
...(transforms.length || Object.keys(vueCustomBlockTransforms).length
? [transform_1.createBuildJsTransformPlugin(transforms, vueCustomBlockTransforms)]
: []),
nodeResolve({
rootDir: root,
extensions: resolver_1.supportedExts,
preferBuiltins: false,
dedupe: options.rollupDedupe || [],
mainFields: resolver_1.mainFields
}),
require('@rollup/plugin-commonjs')({
extensions: ['.js', '.cjs']
}),
dynamicImport({
warnOnError: true,
include: [/\.js$/],
exclude: [/node_modules/]
}),
// #728 user plugins should apply after `@rollup/plugin-commonjs`
...(rollupInputOptions.plugins || [])
].filter(Boolean);
}
exports.createBaseRollupPlugins = createBaseRollupPlugins;
async function createVuePlugin(root, { vueCustomBlockTransforms = {}, rollupPluginVueOptions, cssPreprocessOptions, cssModuleOptions, vueCompilerOptions, vueTransformAssetUrls = {}, vueTemplatePreprocessOptions = {} }) {
const { options: postcssOptions, plugins: postcssPlugins } = await cssUtils_1.resolvePostcssOptions(root, true);
if (typeof vueTransformAssetUrls === 'object') {
vueTransformAssetUrls = {
includeAbsolute: true,
...vueTransformAssetUrls
};
}
return require('rollup-plugin-vue')({
...rollupPluginVueOptions,
templatePreprocessOptions: {
...vueTemplatePreprocessOptions,
pug: {
doctype: 'html',
...(vueTemplatePreprocessOptions && vueTemplatePreprocessOptions.pug)
}
},
transformAssetUrls: vueTransformAssetUrls,
postcssOptions,
postcssPlugins,
preprocessStyles: true,
preprocessOptions: cssPreprocessOptions,
preprocessCustomRequire: (id) => require(utils_1.resolveFrom(root, id)),
compilerOptions: vueCompilerOptions,
cssModulesOptions: {
localsConvention: 'camelCase',
generateScopedName: (local, filename) => `${local}_${hash_sum_1.default(filename)}`,
...cssModuleOptions,
...(rollupPluginVueOptions && rollupPluginVueOptions.cssModulesOptions)
},
customBlocks: Object.keys(vueCustomBlockTransforms)
});
}
/**
* Bundles the app for production.
* Returns a Promise containing the build result.
*/
async function build(options) {
const { root = process.cwd(), base = '/', outDir = path_1.default.resolve(root, 'dist'), assetsDir = '_assets', assetsInlineLimit = 4096, cssCodeSplit = true, alias = {}, resolvers = [], rollupInputOptions = {}, rollupOutputOptions = {}, emitIndex = true, emitAssets = true, write = true, minify = true, terserOption = {}, esbuildTarget = 'es2020', enableEsbuild = true, silent = false, sourcemap = false, shouldPreload = null, env = {}, mode: configMode = 'production', define: userDefineReplacements = {}, cssPreprocessOptions, cssModuleOptions = {} } = options;
const isTest = process.env.NODE_ENV === 'test';
const resolvedMode = process.env.VITE_ENV || configMode;
const start = Date.now();
let spinner;
const msg = `Building ${configMode} bundle...`;
if (!silent) {
if (process.env.DEBUG || isTest) {
console.log(msg);
}
else {
spinner = require('ora')(msg + '\n').start();
}
}
await fs_extra_1.default.emptyDir(outDir);
const indexPath = path_1.default.resolve(root, 'index.html');
const publicBasePath = base.replace(/([^/])$/, '$1/'); // ensure ending slash
const resolvedAssetsPath = path_1.default.join(outDir, assetsDir);
const resolver = resolver_1.createResolver(root, resolvers, alias);
const { htmlPlugin, renderIndex } = await buildPluginHtml_1.createBuildHtmlPlugin(root, indexPath, publicBasePath, assetsDir, assetsInlineLimit, resolver, shouldPreload);
const basePlugins = await createBaseRollupPlugins(root, resolver, options);
// https://github.com/darionco/rollup-plugin-web-worker-loader
// configured to support `import Worker from './my-worker?worker'`
// this plugin relies on resolveId and must be placed before node-resolve
// since the latter somehow swallows ids with query strings since 8.x
basePlugins.splice(basePlugins.findIndex((p) => p.name.includes('node-resolve')), 0, require('rollup-plugin-web-worker-loader')({
targetPlatform: 'browser',
pattern: /(.+)\?worker$/,
extensions: resolver_1.supportedExts,
sourcemap: false // it's inlined so it bloats the bundle
}));
// user env variables loaded from .env files.
// only those prefixed with VITE_ are exposed.
const userClientEnv = {};
const userEnvReplacements = {};
Object.keys(env).forEach((key) => {
if (key.startsWith(`VITE_`)) {
userEnvReplacements[`import.meta.env.${key}`] = JSON.stringify(env[key]);
userClientEnv[key] = env[key];
}
});
const builtInClientEnv = {
BASE_URL: publicBasePath,
MODE: configMode,
DEV: resolvedMode !== 'production',
PROD: resolvedMode === 'production'
};
const builtInEnvReplacements = {};
Object.keys(builtInClientEnv).forEach((key) => {
builtInEnvReplacements[`import.meta.env.${key}`] = JSON.stringify(builtInClientEnv[key]);
});
Object.keys(userDefineReplacements).forEach((key) => {
userDefineReplacements[key] = JSON.stringify(userDefineReplacements[key]);
});
// lazy require rollup so that we don't load it when only using the dev server
// importing it just for the types
const rollup = require('rollup').rollup;
const bundle = await rollup({
input: path_1.default.resolve(root, 'index.html'),
preserveEntrySignatures: false,
treeshake: { moduleSideEffects: 'no-external' },
onwarn: onRollupWarning(spinner, options.optimizeDeps),
...rollupInputOptions,
plugins: [
...basePlugins,
// vite:html
htmlPlugin,
// we use a custom replacement plugin because @rollup/plugin-replace
// performs replacements twice, once at transform and once at renderChunk
// - which makes it impossible to exclude Vue templates from it since
// Vue templates are compiled into js and included in chunks.
buildPluginReplace_1.createReplacePlugin((id) => !/\?vue&type=template/.test(id) &&
// also exclude css and static assets for performance
!cssUtils_1.isCSSRequest(id) &&
!utils_1.isStaticAsset(id), {
...config_1.defaultDefines,
...userDefineReplacements,
...userEnvReplacements,
...builtInEnvReplacements,
'import.meta.env.': `({}).`,
'import.meta.env': JSON.stringify({
...userClientEnv,
...builtInClientEnv
}),
'process.env.NODE_ENV': JSON.stringify(resolvedMode),
'process.env.': `({}).`,
'process.env': JSON.stringify({ NODE_ENV: resolvedMode }),
'import.meta.hot': `false`
}, sourcemap),
// vite:css
buildPluginCss_1.createBuildCssPlugin({
root,
publicBase: publicBasePath,
assetsDir,
minify,
inlineLimit: assetsInlineLimit,
cssCodeSplit,
preprocessOptions: cssPreprocessOptions,
modulesOptions: cssModuleOptions
}),
// vite:asset
buildPluginAsset_1.createBuildAssetPlugin(root, publicBasePath, assetsDir, assetsInlineLimit),
buildPluginWasm_1.createBuildWasmPlugin(root, publicBasePath, assetsDir, assetsInlineLimit),
enableEsbuild
? buildPluginEsbuild_1.createEsbuildRenderChunkPlugin(esbuildTarget, minify === 'esbuild')
: undefined,
// minify with terser
// this is the default which has better compression, but slow
// the user can opt-in to use esbuild which is much faster but results
// in ~8-10% larger file size.
minify && minify !== 'esbuild'
? require('rollup-plugin-terser').terser(terserOption)
: undefined
].filter(Boolean)
});
const { output } = await bundle.generate({
format: 'es',
sourcemap,
entryFileNames: `[name].[hash].js`,
chunkFileNames: `[name].[hash].js`,
...rollupOutputOptions
});
spinner && spinner.stop();
const indexHtml = emitIndex ? renderIndex(output) : '';
if (write) {
const cwd = process.cwd();
const writeFile = async (filepath, content, type) => {
await fs_extra_1.default.ensureDir(path_1.default.dirname(filepath));
await fs_extra_1.default.writeFile(filepath, content);
if (!silent) {
const needCompression = type === 0 /* JS */ ||
type === 1 /* CSS */ ||
type === 3 /* HTML */;
const compressed = needCompression
? `, brotli: ${(require('brotli-size').sync(content) / 1024).toFixed(2)}kb`
: ``;
console.log(`${chalk_1.default.gray(`[write]`)} ${writeColors[type](path_1.default.relative(cwd, filepath))} ${(content.length / 1024).toFixed(2)}kb${compressed}`);
}
};
await fs_extra_1.default.ensureDir(outDir);
// write js chunks and assets
for (const chunk of output) {
if (chunk.type === 'chunk') {
// write chunk
const filepath = path_1.default.join(resolvedAssetsPath, chunk.fileName);
let code = chunk.code;
if (chunk.map) {
code += `\n//# sourceMappingURL=${path_1.default.basename(filepath)}.map`;
}
await writeFile(filepath, code, 0 /* JS */);
if (chunk.map) {
await writeFile(filepath + '.map', chunk.map.toString(), 4 /* SOURCE_MAP */);
}
}
else if (emitAssets) {
if (!chunk.source)
continue;
// write asset
const filepath = path_1.default.join(resolvedAssetsPath, chunk.fileName);
await writeFile(filepath, chunk.source, chunk.fileName.endsWith('.css') ? 1 /* CSS */ : 2 /* ASSET */);
}
}
// write html
if (indexHtml && emitIndex) {
await writeFile(path_1.default.join(outDir, 'index.html'), indexHtml, 3 /* HTML */);
}
// copy over /public if it exists
if (emitAssets) {
const publicDir = path_1.default.resolve(root, 'public');
if (fs_extra_1.default.existsSync(publicDir)) {
for (const file of await fs_extra_1.default.readdir(publicDir)) {
await fs_extra_1.default.copy(path_1.default.join(publicDir, file), path_1.default.resolve(outDir, file));
}
}
}
}
if (!silent) {
console.log(`Build completed in ${((Date.now() - start) / 1000).toFixed(2)}s.\n`);
}
// stop the esbuild service after each build
await esbuildService_1.stopService();
return {
assets: output,
html: indexHtml
};
}
exports.build = build;
/**
* Bundles the app in SSR mode.
* - All Vue dependencies are automatically externalized
* - Imports to dependencies are compiled into require() calls
* - Templates are compiled with SSR specific optimizations.
*/
async function ssrBuild(options) {
const { rollupInputOptions, rollupOutputOptions, rollupPluginVueOptions } = options;
return build({
outDir: path_1.default.resolve(options.root || process.cwd(), 'dist-ssr'),
assetsDir: '.',
...options,
rollupPluginVueOptions: {
...rollupPluginVueOptions,
target: 'node'
},
rollupInputOptions: {
...rollupInputOptions,
external: resolveExternal(rollupInputOptions && rollupInputOptions.external)
},
rollupOutputOptions: {
...rollupOutputOptions,
format: 'cjs',
exports: 'named',
entryFileNames: '[name].js'
},
emitIndex: false,
emitAssets: false,
cssCodeSplit: false,
minify: false
});
}
exports.ssrBuild = ssrBuild;
function resolveExternal(userExternal) {
const required = ['vue', /^@vue\//];
if (!userExternal) {
return required;
}
if (Array.isArray(userExternal)) {
return [...required, ...userExternal];
}
else if (typeof userExternal === 'function') {
return (src, importer, isResolved) => {
if (src === 'vue' || /^@vue\//.test(src)) {
return true;
}
return userExternal(src, importer, isResolved);
};
}
else {
return [...required, userExternal];
}
}
//# sourceMappingURL=index.js.map