vike
Version:
The Framework *You* Control - Next.js & Nuxt alternative for unprecedented flexibility and dependability.
229 lines (228 loc) • 12 kB
JavaScript
;
// Remove this workaround if the other workaround config.build.ssrEmitAssets turns out to be reliable.
// - Remove this file then revert this commit: https://github.com/vikejs/vike/commit/805a18974f13420a78fcc30fdd676696e405c3ca
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.extractAssetsRE = void 0;
exports.pluginExtractAssets = pluginExtractAssets;
const utils_js_1 = require("../utils.js");
const virtualFiles_js_1 = require("../../shared/virtualFiles.js");
const extractAssetsQuery_js_1 = require("../../shared/extractAssetsQuery.js");
const isAsset_js_1 = require("../shared/isAsset.js");
const parseEsModule_js_1 = require("../shared/parseEsModule.js");
const picocolors_1 = __importDefault(require("@brillout/picocolors"));
const handleAssetsManifest_js_1 = require("./pluginBuild/handleAssetsManifest.js");
const resolveVikeConfigInternal_js_1 = require("../shared/resolveVikeConfigInternal.js");
const assertV1Design_js_1 = require("../../shared/assertV1Design.js");
const normalizeId_js_1 = require("../shared/normalizeId.js");
const isViteServerBuild_js_1 = require("../shared/isViteServerBuild.js");
const getPageAssets_js_1 = require("../../runtime/renderPage/getPageAssets.js");
const extractAssetsRE = /(\?|&)extractAssets(?:&|$)/;
exports.extractAssetsRE = extractAssetsRE;
const rawRE = /(\?|&)raw(?:&|$)/;
const urlRE = /(\?|&)url(?:&|$)/;
const EMPTY_MODULE_ID = 'virtual:vike:empty-module';
const debug = (0, utils_js_1.createDebugger)('vike:pluginExtractAssets');
function pluginExtractAssets() {
let config;
let vikeConfig;
let isFixEnabled;
return [
// This plugin removes all JavaScript from server-side only code, so that only CSS imports remains. (And also static files imports e.g. `import logoURL from './logo.svg.js'`).
{
name: 'vike:pluginExtractAssets:remove-javaScript',
// In dev, things just work. (Because Vite's module graph erroneously conflates the Vite server-side importees with the client-side importees.)
apply: 'build',
enforce: 'post',
async transform(src, id, options) {
id = (0, normalizeId_js_1.normalizeId)(id);
if (!extractAssetsRE.test(id)) {
return;
}
if (isFixEnabled) {
// I'm guessing isFixEnabled can only be true when mixing both designs: https://github.com/vikejs/vike/issues/1480
(0, assertV1Design_js_1.assertV1Design)(vikeConfig._pageConfigs, true);
(0, utils_js_1.assert)(false);
}
const includeAssetsImportedByServer = (0, getPageAssets_js_1.resolveIncludeAssetsImportedByServer)(vikeConfig.config);
(0, utils_js_1.assert)(includeAssetsImportedByServer);
(0, utils_js_1.assert)(!(0, isViteServerBuild_js_1.isViteServerBuild_safe)(config, options));
const importStatements = await (0, parseEsModule_js_1.getImportStatements)(src);
const moduleNames = getImportedModules(importStatements);
const code = moduleNames.map((moduleName) => `import '${moduleName}';`).join('\n');
debugTransformResult(id, code, importStatements);
return (0, utils_js_1.rollupSourceMapRemove)(code);
},
},
// This plugin appends `?extractAssets` to module IDs
{
name: 'vike:pluginExtractAssets:append-extractAssets-query',
apply: 'build',
// We ensure this plugin to be run before:
// - rollup's `alias` plugin; https://github.com/rollup/plugins/blob/5363f55aa1933b6c650832b08d6a54cb9ea64539/packages/alias/src/index.ts
// - Vite's `vite:resolve` plugin; https://github.com/vitejs/vite/blob/d649daba7682791178b711d9a3e44a6b5d00990c/packages/vite/src/node/plugins/resolve.ts#L105
enforce: 'pre',
async resolveId(source, importer, options) {
if ((0, isViteServerBuild_js_1.isViteServerBuild_safe)(config, options)) {
// When building for the server, there should never be a `?extractAssets` query
(0, utils_js_1.assert)(!extractAssetsRE.test(source));
(0, utils_js_1.assert)(importer === undefined || !extractAssetsRE.test(importer));
return;
}
// If there is no `importer` then `module` is an entry.
// We don't need to append `?extractAssets` to entries because they already have `?extractAssets` as Vike appends `?extractAssets` to entries by using `import.meta.glob('/**/*.page.server.js', { as: "extractAssets" })` (see `generateImportGlobs.ts`).
if (!importer) {
return;
}
// We only append `?extractAssets` if the parent module has `?extractAssets`
if (!extractAssetsRE.test(importer)) {
return;
}
const includeAssetsImportedByServer = (0, getPageAssets_js_1.resolveIncludeAssetsImportedByServer)(vikeConfig.config);
(0, utils_js_1.assert)(includeAssetsImportedByServer);
let resolution = null;
try {
resolution = await this.resolve(source, importer, { skipSelf: true, ...options });
}
catch { }
// Sometimes Rollup fails to resolve. If it fails to resolve, we assume the dependency to be an npm package and we skip it. (I guess Rollup should always be able to resolve local dependencies?)
if (!resolution)
return emptyModule(source, importer);
const { id: file, external } = resolution;
// Nothing is externalized when building for the client-side
(0, utils_js_1.assert)(external === false);
// We include:
// - CSS(/LESS/SCSS/...) files
// - Asset files (`.svg`, `.pdf`, ...)
// - URL imports (e.g. `import scriptUrl from './script.js?url.js'`)
if (utils_js_1.styleFileRE.test(file) || (0, isAsset_js_1.isAsset)(file) || urlRE.test(file)) {
debugOperation('INCLUDED', file, importer);
return resolution;
}
// We erase `source` if its file doesn't contain JavaScript
if (!(0, utils_js_1.isScriptFile)(file)) {
return emptyModule(file, importer);
}
// If the import path resolves to a file in `node_modules/`, we ignore that file:
// - Direct CSS dependencies are included though, such as `import 'bootstrap/theme/dark.css'`. (Because the above if-branch for CSS files will add the file.)
// - Loading CSS from a library (living in `node_modules/`) in a non-direct way is unconventional; we can safely not support this case. (I'm not aware of any library that does this.)
(0, utils_js_1.assertPosixPath)(file);
if (file.includes('/node_modules/')) {
return emptyModule(file, importer);
}
// When a library is symlinked, it lives outside `root`.
(0, utils_js_1.assertPosixPath)(config.root);
if (!file.startsWith(config.root)) {
return emptyModule(file, importer);
}
return appendExtractAssetsQuery(file, importer);
},
},
{
name: 'vike:pluginExtractAssets-3',
apply: 'build',
load(id) {
if (!(0, virtualFiles_js_1.isVirtualFileId)(id))
return undefined;
id = (0, virtualFiles_js_1.getVirtualFileId)(id);
if (id === EMPTY_MODULE_ID) {
return '// Erased by vike:pluginExtractAssets';
}
},
config() {
if (debug.isActivated) {
return { logLevel: 'silent' };
}
},
},
{
name: 'vike:pluginExtractAssets-4',
async configResolved(config_) {
config = config_;
vikeConfig = await (0, resolveVikeConfigInternal_js_1.getVikeConfigInternal)();
isFixEnabled = (0, handleAssetsManifest_js_1.handleAssetsManifest_isFixEnabled)(config);
if (!isFixEnabled) {
// https://github.com/vikejs/vike/issues/1060
(0, utils_js_1.assertUsage)(!config.plugins.find((p) => p.name === 'vite-tsconfig-paths'), 'vite-tsconfig-paths not supported, remove it and use vite.config.js#resolve.alias instead');
}
},
},
];
}
function emptyModule(file, importer) {
debugOperation('NUKED', file, importer);
return (0, virtualFiles_js_1.resolveVirtualFileId)(EMPTY_MODULE_ID);
}
function appendExtractAssetsQuery(file, importer) {
debugOperation('TRANSFORMED', file, importer);
return (0, extractAssetsQuery_js_1.extractAssetsAddQuery)(file);
}
function getImportedModules(importStatements) {
const moduleNames = importStatements
.map(analyzeImport)
.filter(({ moduleName, skip }) => {
debug(`import ${skip ? 'SKIPPED' : 'INCLUDED'} : ${moduleName}`);
return !skip;
})
.map(({ moduleName }) => {
(0, utils_js_1.assert)(moduleName);
return moduleName;
});
return moduleNames;
}
function analyzeImport(importStatement) {
const { a: assertion, n } = importStatement;
// `n` is `undefined` for dynamic imports with variable, e.g. `import(moduleName)`
if (n === undefined)
return { moduleName: null, skip: true };
const moduleName = n;
// Remove assertions such as:
// - `import json from './json.json' assert { type: 'json.js' }`
// - `import('asdf', { assert: { type: 'json' }})
if (assertion !== -1) {
return { moduleName, skip: true };
}
// Add imports such as `import logoUrl from './logo.svg?url.js'`
if (urlRE.test(moduleName)) {
return { moduleName, skip: false };
}
// Remove imports such as `import logoUrl from './logo.svg?raw.js'`
if (rawRE.test(moduleName)) {
return { moduleName, skip: true };
}
/* We shouldn't do this because of aliased imports
if (!moduleName.startsWith('.')) {
return { moduleName, skip: true }
}
*/
// It seems like we need to manually nuke `react`; it seems that what the React runtime `@vitejs/react` injects is not picked up by our `resolveId` hook.
if (/^react($|\/)/.test(moduleName)) {
return { moduleName, skip: true };
}
return { moduleName, skip: false };
}
function debugOperation(operation, id, importer) {
debug(`import ${operation}: ${id} (importer: ${importer})`);
}
function stringifyImportStatements(importStatements) {
const importsStr = importStatements.map((importStatement) => {
const { a: assertion, n } = importStatement;
// `n` is `undefined` for dynamic imports with variable, e.g. `import(moduleName)`
if (n === undefined)
return 'import(...)';
let importStr = `import '${n}'`;
if (assertion !== -1) {
importStr += ' assert { ... }';
}
return importStr;
});
return importsStr;
}
function debugTransformResult(id, code, importStatements) {
const importStatementsStr = stringifyImportStatements(importStatements)
.map((s) => picocolors_1.default.cyan(s))
.join(', ');
debug(`source TRANSFORMED: ${id} (CODE: ${picocolors_1.default.cyan(code.split('\n').join(' '))}, IMPORTS: ${importStatementsStr})`);
}