vike
Version:
The Framework *You* Control - Next.js & Nuxt alternative for unprecedented flexibility and dependability.
254 lines (253 loc) • 11.8 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.crawlPlusFiles = crawlPlusFiles;
exports.isPlusFile = isPlusFile;
exports.getPlusFileValueConfigName = getPlusFileValueConfigName;
const utils_js_1 = require("../../../../utils.js");
const path_1 = __importDefault(require("path"));
const tinyglobby_1 = require("tinyglobby");
const child_process_1 = require("child_process");
const util_1 = require("util");
const transpileAndExecuteFile_js_1 = require("./transpileAndExecuteFile.js");
const getEnvVarObject_js_1 = require("../../../../shared/getEnvVarObject.js");
const picocolors_1 = __importDefault(require("@brillout/picocolors"));
const picomatch_1 = __importDefault(require("picomatch"));
const ignorePatternsBuiltIn_js_1 = require("./crawlPlusFiles/ignorePatternsBuiltIn.js");
const execA = (0, util_1.promisify)(child_process_1.exec);
const debug = (0, utils_js_1.createDebugger)('vike:crawl');
(0, utils_js_1.assertIsNotProductionRuntime)();
(0, utils_js_1.assertIsSingleModuleInstance)('getVikeConfig/crawlPlusFiles.ts');
let gitIsNotUsable = false;
async function crawlPlusFiles(userRootDir) {
(0, utils_js_1.assertPosixPath)(userRootDir);
(0, utils_js_1.assertFilePathAbsoluteFilesystem)(userRootDir);
const userSettings = getUserSettings();
const { ignorePatterns, ignoreMatchers } = getIgnore(userSettings);
// Crawl
const filesGit = userSettings.git !== false && (await gitLsFiles(userRootDir, ignorePatterns, ignoreMatchers));
const filesGitNothingFound = !filesGit || filesGit.length === 0;
const filesGlob = (filesGitNothingFound || debug.isActivated) && (await tinyglobby(userRootDir, ignorePatterns));
let files = !filesGitNothingFound
? filesGit
: // Fallback to tinyglobby for users that dynamically generate plus files. (Assuming that no plus file is found because of the user's .gitignore list.)
filesGlob;
(0, utils_js_1.assert)(files);
if (debug.isActivated) {
(0, utils_js_1.assert)(filesGit);
(0, utils_js_1.assert)(filesGlob);
(0, utils_js_1.assertWarning)((0, utils_js_1.deepEqual)(filesGlob.slice().sort(), filesGit.slice().sort()), "Git and glob results aren't matching.", { onlyOnce: false });
}
// Filter build files
files = files.filter((filePath) => !(0, transpileAndExecuteFile_js_1.isTemporaryBuildFile)(filePath));
// Normalize
const plusFiles = files.map((filePath) => {
// Both `$ git-ls files` and tinyglobby return posix paths
(0, utils_js_1.assertPosixPath)(filePath);
(0, utils_js_1.assert)(!filePath.startsWith(userRootDir));
const filePathAbsoluteUserRootDir = path_1.default.posix.join('/', filePath);
(0, utils_js_1.assert)(isPlusFile(filePathAbsoluteUserRootDir));
return { filePathAbsoluteUserRootDir };
});
return plusFiles;
}
// Same as tinyglobby() but using `$ git ls-files`
async function gitLsFiles(userRootDir, ignorePatterns, ignoreMatchers) {
if (gitIsNotUsable)
return null;
// Preserve UTF-8 file paths.
// https://github.com/vikejs/vike/issues/1658
// https://stackoverflow.com/questions/22827239/how-to-make-git-properly-display-utf-8-encoded-pathnames-in-the-console-window/22828826#22828826
// https://stackoverflow.com/questions/15884180/how-do-i-override-git-configuration-options-by-command-line-parameters/15884261#15884261
const preserveUTF8 = '-c core.quotepath=off';
const cmd = [
'git',
preserveUTF8,
'ls-files',
// Performance gain seems negligible: https://github.com/vikejs/vike/pull/1688#issuecomment-2166206648
...utils_js_1.scriptFileExtensionList.map((ext) => `"**/+*.${ext}" "+*.${ext}"`),
// Performance gain is non-negligible.
// - https://github.com/vikejs/vike/pull/1688#issuecomment-2166206648
// - When node_modules/ is untracked the performance gain could be significant?
...ignorePatterns.map((pattern) => `--exclude="${pattern}"`),
// --others --exclude-standard => list untracked files (--others) while using .gitignore (--exclude-standard)
// --cached => list tracked files
'--others --exclude-standard --cached'
].join(' ');
let filesAll;
let filesDeleted;
try {
;
[filesAll, filesDeleted] = await Promise.all([
// Main command
runCmd1(cmd, userRootDir),
// Get tracked but deleted files
runCmd1('git ls-files --deleted', userRootDir)
]);
}
catch (err) {
if (await isGitNotUsable(userRootDir)) {
gitIsNotUsable = true;
return null;
}
throw err;
}
if (debug.isActivated) {
debug('[git] userRootDir:', userRootDir);
debug('[git] cmd:', cmd);
debug('[git] result:', filesAll);
debug('[git] filesDeleted:', filesDeleted);
}
const files = [];
for (const filePath of filesAll) {
// + file?
if (!path_1.default.posix.basename(filePath).startsWith('+'))
continue;
// We have to repeat the same exclusion logic here because the option --exclude of `$ git ls-files` only applies to untracked files. (We use --exclude only to speed up the `$ git ls-files` command.)
if (ignoreMatchers.some((m) => m(filePath)))
continue;
// JavaScript file?
if (!(0, utils_js_1.isScriptFile)(filePath))
continue;
// Deleted?
if (filesDeleted.includes(filePath))
continue;
files.push(filePath);
}
return files;
}
// Same as gitLsFiles() but using tinyglobby
async function tinyglobby(userRootDir, ignorePatterns) {
const pattern = `**/+*.${utils_js_1.scriptFileExtensions}`;
const options = {
ignore: ignorePatterns,
cwd: userRootDir,
dot: false
};
const files = await (0, tinyglobby_1.glob)(pattern, options);
// Make build deterministic, in order to get a stable generated hash for dist/client/assets/entries/entry-client-routing.${hash}.js
// https://github.com/vikejs/vike/pull/1750
files.sort();
if (debug.isActivated) {
debug('[glob] pattern:', pattern);
debug('[glob] options:', options);
debug('[glob] result:', files);
}
return files;
}
// Whether Git is installed and whether we can use it
async function isGitNotUsable(userRootDir) {
// Check Git version
{
const res = await runCmd2('git --version', userRootDir);
if ('err' in res)
return true;
let { stdout, stderr } = res;
(0, utils_js_1.assert)(stderr === '');
const prefix = 'git version ';
(0, utils_js_1.assert)(stdout.startsWith(prefix));
const gitVersion = stdout.slice(prefix.length);
// - Works with Git 2.43.1 but also (most certainly) with earlier versions.
// - We didn't bother test which is the earliest verision that works.
// - Git 2.32.0 doesn't seem to work: https://github.com/vikejs/vike/discussions/1549
// - Maybe it's because of StackBlitz: looking at the release notes, Git 2.32.0 should be working.
if (!(0, utils_js_1.isVersionOrAbove)(gitVersion, '2.43.1'))
return true;
}
// Is userRootDir inside a Git repository?
{
const res = await runCmd2('git rev-parse --is-inside-work-tree', userRootDir);
if ('err' in res)
return true;
let { stdout, stderr } = res;
(0, utils_js_1.assert)(stderr === '');
(0, utils_js_1.assert)(stdout === 'true');
return false;
}
}
async function runCmd1(cmd, cwd) {
const { stdout } = await execA(cmd, {
cwd,
// https://github.com/vikejs/vike/issues/1982
maxBuffer: Infinity
});
/* Not always true: https://github.com/vikejs/vike/issues/1440#issuecomment-1892831303
assert(res.stderr === '')
*/
return stdout.toString().split('\n').filter(Boolean);
}
async function runCmd2(cmd, cwd) {
let res;
try {
res = await execA(cmd, { cwd });
}
catch (err) {
return { err };
}
let { stdout, stderr } = res;
stdout = stdout.toString().trim();
stderr = stderr.toString().trim();
return { stdout, stderr };
}
function getUserSettings() {
const userSettings = (0, getEnvVarObject_js_1.getEnvVarObject)('VIKE_CRAWL') ?? {};
const wrongUsage = (settingName, settingType) => `Setting ${picocolors_1.default.cyan(settingName)} in VIKE_CRAWL should be a ${picocolors_1.default.cyan(settingType)}`;
(0, utils_js_1.assertUsage)((0, utils_js_1.hasProp)(userSettings, 'git', 'boolean') || (0, utils_js_1.hasProp)(userSettings, 'git', 'undefined'), wrongUsage('git', 'boolean'));
(0, utils_js_1.assertUsage)((0, utils_js_1.hasProp)(userSettings, 'ignore', 'string[]') ||
(0, utils_js_1.hasProp)(userSettings, 'ignore', 'string') ||
(0, utils_js_1.hasProp)(userSettings, 'ignore', 'undefined'), wrongUsage('git', 'string or an array of strings'));
(0, utils_js_1.assertUsage)((0, utils_js_1.hasProp)(userSettings, 'ignoreBuiltIn', 'boolean') || (0, utils_js_1.hasProp)(userSettings, 'ignoreBuiltIn', 'undefined'), wrongUsage('ignoreBuiltIn', 'boolean'));
const settingNames = ['git', 'ignore', 'ignoreBuiltIn'];
Object.keys(userSettings).forEach((name) => {
(0, utils_js_1.assertUsage)(settingNames.includes(name), `Unknown setting ${picocolors_1.default.bold(picocolors_1.default.red(name))} in VIKE_CRAWL`);
});
return userSettings;
}
function isPlusFile(filePath) {
(0, utils_js_1.assertPosixPath)(filePath);
if ((0, transpileAndExecuteFile_js_1.isTemporaryBuildFile)(filePath))
return false;
const fileName = filePath.split('/').pop();
return fileName.startsWith('+');
}
function getPlusFileValueConfigName(filePath) {
if (!isPlusFile(filePath))
return null;
const fileName = path_1.default.posix.basename(filePath);
// assertNoUnexpectedPlusSign(filePath, fileName)
const basename = fileName.split('.')[0];
(0, utils_js_1.assert)(basename.startsWith('+'));
const configName = basename.slice(1);
(0, utils_js_1.assertUsage)(configName !== '', `${filePath} Invalid filename ${fileName}`);
return configName;
}
/* https://github.com/vikejs/vike/issues/1407
function assertNoUnexpectedPlusSign(filePath: string, fileName: string) {
const dirs = path.posix.dirname(filePath).split('/')
dirs.forEach((dir, i) => {
const dirPath = dirs.slice(0, i + 1).join('/')
assertUsage(
!dir.includes('+'),
`Character '+' is a reserved character: remove '+' from the directory name ${dirPath}/`
)
})
assertUsage(
!fileName.slice(1).includes('+'),
`Character '+' is only allowed at the beginning of filenames: make sure ${filePath} doesn't contain any '+' in its filename other than its first letter`
)
}
*/
function getIgnore(userSettings) {
const ignorePatternsSetByUser = [userSettings.ignore].flat().filter(utils_js_1.isNotNullish);
const { ignoreBuiltIn } = userSettings;
const ignorePatterns = [...(ignoreBuiltIn === false ? [] : ignorePatternsBuiltIn_js_1.ignorePatternsBuiltIn), ...ignorePatternsSetByUser];
const ignoreMatchers = ignorePatterns.map((p) => (0, picomatch_1.default)(p, {
// We must pass the same settings than tinyglobby
// https://github.com/SuperchupuDev/tinyglobby/blob/fcfb08a36c3b4d48d5488c21000c95a956d9797c/src/index.ts#L191-L194
dot: false,
nocase: false
}));
return { ignorePatterns, ignoreMatchers };
}