UNPKG

fuse-box

Version:

Fuse-Box a bundler that does it right

192 lines (191 loc) • 7.29 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.nodeModuleLookup = exports.findTargetFolder = exports.parseExistingModulePaths = exports.parseAllModulePaths = exports.isNodeModule = void 0; const fs_1 = require("fs"); const path = require("path"); const findUp_1 = require("../utils/findUp"); const utils_1 = require("../utils/utils"); const browserField_1 = require("./browserField"); const fileLookup_1 = require("./fileLookup"); const NODE_MODULE_REGEX = /^(([^\.][\.a-z0-9@\-_]*)(\/)?([_a-z0-9.@-]+)?(\/)?(.*))$/i; function isNodeModule(path) { const matched = path.match(NODE_MODULE_REGEX); if (!matched) return; let [name, b, c] = [matched[2], matched[4], matched[6]]; const result = { name }; if (name[0] === '@') { result.name = name + '/' + b; if (c) { result.target = c; } } else { if (b) { result.target = c ? b + '/' + c : b; } } return result; } exports.isNodeModule = isNodeModule; function parentDir(normalizedPath) { const parent = path.dirname(normalizedPath); if (parent === normalizedPath) return undefined; return parent; } function parseAllModulePaths(fileAbsPath) { const start = path.normalize(fileAbsPath); const paths = []; for (let dir = parentDir(start); dir !== undefined; dir = parentDir(dir)) { const name = path.basename(dir); if (name === 'node_modules') continue; paths.unshift(path.join(dir, 'node_modules')); } return paths; } exports.parseAllModulePaths = parseAllModulePaths; const pathExists = new Map(); function memoizedExists(absPath) { let exists = pathExists.get(absPath); if (exists === undefined) { exists = utils_1.fileExists(absPath); pathExists.set(absPath, exists); } return exists; } function parseExistingModulePaths(fileAbsPath) { const all = parseAllModulePaths(fileAbsPath); const existing = []; for (let i = 0; i < all.length; i++) { memoizedExists(all[i]) && existing.push(all[i]); } return existing; } exports.parseExistingModulePaths = parseExistingModulePaths; function isDirectory(path) { return utils_1.fileExists(path) && fs_1.lstatSync(path).isDirectory(); } const pnp = process.versions.pnp ? require('pnpapi') : undefined; function isFolderUserOwned(realpath) { if (pnp) { // in a pnp environment, anything that is not in an archive is probably user-owned // this should support, workspaces, portals, and unplugged correctly const physical = (pnp.resolveVirtual && pnp.resolveVirtual(realpath)) || realpath; return isDirectory(physical); } else { // in a node_modules environment, anything that is really inside node_modules is *not* user-owned return !/node_modules/.test(realpath); } } const CACHED_LOCAL_MODULES = {}; function findTargetFolder(props, name) { // handle custom modules here if (props.modules) { for (const i in props.modules) { const f = path.join(props.modules[i], name); if (utils_1.fileExists(f)) { const folder = fs_1.realpathSync(f); const isUserOwned = isFolderUserOwned(folder); return { folder, isUserOwned }; } } } // Support for Yarn v2 PnP if (pnp) { try { const folder = pnp.resolveToUnqualified(name, props.filePath, { considerBuiltins: false }); const isUserOwned = isFolderUserOwned(folder); return { folder, isUserOwned }; } catch (e) { // If this is PnP and PnP says it doesn't exist, // don't continue trying the rest of the node_modules stuff return { error: e.message }; } } const paths = parseExistingModulePaths(props.filePath); for (let i = paths.length - 1; i >= 0; i--) { const attempted = path.join(paths[i], name); if (utils_1.fileExists(path.join(attempted, 'package.json'))) { const folder = fs_1.realpathSync(attempted); const isUserOwned = isFolderUserOwned(folder); return { folder, isUserOwned }; } } let localModuleRoot = CACHED_LOCAL_MODULES[props.filePath]; if (localModuleRoot === undefined) { localModuleRoot = CACHED_LOCAL_MODULES[props.filePath] = findUp_1.findUp(props.filePath, 'node_modules'); } if (!!localModuleRoot && paths.indexOf(localModuleRoot) === -1) { const attempted = path.join(localModuleRoot, name); if (utils_1.fileExists(path.join(attempted, 'package.json'))) { const folder = fs_1.realpathSync(attempted); const isUserOwned = isFolderUserOwned(folder); return { folder, isUserOwned }; } } return { error: `Cannot resolve "${name}"` }; } exports.findTargetFolder = findTargetFolder; function nodeModuleLookup(props, parsed) { const { name: moduleName, target } = parsed; // Resolve the module name to a folder const result = findTargetFolder(props, moduleName); if ('error' in result) { return result; } const { folder, isUserOwned } = result; if (!folder) { return { error: `Cannot resolve "${moduleName}"` }; } const packageJSONFile = path.join(folder, 'package.json'); if (!utils_1.fileExists(packageJSONFile)) { return { error: `Failed to find package.json in ${folder} when resolving module ${moduleName}` }; } const json = JSON.parse(utils_1.readFile(packageJSONFile)); // Prepare the package metadata structure and copy a bunch of stuff into it const pkg = { browser: json.browser, fusebox: json['fuse-box'] || undefined, name: moduleName, packageJSONLocation: packageJSONFile, packageRoot: folder, version: json.version || '0.0.0', }; const isBrowser = props.buildTarget === 'browser'; const isEntry = !target; let targetResolver = fileLookup_1.fileLookup; if (isUserOwned && props.tsTargetResolver) targetResolver = props.tsTargetResolver; const resolved = targetResolver({ fileDir: folder, isBrowserBuild: isBrowser, target: target || '' }); if (!resolved || !resolved.fileExists) { const spec = target ? `"${target}"` : 'an entry point'; return { error: `Failed to resolve ${spec} in package "${moduleName}"` }; } let targetAbsPath = resolved.absPath; const checkBrowserOverride = isBrowser && json.browser && typeof json.browser === 'object'; if (checkBrowserOverride) { const override = browserField_1.handleBrowserField(pkg, resolved.absPath); if (override) { targetAbsPath = override; } } const targetFuseBoxPath = utils_1.makeFuseBoxPath(folder, targetAbsPath); if (isEntry) { pkg.entryAbsPath = targetAbsPath; pkg.entryFuseBoxPath = targetFuseBoxPath; } const targetExtension = path.extname(targetAbsPath); return { isEntry, isUserOwned: isUserOwned, meta: pkg, targetAbsPath, targetExtension, targetFuseBoxPath, }; } exports.nodeModuleLookup = nodeModuleLookup;