UNPKG

vike

Version:

The Framework *You* Control - Next.js & Nuxt alternative for unprecedented flexibility and dependability.

254 lines (253 loc) 11.8 kB
"use strict"; 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 }; }