Ajout de promotion et de commande
This commit is contained in:
+273
@@ -0,0 +1,273 @@
|
||||
'use strict';
|
||||
|
||||
var objectKeys = require('object-keys');
|
||||
var $Error = require('es-errors');
|
||||
|
||||
// Check if an exports map key looks like a subpath (starts with '.')
|
||||
function isSubpathKey(key) {
|
||||
return key.length > 0 && key.charAt(0) === '.';
|
||||
}
|
||||
|
||||
// Normalize the exports field into a map of subpath -> target
|
||||
function normalizeExports(exportsField) {
|
||||
if (typeof exportsField === 'string') {
|
||||
return { __proto__: null, '.': exportsField };
|
||||
}
|
||||
if (Array.isArray(exportsField)) {
|
||||
return { __proto__: null, '.': exportsField };
|
||||
}
|
||||
if (typeof exportsField === 'object' && exportsField !== null) {
|
||||
var keys = objectKeys(exportsField);
|
||||
if (keys.length === 0) {
|
||||
return { __proto__: null };
|
||||
}
|
||||
// If any key starts with '.', it's a subpath map
|
||||
// If no key starts with '.', it's a conditions object for '.'
|
||||
var hasSubpath = false;
|
||||
for (var i = 0; !hasSubpath && i < keys.length; i++) {
|
||||
if (isSubpathKey(keys[i])) {
|
||||
hasSubpath = true;
|
||||
}
|
||||
}
|
||||
// Copy to new object with null prototype
|
||||
var result = { __proto__: null };
|
||||
for (var j = 0; j < keys.length; j++) {
|
||||
result[keys[j]] = exportsField[keys[j]];
|
||||
}
|
||||
if (hasSubpath) {
|
||||
return result;
|
||||
}
|
||||
return { __proto__: null, '.': result };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Resolve a target value through conditions
|
||||
// conditions: array of condition strings, or null (broken: string/array only)
|
||||
function resolveTarget(target, conditions) {
|
||||
if (typeof target === 'string') {
|
||||
return target;
|
||||
}
|
||||
|
||||
if (target === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (Array.isArray(target)) {
|
||||
for (var i = 0; i < target.length; i++) {
|
||||
var resolved = resolveTarget(target[i], conditions);
|
||||
if (resolved !== null && typeof resolved !== 'undefined') {
|
||||
return resolved;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
if (typeof target === 'object') {
|
||||
// If no conditions supported (broken category), can't resolve objects
|
||||
if (conditions === null) {
|
||||
return null;
|
||||
}
|
||||
var keys = objectKeys(target);
|
||||
for (var j = 0; j < keys.length; j++) {
|
||||
var key = keys[j];
|
||||
for (var k = 0; k < conditions.length; k++) {
|
||||
if (key === conditions[k]) {
|
||||
var result = resolveTarget(target[key], conditions);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Validate a resolved path
|
||||
function validateTarget(target) {
|
||||
if (typeof target !== 'string') {
|
||||
return false;
|
||||
}
|
||||
if (target.slice(0, 2) !== './') {
|
||||
return false;
|
||||
}
|
||||
if (target.indexOf('/node_modules/') !== -1) {
|
||||
return false;
|
||||
}
|
||||
// Check for '..' path traversal
|
||||
var parts = target.split('/');
|
||||
for (var i = 0; i < parts.length; i++) {
|
||||
if (parts[i] === '..') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Find the best pattern match for a subpath among keys with '*'
|
||||
function findPatternMatch(subpath, exportsMap, allowPatternTrailers) {
|
||||
var keys = objectKeys(exportsMap);
|
||||
var bestKey = null;
|
||||
var bestPrefixLen = -1;
|
||||
var bestMatch = '';
|
||||
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
var key = keys[i];
|
||||
var starIndex = key.indexOf('*');
|
||||
// Key must have exactly one '*'
|
||||
if (starIndex !== -1 && key.indexOf('*', starIndex + 1) === -1) {
|
||||
var prefix = key.slice(0, starIndex);
|
||||
var suffix = key.slice(starIndex + 1);
|
||||
|
||||
// Pattern trailers: if suffix is non-empty after *, need allowPatternTrailers
|
||||
if (suffix.length === 0 || allowPatternTrailers) {
|
||||
if (
|
||||
subpath.length >= prefix.length + suffix.length
|
||||
&& subpath.slice(0, prefix.length) === prefix
|
||||
&& (suffix.length === 0 || subpath.slice(subpath.length - suffix.length) === suffix)
|
||||
) {
|
||||
// Longest prefix wins
|
||||
if (prefix.length > bestPrefixLen) {
|
||||
bestPrefixLen = prefix.length;
|
||||
bestKey = key;
|
||||
bestMatch = subpath.slice(prefix.length, subpath.length - suffix.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bestKey !== null) {
|
||||
return {
|
||||
__proto__: null, key: bestKey, match: bestMatch
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Find directory slash match (for categories that support it)
|
||||
function findDirSlashMatch(subpath, exportsMap) {
|
||||
var keys = objectKeys(exportsMap);
|
||||
var bestKey = null;
|
||||
var bestPrefixLen = -1;
|
||||
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
var key = keys[i];
|
||||
if (key.charAt(key.length - 1) === '/') {
|
||||
if (subpath.slice(0, key.length) === key && key.length > bestPrefixLen) {
|
||||
bestPrefixLen = key.length;
|
||||
bestKey = key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bestKey !== null) {
|
||||
return {
|
||||
__proto__: null, key: bestKey, remainder: subpath.slice(bestKey.length)
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Replace '*' in target string with match value
|
||||
function substitutePattern(target, match) {
|
||||
if (typeof target === 'string') {
|
||||
return target.split('*').join(match);
|
||||
}
|
||||
if (Array.isArray(target)) {
|
||||
var result = [];
|
||||
for (var i = 0; i < target.length; i++) {
|
||||
result.push(substitutePattern(target[i], match));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
if (typeof target === 'object' && target !== null) {
|
||||
var obj = { __proto__: null };
|
||||
var keys = objectKeys(target);
|
||||
for (var j = 0; j < keys.length; j++) {
|
||||
obj[keys[j]] = substitutePattern(target[keys[j]], match);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
// Main exports resolution function
|
||||
// exportsField: the value of package.json "exports"
|
||||
// subpath: the subpath to resolve (e.g., "." or "./foo/bar")
|
||||
// conditions: array of condition strings, or null for broken category
|
||||
// options: { patterns: boolean, patternTrailers: boolean, dirSlash: boolean }
|
||||
// Returns: resolved relative path string, or null if no exports field
|
||||
// Throws: when exports field exists but subpath is not exported
|
||||
module.exports = function resolveExports(exportsField, subpath, conditions, options) {
|
||||
if (typeof exportsField === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
|
||||
var exportsMap = normalizeExports(exportsField);
|
||||
if (!exportsMap) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var allowPatterns = options && options.patterns;
|
||||
var allowPatternTrailers = options && options.patternTrailers;
|
||||
var allowDirSlash = options && options.dirSlash;
|
||||
|
||||
// 1. Exact key match
|
||||
if (typeof exportsMap[subpath] !== 'undefined') {
|
||||
var resolved = resolveTarget(exportsMap[subpath], conditions);
|
||||
if (resolved !== null && typeof resolved !== 'undefined') {
|
||||
if (!validateTarget(resolved)) {
|
||||
var invalidError = new $Error('Invalid "exports" target "' + resolved + '" for subpath "' + subpath + '"');
|
||||
invalidError.code = 'ERR_INVALID_PACKAGE_CONFIG';
|
||||
throw invalidError;
|
||||
}
|
||||
return resolved;
|
||||
}
|
||||
// Target exists but resolved to null (explicitly not exported)
|
||||
var notExportedError = new $Error('Package subpath "' + subpath + '" is not defined by "exports"');
|
||||
notExportedError.code = 'ERR_PACKAGE_PATH_NOT_EXPORTED';
|
||||
throw notExportedError;
|
||||
}
|
||||
|
||||
// 2. Pattern match (keys with '*')
|
||||
if (allowPatterns) {
|
||||
var patternResult = findPatternMatch(subpath, exportsMap, allowPatternTrailers);
|
||||
if (patternResult) {
|
||||
var substituted = substitutePattern(exportsMap[patternResult.key], patternResult.match);
|
||||
var patternResolved = resolveTarget(substituted, conditions);
|
||||
if (patternResolved !== null && typeof patternResolved !== 'undefined') {
|
||||
if (!validateTarget(patternResolved)) {
|
||||
var patternInvalidError = new $Error('Invalid "exports" target "' + patternResolved + '" for subpath "' + subpath + '"');
|
||||
patternInvalidError.code = 'ERR_INVALID_PACKAGE_CONFIG';
|
||||
throw patternInvalidError;
|
||||
}
|
||||
return patternResolved;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Directory slash match (for older categories)
|
||||
if (allowDirSlash) {
|
||||
var dirResult = findDirSlashMatch(subpath, exportsMap);
|
||||
if (dirResult) {
|
||||
var dirTarget = resolveTarget(exportsMap[dirResult.key], conditions);
|
||||
if (dirTarget !== null && typeof dirTarget !== 'undefined' && typeof dirTarget === 'string') {
|
||||
var dirResolved = dirTarget + dirResult.remainder;
|
||||
if (!validateTarget(dirResolved)) {
|
||||
var dirInvalidError = new $Error('Invalid "exports" target "' + dirResolved + '" for subpath "' + subpath + '"');
|
||||
dirInvalidError.code = 'ERR_INVALID_PACKAGE_CONFIG';
|
||||
throw dirInvalidError;
|
||||
}
|
||||
return dirResolved;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var err = new $Error('Package subpath "' + subpath + '" is not defined by "exports"');
|
||||
err.code = 'ERR_PACKAGE_PATH_NOT_EXPORTED';
|
||||
throw err;
|
||||
};
|
||||
Reference in New Issue
Block a user