feat: Phase 1 — extract the client (web SPA + desktop) to dtoro/oikos-web
Problem: the hexagonal refactor churns the backend tree for nine more phases; the UI delivery stack (web/ SPA, cmd/desktop Wails wrapper, compose/web image) must move to its own repo first so doc/layout rewrites land once on a backend-only tree. Change: - New repo git.hubris.network/dtoro/oikos-web (v0.33.0): web/, desktop/ (updateURL repointed to oikos-web releases), compose/, own CI (web + desktop jobs), own deploy script (CI-green gate, TOCTOU guard, version-tagged images, prune-to-3), own webhook receiver on :9798 + launchd unit, own compose project publishing the same 8091:80. - Cutover executed on mac-mini in order: oikos stack's web service stopped+removed, oikos-web project brought up on 8091; outer Caddy untouched (targets the published port) — serving + Authentik flow + /wails 404 quirk verified post-cutover. - Stripped from oikos: web/, cmd/desktop/, compose/web/, desktop CI workflow, ci.yml web job, Makefile ui/desktop/desktop-package/install targets, the compose web service, oikos-web from deploy.sh's fallback prune list; wails + go-keyring dropped from go.mod, vendor synced. - README / CONTRIBUTING / AGENTS.md / .agents dev+operations docs now point at the new repo; mbse + mascot design docs carry a path note. Risk: production SPA serving depends on the new pipeline now; rollback is versioned-image re-up of the old web service from a pre-split checkout (port 8091). Desktop builds installed before the split still check dtoro/oikos releases — one manual reinstall, noted in the oikos-web release notes. Verification: go vet, make test (race), make generate-check, golangci (no new findings; baseline down 400→365); post-cutover curls — localhost:8091 200, /wails/runtime.js 404, outer Caddy 302 Authentik.
This commit is contained in:
745
web/node_modules/enhanced-resolve/lib/util/entrypoints.js
generated
vendored
Normal file
745
web/node_modules/enhanced-resolve/lib/util/entrypoints.js
generated
vendored
Normal file
@@ -0,0 +1,745 @@
|
||||
/*
|
||||
MIT License http://www.opensource.org/licenses/mit-license.php
|
||||
Author Ivan Kopeykin @vankop
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const { parseIdentifier } = require("./identifier");
|
||||
|
||||
/** @typedef {string | (string | ConditionalMapping)[]} DirectMapping */
|
||||
/** @typedef {{ [k: string]: MappingValue }} ConditionalMapping */
|
||||
/** @typedef {ConditionalMapping | DirectMapping | null} MappingValue */
|
||||
/** @typedef {Record<string, MappingValue> | ConditionalMapping | DirectMapping} ExportsField */
|
||||
/** @typedef {Record<string, MappingValue>} ImportsField */
|
||||
|
||||
/**
|
||||
* Processing exports/imports field
|
||||
* @callback FieldProcessor
|
||||
* @param {string} request request
|
||||
* @param {Set<string>} conditionNames condition names
|
||||
* @returns {[string[], string | null]} resolved paths with used field
|
||||
*/
|
||||
|
||||
/*
|
||||
Example exports field:
|
||||
{
|
||||
".": "./main.js",
|
||||
"./feature": {
|
||||
"browser": "./feature-browser.js",
|
||||
"default": "./feature.js"
|
||||
}
|
||||
}
|
||||
Terminology:
|
||||
|
||||
Enhanced-resolve name keys ("." and "./feature") as exports field keys.
|
||||
|
||||
If value is string or string[], mapping is called as a direct mapping
|
||||
and value called as a direct export.
|
||||
|
||||
If value is key-value object, mapping is called as a conditional mapping
|
||||
and value called as a conditional export.
|
||||
|
||||
Key in conditional mapping is called condition name.
|
||||
|
||||
Conditional mapping nested in another conditional mapping is called nested mapping.
|
||||
|
||||
----------
|
||||
|
||||
Example imports field:
|
||||
{
|
||||
"#a": "./main.js",
|
||||
"#moment": {
|
||||
"browser": "./moment/index.js",
|
||||
"default": "moment"
|
||||
},
|
||||
"#moment/": {
|
||||
"browser": "./moment/",
|
||||
"default": "moment/"
|
||||
}
|
||||
}
|
||||
Terminology:
|
||||
|
||||
Enhanced-resolve name keys ("#a" and "#moment/", "#moment") as imports field keys.
|
||||
|
||||
If value is string or string[], mapping is called as a direct mapping
|
||||
and value called as a direct export.
|
||||
|
||||
If value is key-value object, mapping is called as a conditional mapping
|
||||
and value called as a conditional export.
|
||||
|
||||
Key in conditional mapping is called condition name.
|
||||
|
||||
Conditional mapping nested in another conditional mapping is called nested mapping.
|
||||
|
||||
*/
|
||||
|
||||
const slashCode = "/".charCodeAt(0);
|
||||
const dotCode = ".".charCodeAt(0);
|
||||
const hashCode = "#".charCodeAt(0);
|
||||
const patternRegEx = /\*/g;
|
||||
const DOLLAR_ESCAPE_RE = /\$/g;
|
||||
|
||||
/** @typedef {Record<string, MappingValue>} RecordMapping */
|
||||
|
||||
/**
|
||||
* Cached `Object.keys()` for objects whose shape does not change after the
|
||||
* first observation — i.e. parsed `package.json` fields and the nested
|
||||
* conditional mappings inside them. `Object.keys` allocates a fresh array
|
||||
* on every call; since `findMatch` / `conditionalMapping` run on every
|
||||
* bare-specifier resolve, the allocation adds up quickly.
|
||||
* @type {WeakMap<RecordMapping, string[]>}
|
||||
*/
|
||||
const _keysCache = new WeakMap();
|
||||
|
||||
/**
|
||||
* @param {RecordMapping} obj object to read keys from
|
||||
* @returns {string[]} cached keys array (DO NOT mutate)
|
||||
*/
|
||||
function cachedKeys(obj) {
|
||||
let keys = _keysCache.get(obj);
|
||||
if (keys === undefined) {
|
||||
keys = Object.keys(obj);
|
||||
_keysCache.set(obj, keys);
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Per-key precomputed info used by `findMatch`. Equivalent to what the
|
||||
* previous implementation recomputed inline on every resolve.
|
||||
* @typedef {object} FieldKeyInfo
|
||||
* @property {string} key the original key
|
||||
* @property {number} patternIndex position of the single "*" in the key, or -1 when absent
|
||||
* @property {string} wildcardPrefix substring before "*" (empty when patternIndex === -1)
|
||||
* @property {string} wildcardSuffix substring after "*" (empty when patternIndex === -1)
|
||||
* @property {boolean} isLegacySubpath true when key is a legacy `./foo/`-style folder key with no "*"
|
||||
* @property {boolean} isPattern true when key contains "*"
|
||||
* @property {boolean} isSubpathMapping true when key ends with "/"
|
||||
* @property {boolean} isValidPattern true when key has at most one "*"
|
||||
*/
|
||||
|
||||
/**
|
||||
* Cached per-field key metadata, keyed by the exports/imports field
|
||||
* object. Computed lazily on first `findMatch` call and reused forever.
|
||||
* Safe because `package.json` fields are immutable JSON values.
|
||||
* @type {WeakMap<RecordMapping, FieldKeyInfo[]>}
|
||||
*/
|
||||
const _fieldKeyInfoCache = new WeakMap();
|
||||
|
||||
/**
|
||||
* @param {ExportsField | ImportsField} field field object
|
||||
* @returns {FieldKeyInfo[]} precomputed per-key info
|
||||
*/
|
||||
function getFieldKeyInfos(field) {
|
||||
const fieldKey = /** @type {RecordMapping} */ (field);
|
||||
let infos = _fieldKeyInfoCache.get(fieldKey);
|
||||
if (infos !== undefined) return infos;
|
||||
const keys = Object.getOwnPropertyNames(field);
|
||||
infos = Array.from({ length: keys.length });
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const key = keys[i];
|
||||
const patternIndex = key.indexOf("*");
|
||||
// `isValidPattern` is true when the key has at most one `*`. Searching
|
||||
// from `patternIndex + 1` stops as soon as a second `*` is found, so
|
||||
// we avoid the full-string scan that `lastIndexOf` would do — and the
|
||||
// single-star common case finishes in one pass.
|
||||
const isValidPattern =
|
||||
patternIndex === -1 || !key.includes("*", patternIndex + 1);
|
||||
const keyLen = key.length;
|
||||
const endsWithSlash =
|
||||
keyLen > 0 && key.charCodeAt(keyLen - 1) === slashCode;
|
||||
infos[i] = {
|
||||
key,
|
||||
patternIndex,
|
||||
wildcardPrefix: patternIndex === -1 ? "" : key.slice(0, patternIndex),
|
||||
wildcardSuffix: patternIndex === -1 ? "" : key.slice(patternIndex + 1),
|
||||
isLegacySubpath: patternIndex === -1 && endsWithSlash,
|
||||
isPattern: patternIndex !== -1,
|
||||
isSubpathMapping: endsWithSlash,
|
||||
isValidPattern,
|
||||
};
|
||||
}
|
||||
_fieldKeyInfoCache.set(fieldKey, infos);
|
||||
return infos;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} a first string
|
||||
* @param {string} b second string
|
||||
* @returns {number} compare result
|
||||
*/
|
||||
function patternKeyCompare(a, b) {
|
||||
const aPatternIndex = a.indexOf("*");
|
||||
const bPatternIndex = b.indexOf("*");
|
||||
const baseLenA = aPatternIndex === -1 ? a.length : aPatternIndex + 1;
|
||||
const baseLenB = bPatternIndex === -1 ? b.length : bPatternIndex + 1;
|
||||
|
||||
if (baseLenA > baseLenB) return -1;
|
||||
if (baseLenB > baseLenA) return 1;
|
||||
if (aPatternIndex === -1) return 1;
|
||||
if (bPatternIndex === -1) return -1;
|
||||
if (a.length > b.length) return -1;
|
||||
if (b.length > a.length) return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** @typedef {[MappingValue, string, boolean, boolean, string] | null} MatchTuple */
|
||||
|
||||
/**
|
||||
* Per-field memoization of `findMatch(request, field)`. For a given field
|
||||
* the result depends only on the `request` string (it does NOT depend on
|
||||
* `conditionNames` — that's applied separately by `conditionalMapping`),
|
||||
* so we can cache the tuple keyed by request.
|
||||
*
|
||||
* Typical build traffic runs the same request through the resolver
|
||||
* repeatedly (same import re-resolved from different source files, module
|
||||
* graph traversals that revisit a package, etc.), and every one of those
|
||||
* hits walks the same key list and allocates the same tuple. Caching the
|
||||
* tuple turns the second-and-onward call into a single Map lookup.
|
||||
*
|
||||
* Keyed on the field object via a module-level `WeakMap`, so the cache
|
||||
* is freed automatically when the owning description file is GC'd.
|
||||
* @type {WeakMap<RecordMapping, Map<string, MatchTuple>>}
|
||||
*/
|
||||
const _findMatchCache = new WeakMap();
|
||||
|
||||
/**
|
||||
* @param {string} request request
|
||||
* @param {ExportsField | ImportsField} field exports or import field
|
||||
* @returns {MatchTuple} match result (uncached)
|
||||
*/
|
||||
function computeFindMatch(request, field) {
|
||||
const requestLen = request.length;
|
||||
const requestEndsWithSlash =
|
||||
requestLen > 0 && request.charCodeAt(requestLen - 1) === slashCode;
|
||||
const requestHasStar = request.includes("*");
|
||||
|
||||
if (
|
||||
!requestHasStar &&
|
||||
!requestEndsWithSlash &&
|
||||
Object.prototype.hasOwnProperty.call(field, request)
|
||||
) {
|
||||
const target = /** @type {{ [k: string]: MappingValue }} */ (field)[
|
||||
request
|
||||
];
|
||||
|
||||
return [target, "", false, false, request];
|
||||
}
|
||||
|
||||
/** @type {string} */
|
||||
let bestMatch = "";
|
||||
/** @type {FieldKeyInfo | null} */
|
||||
let bestMatchInfo = null;
|
||||
/** @type {string | undefined} */
|
||||
let bestMatchSubpath;
|
||||
|
||||
const infos = getFieldKeyInfos(field);
|
||||
|
||||
for (let i = 0; i < infos.length; i++) {
|
||||
const info = infos[i];
|
||||
const { key, patternIndex } = info;
|
||||
|
||||
if (patternIndex !== -1) {
|
||||
if (
|
||||
!info.isValidPattern ||
|
||||
!request.startsWith(info.wildcardPrefix) ||
|
||||
requestLen < key.length ||
|
||||
!request.endsWith(info.wildcardSuffix) ||
|
||||
patternKeyCompare(bestMatch, key) !== 1
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
bestMatch = key;
|
||||
bestMatchInfo = info;
|
||||
bestMatchSubpath = request.slice(
|
||||
patternIndex,
|
||||
requestLen - info.wildcardSuffix.length,
|
||||
);
|
||||
} else if (
|
||||
info.isLegacySubpath &&
|
||||
request.startsWith(key) &&
|
||||
patternKeyCompare(bestMatch, key) === 1
|
||||
) {
|
||||
bestMatch = key;
|
||||
bestMatchInfo = info;
|
||||
bestMatchSubpath = request.slice(key.length);
|
||||
}
|
||||
}
|
||||
|
||||
if (bestMatch === "") return null;
|
||||
|
||||
const target =
|
||||
/** @type {{ [k: string]: MappingValue }} */
|
||||
(field)[bestMatch];
|
||||
|
||||
return [
|
||||
target,
|
||||
/** @type {string} */ (bestMatchSubpath),
|
||||
/** @type {FieldKeyInfo} */ (bestMatchInfo).isSubpathMapping,
|
||||
/** @type {FieldKeyInfo} */ (bestMatchInfo).isPattern,
|
||||
bestMatch,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Trying to match request to field
|
||||
* @param {string} request request
|
||||
* @param {ExportsField | ImportsField} field exports or import field
|
||||
* @returns {MatchTuple} match or null, number is negative and one less when it's a folder mapping, number is request.length + 1 for direct mappings
|
||||
*/
|
||||
function findMatch(request, field) {
|
||||
const fieldKey = /** @type {RecordMapping} */ (field);
|
||||
let perRequest = _findMatchCache.get(fieldKey);
|
||||
if (perRequest === undefined) {
|
||||
perRequest = new Map();
|
||||
_findMatchCache.set(fieldKey, perRequest);
|
||||
} else {
|
||||
// `computeFindMatch` only ever returns `MatchTuple | null` — never
|
||||
// `undefined` — and `Map.set(k, null)` then `Map.get(k)` returns
|
||||
// `null`, not `undefined`. So `get(...) === undefined` already
|
||||
// unambiguously means "not cached yet"; one Map lookup is enough,
|
||||
// no follow-up `has` needed to disambiguate "cached null".
|
||||
const cached = perRequest.get(request);
|
||||
if (cached !== undefined) return cached;
|
||||
}
|
||||
|
||||
const result = computeFindMatch(request, field);
|
||||
perRequest.set(request, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sentinel stored in the conditional-mapping cache for inputs whose walk
|
||||
* returns `null` ("no condition matched"). Using a non-null marker lets the
|
||||
* cache-hit path be a single `WeakMap.get()` — we distinguish
|
||||
* "cached null" from "not cached yet" without a second `has` call.
|
||||
*/
|
||||
const NULL_RESULT = Symbol("NULL_RESULT");
|
||||
|
||||
/**
|
||||
* Memoization of `conditionalMapping(mapping, conditionNames)`. The result
|
||||
* depends only on the mapping object (immutable — owned by a parsed
|
||||
* `package.json`) and the `conditionNames` Set (owned by the resolver's
|
||||
* options and stable for its lifetime), so it is safe to cache per (mapping,
|
||||
* conditionNames) pair.
|
||||
*
|
||||
* A conditional `exports` entry that appears inside a `directMapping` array
|
||||
* (the common `"browser": [...fallback list...]` shape, plus nested
|
||||
* conditions) gets walked on every resolve that traverses the parent entry.
|
||||
* Without this cache each of those walks re-reads `Object.keys` on the
|
||||
* mapping and re-visits every condition until one matches, even though the
|
||||
* inputs are identical.
|
||||
*
|
||||
* Outer key is the conditional mapping itself; inner key is the condition
|
||||
* Set. Both are object references, so WeakMap-of-WeakMap lets both levels
|
||||
* be collected automatically when the description file or resolver go away.
|
||||
* @type {WeakMap<ConditionalMapping, WeakMap<Set<string>, DirectMapping | typeof NULL_RESULT>>}
|
||||
*/
|
||||
const _conditionalMappingCache = new WeakMap();
|
||||
|
||||
/**
|
||||
* @param {ConditionalMapping} conditionalMapping_ conditional mapping
|
||||
* @param {Set<string>} conditionNames condition names
|
||||
* @returns {DirectMapping | null} direct mapping if found (uncached)
|
||||
*/
|
||||
function computeConditionalMapping(conditionalMapping_, conditionNames) {
|
||||
/** @type {[ConditionalMapping, string[], number][]} */
|
||||
const lookup = [[conditionalMapping_, cachedKeys(conditionalMapping_), 0]];
|
||||
|
||||
loop: while (lookup.length > 0) {
|
||||
const top = lookup[lookup.length - 1];
|
||||
const [mapping, conditions, j] = top;
|
||||
|
||||
for (let i = j; i < conditions.length; i++) {
|
||||
const condition = conditions[i];
|
||||
|
||||
if (condition === "default" || conditionNames.has(condition)) {
|
||||
const innerMapping = mapping[condition];
|
||||
if (
|
||||
innerMapping !== null &&
|
||||
typeof innerMapping === "object" &&
|
||||
!Array.isArray(innerMapping)
|
||||
) {
|
||||
const nested = /** @type {ConditionalMapping} */ (innerMapping);
|
||||
top[2] = i + 1;
|
||||
lookup.push([nested, cachedKeys(nested), 0]);
|
||||
continue loop;
|
||||
}
|
||||
|
||||
return /** @type {DirectMapping} */ (innerMapping);
|
||||
}
|
||||
}
|
||||
|
||||
lookup.pop();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ConditionalMapping} conditionalMapping_ conditional mapping
|
||||
* @param {Set<string>} conditionNames condition names
|
||||
* @returns {DirectMapping | null} direct mapping if found
|
||||
*/
|
||||
function conditionalMapping(conditionalMapping_, conditionNames) {
|
||||
let perSet = _conditionalMappingCache.get(conditionalMapping_);
|
||||
if (perSet !== undefined) {
|
||||
const cached = perSet.get(conditionNames);
|
||||
if (cached !== undefined) {
|
||||
return cached === NULL_RESULT
|
||||
? null
|
||||
: /** @type {DirectMapping} */ (cached);
|
||||
}
|
||||
} else {
|
||||
perSet = new WeakMap();
|
||||
_conditionalMappingCache.set(conditionalMapping_, perSet);
|
||||
}
|
||||
const result = computeConditionalMapping(conditionalMapping_, conditionNames);
|
||||
perSet.set(conditionNames, result === null ? NULL_RESULT : result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string | undefined} remainingRequest remaining request when folder mapping, undefined for file mappings
|
||||
* @param {boolean} isPattern true, if mapping is a pattern (contains "*")
|
||||
* @param {boolean} isSubpathMapping true, for subpath mappings
|
||||
* @param {string} mappingTarget direct export
|
||||
* @param {(d: string, f: boolean) => void} assert asserting direct value
|
||||
* @returns {string} mapping result
|
||||
*/
|
||||
function targetMapping(
|
||||
remainingRequest,
|
||||
isPattern,
|
||||
isSubpathMapping,
|
||||
mappingTarget,
|
||||
assert,
|
||||
) {
|
||||
if (remainingRequest === undefined) {
|
||||
assert(mappingTarget, false);
|
||||
|
||||
return mappingTarget;
|
||||
}
|
||||
|
||||
if (isSubpathMapping) {
|
||||
assert(mappingTarget, true);
|
||||
|
||||
return mappingTarget + remainingRequest;
|
||||
}
|
||||
|
||||
assert(mappingTarget, false);
|
||||
|
||||
let result = mappingTarget;
|
||||
|
||||
if (isPattern) {
|
||||
const escapedRemainder = remainingRequest.includes("$")
|
||||
? remainingRequest.replace(DOLLAR_ESCAPE_RE, "$$")
|
||||
: remainingRequest;
|
||||
result = result.replace(patternRegEx, escapedRemainder);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string | undefined} remainingRequest remaining request when folder mapping, undefined for file mappings
|
||||
* @param {boolean} isPattern true, if mapping is a pattern (contains "*")
|
||||
* @param {boolean} isSubpathMapping true, for subpath mappings
|
||||
* @param {DirectMapping | null} mappingTarget direct export
|
||||
* @param {Set<string>} conditionNames condition names
|
||||
* @param {(d: string, f: boolean) => void} assert asserting direct value
|
||||
* @returns {string[]} mapping result
|
||||
*/
|
||||
function directMapping(
|
||||
remainingRequest,
|
||||
isPattern,
|
||||
isSubpathMapping,
|
||||
mappingTarget,
|
||||
conditionNames,
|
||||
assert,
|
||||
) {
|
||||
if (mappingTarget === null) return [];
|
||||
|
||||
if (typeof mappingTarget === "string") {
|
||||
return [
|
||||
targetMapping(
|
||||
remainingRequest,
|
||||
isPattern,
|
||||
isSubpathMapping,
|
||||
mappingTarget,
|
||||
assert,
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
/** @type {string[]} */
|
||||
const targets = [];
|
||||
|
||||
for (let i = 0, len = mappingTarget.length; i < len; i++) {
|
||||
const exp = mappingTarget[i];
|
||||
if (typeof exp === "string") {
|
||||
targets.push(
|
||||
targetMapping(
|
||||
remainingRequest,
|
||||
isPattern,
|
||||
isSubpathMapping,
|
||||
exp,
|
||||
assert,
|
||||
),
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
const mapping = conditionalMapping(exp, conditionNames);
|
||||
if (!mapping) continue;
|
||||
const innerExports = directMapping(
|
||||
remainingRequest,
|
||||
isPattern,
|
||||
isSubpathMapping,
|
||||
mapping,
|
||||
conditionNames,
|
||||
assert,
|
||||
);
|
||||
for (let j = 0, innerLen = innerExports.length; j < innerLen; j++) {
|
||||
targets.push(innerExports[j]);
|
||||
}
|
||||
}
|
||||
|
||||
return targets;
|
||||
}
|
||||
|
||||
/** @type {[string[], null]} */
|
||||
const EMPTY_NO_MATCH = /** @type {[string[], null]} */ ([[], null]);
|
||||
|
||||
/**
|
||||
* @param {ExportsField | ImportsField} field root
|
||||
* @param {(s: string) => string} normalizeRequest Normalize request, for `imports` field it adds `#`, for `exports` field it adds `.` or `./`
|
||||
* @param {(s: string) => string} assertRequest assertRequest
|
||||
* @param {(s: string, f: boolean) => void} assertTarget assertTarget
|
||||
* @returns {FieldProcessor} field processor
|
||||
*/
|
||||
function createFieldProcessor(
|
||||
field,
|
||||
normalizeRequest,
|
||||
assertRequest,
|
||||
assertTarget,
|
||||
) {
|
||||
return function fieldProcessor(request, conditionNames) {
|
||||
const match = findMatch(normalizeRequest(assertRequest(request)), field);
|
||||
|
||||
if (match === null) return EMPTY_NO_MATCH;
|
||||
|
||||
const [mapping, remainingRequest, isSubpathMapping, isPattern, usedField] =
|
||||
match;
|
||||
|
||||
/** @type {DirectMapping | null} */
|
||||
let direct;
|
||||
if (
|
||||
mapping !== null &&
|
||||
typeof mapping === "object" &&
|
||||
!Array.isArray(mapping)
|
||||
) {
|
||||
direct = conditionalMapping(
|
||||
/** @type {ConditionalMapping} */ (mapping),
|
||||
conditionNames,
|
||||
);
|
||||
if (direct === null) return EMPTY_NO_MATCH;
|
||||
} else {
|
||||
direct = /** @type {DirectMapping} */ (mapping);
|
||||
}
|
||||
|
||||
return [
|
||||
directMapping(
|
||||
remainingRequest,
|
||||
isPattern,
|
||||
isSubpathMapping,
|
||||
direct,
|
||||
conditionNames,
|
||||
assertTarget,
|
||||
),
|
||||
usedField,
|
||||
];
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} request request
|
||||
* @returns {string} updated request
|
||||
*/
|
||||
function assertExportsFieldRequest(request) {
|
||||
if (request.charCodeAt(0) !== dotCode) {
|
||||
throw new Error('Request should be relative path and start with "."');
|
||||
}
|
||||
if (request.length === 1) return "";
|
||||
if (request.charCodeAt(1) !== slashCode) {
|
||||
throw new Error('Request should be relative path and start with "./"');
|
||||
}
|
||||
if (request.charCodeAt(request.length - 1) === slashCode) {
|
||||
throw new Error("Only requesting file allowed");
|
||||
}
|
||||
|
||||
return request.slice(2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ExportsField} field exports field
|
||||
* @returns {ExportsField} normalized exports field
|
||||
*/
|
||||
function buildExportsField(field) {
|
||||
// handle syntax sugar, if exports field is direct mapping for "."
|
||||
if (typeof field === "string" || Array.isArray(field)) {
|
||||
return { ".": field };
|
||||
}
|
||||
|
||||
const keys = Object.keys(field);
|
||||
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const key = keys[i];
|
||||
|
||||
if (key.charCodeAt(0) !== dotCode) {
|
||||
// handle syntax sugar, if exports field is conditional mapping for "."
|
||||
if (i === 0) {
|
||||
while (i < keys.length) {
|
||||
const charCode = keys[i].charCodeAt(0);
|
||||
if (charCode === dotCode || charCode === slashCode) {
|
||||
throw new Error(
|
||||
`Exports field key should be relative path and start with "." (key: ${JSON.stringify(
|
||||
key,
|
||||
)})`,
|
||||
);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
return { ".": field };
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`Exports field key should be relative path and start with "." (key: ${JSON.stringify(
|
||||
key,
|
||||
)})`,
|
||||
);
|
||||
}
|
||||
|
||||
if (key.length === 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (key.charCodeAt(1) !== slashCode) {
|
||||
throw new Error(
|
||||
`Exports field key should be relative path and start with "./" (key: ${JSON.stringify(
|
||||
key,
|
||||
)})`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return field;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} exp export target
|
||||
* @param {boolean} expectFolder is folder expected
|
||||
*/
|
||||
function assertExportTarget(exp, expectFolder) {
|
||||
const parsedIdentifier = parseIdentifier(exp);
|
||||
|
||||
if (!parsedIdentifier) {
|
||||
return;
|
||||
}
|
||||
|
||||
const [relativePath] = parsedIdentifier;
|
||||
const isFolder =
|
||||
relativePath.charCodeAt(relativePath.length - 1) === slashCode;
|
||||
|
||||
if (isFolder !== expectFolder) {
|
||||
throw new Error(
|
||||
expectFolder
|
||||
? `Expecting folder to folder mapping. ${JSON.stringify(
|
||||
exp,
|
||||
)} should end with "/"`
|
||||
: `Expecting file to file mapping. ${JSON.stringify(
|
||||
exp,
|
||||
)} should not end with "/"`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ExportsField} exportsField the exports field
|
||||
* @returns {FieldProcessor} process callback
|
||||
*/
|
||||
module.exports.processExportsField = function processExportsField(
|
||||
exportsField,
|
||||
) {
|
||||
return createFieldProcessor(
|
||||
buildExportsField(exportsField),
|
||||
(request) => (request.length === 0 ? "." : `./${request}`),
|
||||
assertExportsFieldRequest,
|
||||
assertExportTarget,
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} request request
|
||||
* @returns {string} updated request
|
||||
*/
|
||||
function assertImportsFieldRequest(request) {
|
||||
if (request.charCodeAt(0) !== hashCode) {
|
||||
throw new Error('Request should start with "#"');
|
||||
}
|
||||
if (request.length === 1) {
|
||||
throw new Error("Request should have at least 2 characters");
|
||||
}
|
||||
// Note: #/ patterns are now allowed per Node.js PR #60864
|
||||
// https://github.com/nodejs/node/pull/60864
|
||||
if (request.charCodeAt(request.length - 1) === slashCode) {
|
||||
throw new Error("Only requesting file allowed");
|
||||
}
|
||||
|
||||
return request.slice(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} imp import target
|
||||
* @param {boolean} expectFolder is folder expected
|
||||
*/
|
||||
function assertImportTarget(imp, expectFolder) {
|
||||
const parsedIdentifier = parseIdentifier(imp);
|
||||
|
||||
if (!parsedIdentifier) {
|
||||
return;
|
||||
}
|
||||
|
||||
const [relativePath] = parsedIdentifier;
|
||||
const isFolder =
|
||||
relativePath.charCodeAt(relativePath.length - 1) === slashCode;
|
||||
|
||||
if (isFolder !== expectFolder) {
|
||||
throw new Error(
|
||||
expectFolder
|
||||
? `Expecting folder to folder mapping. ${JSON.stringify(
|
||||
imp,
|
||||
)} should end with "/"`
|
||||
: `Expecting file to file mapping. ${JSON.stringify(
|
||||
imp,
|
||||
)} should not end with "/"`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ImportsField} importsField the exports field
|
||||
* @returns {FieldProcessor} process callback
|
||||
*/
|
||||
module.exports.processImportsField = function processImportsField(
|
||||
importsField,
|
||||
) {
|
||||
return createFieldProcessor(
|
||||
importsField,
|
||||
(request) => `#${request}`,
|
||||
assertImportsFieldRequest,
|
||||
assertImportTarget,
|
||||
);
|
||||
};
|
||||
93
web/node_modules/enhanced-resolve/lib/util/fileURLToPath.js
generated
vendored
Normal file
93
web/node_modules/enhanced-resolve/lib/util/fileURLToPath.js
generated
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
MIT License http://www.opensource.org/licenses/mit-license.php
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
// A runtime-agnostic port of Node's `url.fileURLToPath`, built on the global
|
||||
// `URL` (available in Node, browsers, Deno and Bun) so the resolver does not
|
||||
// depend on the Node `url` builtin. The platform branch can be forced via the
|
||||
// `windows` option; otherwise it follows the host platform like Node does.
|
||||
|
||||
const CHAR_LOWERCASE_A = 97;
|
||||
const CHAR_LOWERCASE_Z = 122;
|
||||
|
||||
const isWindows =
|
||||
typeof process !== "undefined" && process.platform === "win32";
|
||||
|
||||
const forwardSlashRegEx = /\//g;
|
||||
|
||||
/**
|
||||
* @param {URL} url file: URL instance
|
||||
* @returns {string} Windows filesystem path
|
||||
*/
|
||||
function getPathFromURLWin32(url) {
|
||||
const { hostname } = url;
|
||||
let { pathname } = url;
|
||||
for (let n = 0; n < pathname.length; n++) {
|
||||
if (pathname[n] === "%") {
|
||||
const third = (pathname.codePointAt(n + 2) || 0) | 0x20;
|
||||
if (
|
||||
(pathname[n + 1] === "2" && third === 102) || // 2f 2F (/)
|
||||
(pathname[n + 1] === "5" && third === 99) // 5c 5C (\)
|
||||
) {
|
||||
throw new TypeError(
|
||||
"File URL path must not include encoded \\ or / characters",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
pathname = pathname.replace(forwardSlashRegEx, "\\");
|
||||
pathname = decodeURIComponent(pathname);
|
||||
if (hostname !== "") {
|
||||
// UNC path: `\\host\share\…`. Node runs the host through
|
||||
// `domainToUnicode`; we keep the (already punycode) hostname as-is,
|
||||
// which is identical for ASCII hosts and only differs for rare IDN UNC.
|
||||
return `\\\\${hostname}${pathname}`;
|
||||
}
|
||||
const letter = (pathname.codePointAt(1) || 0) | 0x20;
|
||||
const sep = pathname.charAt(2);
|
||||
if (letter < CHAR_LOWERCASE_A || letter > CHAR_LOWERCASE_Z || sep !== ":") {
|
||||
throw new TypeError("File URL path must be absolute");
|
||||
}
|
||||
return pathname.slice(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {URL} url file: URL instance
|
||||
* @returns {string} POSIX filesystem path
|
||||
*/
|
||||
function getPathFromURLPosix(url) {
|
||||
if (url.hostname !== "") {
|
||||
throw new TypeError('File URL host must be "localhost" or empty');
|
||||
}
|
||||
const { pathname } = url;
|
||||
for (let n = 0; n < pathname.length; n++) {
|
||||
if (pathname[n] === "%") {
|
||||
const third = (pathname.codePointAt(n + 2) || 0) | 0x20;
|
||||
if (pathname[n + 1] === "2" && third === 102) {
|
||||
throw new TypeError(
|
||||
"File URL path must not include encoded / characters",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return decodeURIComponent(pathname);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string | URL} path a `file:` URL string or `URL` instance
|
||||
* @param {{ windows?: boolean }=} options force the platform branch
|
||||
* @returns {string} the filesystem path
|
||||
*/
|
||||
function fileURLToPath(path, options) {
|
||||
const url = typeof path === "string" ? new URL(path) : path;
|
||||
if (url.protocol !== "file:") {
|
||||
throw new TypeError("The URL must be of scheme file");
|
||||
}
|
||||
const windows =
|
||||
options && options.windows !== undefined ? options.windows : isWindows;
|
||||
return windows ? getPathFromURLWin32(url) : getPathFromURLPosix(url);
|
||||
}
|
||||
|
||||
module.exports = fileURLToPath;
|
||||
106
web/node_modules/enhanced-resolve/lib/util/fs.js
generated
vendored
Normal file
106
web/node_modules/enhanced-resolve/lib/util/fs.js
generated
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
MIT License http://www.opensource.org/licenses/mit-license.php
|
||||
Author Natsu @xiaoxiaojx
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const memoize = require("./memoize");
|
||||
const stripJsonComments = require("./strip-json-comments");
|
||||
|
||||
/** @typedef {import("../Resolver").FileSystem} FileSystem */
|
||||
/** @typedef {import("../Resolver").JsonObject} JsonObject */
|
||||
|
||||
/**
|
||||
* @typedef {object} ReadJsonOptions
|
||||
* @property {boolean=} stripComments Whether to strip JSONC comments
|
||||
*/
|
||||
|
||||
/** @type {WeakMap<Buffer | Uint8Array, JsonObject>} */
|
||||
const _stripCommentsCache = new WeakMap();
|
||||
|
||||
// Only constructed for non-Buffer input: on Node the `Buffer.isBuffer` branch
|
||||
// in `decodeText` handles decoding, so the global `TextDecoder` (Node 11+,
|
||||
// always present in browsers/Deno/Bun) is only reached off the Buffer path.
|
||||
// `ignoreBOM: true` keeps a leading BOM in the output, matching
|
||||
// `Buffer.toString("utf8")` so both decode paths behave identically.
|
||||
// eslint-disable-next-line n/no-unsupported-features/node-builtins
|
||||
const getDecoder = memoize(() => new TextDecoder("utf-8", { ignoreBOM: true }));
|
||||
|
||||
/**
|
||||
* Decode a file's raw contents to text without assuming a Node runtime. A
|
||||
* `Buffer` (Node) uses its fast native `toString`; any other binary input
|
||||
* (`Uint8Array` from a browser/Deno/Bun file system) goes through
|
||||
* `TextDecoder`, and strings are returned as-is.
|
||||
* @param {string | Buffer | Uint8Array} data raw file contents
|
||||
* @returns {string} decoded text
|
||||
*/
|
||||
const decodeText = (data) => {
|
||||
if (typeof data === "string") return data;
|
||||
if (typeof Buffer !== "undefined" && Buffer.isBuffer(data)) {
|
||||
return data.toString("utf8");
|
||||
}
|
||||
return getDecoder().decode(data);
|
||||
};
|
||||
|
||||
/**
|
||||
* Read and parse JSON file (supports JSONC with comments).
|
||||
* Callback-based so a synchronous `fileSystem` stays synchronous all the
|
||||
* way through — Promise wrapping would defer resolution by a Promise tick
|
||||
* and break `resolveSync` when `tsconfig` is used together with
|
||||
* `useSyncFileSystemCalls: true`.
|
||||
* @param {FileSystem} fileSystem the file system
|
||||
* @param {string} jsonFilePath absolute path to JSON file
|
||||
* @param {ReadJsonOptions} options Options
|
||||
* @param {(err: NodeJS.ErrnoException | Error | null, content?: JsonObject) => void} callback callback
|
||||
* @returns {void}
|
||||
*/
|
||||
function readJson(fileSystem, jsonFilePath, options, callback) {
|
||||
const { stripComments = false } = options;
|
||||
const { readJson: fsReadJson } = fileSystem;
|
||||
if (fsReadJson && !stripComments) {
|
||||
fsReadJson(jsonFilePath, (err, content) => {
|
||||
if (err) return callback(err);
|
||||
callback(null, /** @type {JsonObject} */ (content));
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
fileSystem.readFile(jsonFilePath, (err, data) => {
|
||||
if (err) return callback(err);
|
||||
const buf = /** @type {Buffer | Uint8Array | string} */ (data);
|
||||
|
||||
// The strip-comments cache is keyed by the file-contents object; a file
|
||||
// system may hand back a plain string, which cannot be a WeakMap key, so
|
||||
// only cache when the contents are an object.
|
||||
const cacheable = stripComments && typeof buf === "object";
|
||||
|
||||
if (cacheable) {
|
||||
const cached = _stripCommentsCache.get(buf);
|
||||
if (cached !== undefined) return callback(null, cached);
|
||||
}
|
||||
|
||||
let result;
|
||||
try {
|
||||
const jsonText = decodeText(buf);
|
||||
const jsonWithoutComments = stripComments
|
||||
? stripJsonComments(jsonText, {
|
||||
trailingCommas: true,
|
||||
whitespace: true,
|
||||
})
|
||||
: jsonText;
|
||||
result = JSON.parse(jsonWithoutComments);
|
||||
} catch (parseErr) {
|
||||
return callback(/** @type {Error} */ (parseErr));
|
||||
}
|
||||
|
||||
if (cacheable) {
|
||||
_stripCommentsCache.set(buf, result);
|
||||
}
|
||||
|
||||
callback(null, result);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports.decodeText = decodeText;
|
||||
module.exports.readJson = readJson;
|
||||
40
web/node_modules/enhanced-resolve/lib/util/graceful-fs-browser.js
generated
vendored
Normal file
40
web/node_modules/enhanced-resolve/lib/util/graceful-fs-browser.js
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
MIT License http://www.opensource.org/licenses/mit-license.php
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
// Browser stub for the default `graceful-fs` file system. A browser has no
|
||||
// ambient file system, so the convenience entry's default resolver has nothing
|
||||
// to read — pass your own `fileSystem` to `create()` /
|
||||
// `ResolverFactory.createResolver()` instead. Each method throws a clear error
|
||||
// if the default is actually used, while keeping the package able to bundle for
|
||||
// the browser (no Node `fs` dependency is pulled in).
|
||||
|
||||
const message =
|
||||
"enhanced-resolve: no default file system is available in the browser. " +
|
||||
"Pass a `fileSystem` to `create()` or `ResolverFactory.createResolver()`.";
|
||||
|
||||
/**
|
||||
* @returns {never} always throws
|
||||
*/
|
||||
const unavailable = () => {
|
||||
throw new Error(message);
|
||||
};
|
||||
|
||||
// The same set of methods `CachedInputFileSystem` reads from the file system
|
||||
// (graceful-fs has no `readJson`/`readJsonSync`, so those stay undefined).
|
||||
module.exports = {
|
||||
lstat: unavailable,
|
||||
lstatSync: unavailable,
|
||||
readFile: unavailable,
|
||||
readFileSync: unavailable,
|
||||
readdir: unavailable,
|
||||
readdirSync: unavailable,
|
||||
readlink: unavailable,
|
||||
readlinkSync: unavailable,
|
||||
realpath: unavailable,
|
||||
realpathSync: unavailable,
|
||||
stat: unavailable,
|
||||
statSync: unavailable,
|
||||
};
|
||||
101
web/node_modules/enhanced-resolve/lib/util/identifier.js
generated
vendored
Normal file
101
web/node_modules/enhanced-resolve/lib/util/identifier.js
generated
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
MIT License http://www.opensource.org/licenses/mit-license.php
|
||||
Author Ivan Kopeykin @vankop
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const { fileURLToPath } = require("url");
|
||||
|
||||
const PATH_QUERY_FRAGMENT_REGEXP =
|
||||
/^(#?(?:\0.|[^?#\0])*)(\?(?:\0.|[^#\0])*)?(#.*)?$/;
|
||||
const ZERO_ESCAPE_REGEXP = /\0(.)/g;
|
||||
const FILE_REG_EXP = /file:/i;
|
||||
|
||||
/**
|
||||
* Index past a DOS device path prefix (`\\?\…` or `\\.\…`), or 0. Kept
|
||||
* out of `parseIdentifier` on purpose: inlining it back bloats the caller
|
||||
* beyond the size where V8's interpreter and JIT both handle it well
|
||||
* (the cause of the description-files-multi CodSpeed regression).
|
||||
* @param {string} identifier identifier known to start with `\`
|
||||
* @returns {number} 4 if identifier starts with a DOS device prefix, else 0
|
||||
*/
|
||||
function dosPrefixEnd(identifier) {
|
||||
if (
|
||||
identifier.length >= 4 &&
|
||||
identifier.charCodeAt(1) === 92 &&
|
||||
identifier.charCodeAt(3) === 92
|
||||
) {
|
||||
const c2 = identifier.charCodeAt(2);
|
||||
if (c2 === 63 || c2 === 46) return 4;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} identifier identifier
|
||||
* @returns {[string, string, string] | null} parsed identifier
|
||||
*/
|
||||
function parseIdentifier(identifier) {
|
||||
if (!identifier) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (FILE_REG_EXP.test(identifier)) {
|
||||
identifier = fileURLToPath(identifier);
|
||||
}
|
||||
|
||||
const firstEscape = identifier.indexOf("\0");
|
||||
|
||||
// Handle `\0`
|
||||
if (firstEscape !== -1) {
|
||||
const match = PATH_QUERY_FRAGMENT_REGEXP.exec(identifier);
|
||||
|
||||
if (!match) return null;
|
||||
|
||||
return [
|
||||
match[1].replace(ZERO_ESCAPE_REGEXP, "$1"),
|
||||
match[2] ? match[2].replace(ZERO_ESCAPE_REGEXP, "$1") : "",
|
||||
match[3] || "",
|
||||
];
|
||||
}
|
||||
|
||||
// Fast path for inputs that don't use \0 escaping. DOS device paths
|
||||
// (`\\?\…`, `\\.\…`) embed a literal `?` / `.` that must not be read
|
||||
// as a query separator; skip past the prefix when the input actually
|
||||
// starts with `\`. Gate is a single char-code compare so this function
|
||||
// stays inside V8's inline budget for its hot callers (resolver parse).
|
||||
const scanStart =
|
||||
identifier.charCodeAt(0) === 92 ? dosPrefixEnd(identifier) : 0;
|
||||
const queryStart = identifier.indexOf("?", scanStart);
|
||||
// Start at index 1 (or past a DOS prefix) to ignore a possible leading hash.
|
||||
const fragmentStart = identifier.indexOf("#", scanStart || 1);
|
||||
|
||||
if (fragmentStart < 0) {
|
||||
if (queryStart < 0) {
|
||||
// No fragment, no query
|
||||
return [identifier, "", ""];
|
||||
}
|
||||
|
||||
// Query, no fragment
|
||||
return [identifier.slice(0, queryStart), identifier.slice(queryStart), ""];
|
||||
}
|
||||
|
||||
if (queryStart < 0 || fragmentStart < queryStart) {
|
||||
// Fragment, no query
|
||||
return [
|
||||
identifier.slice(0, fragmentStart),
|
||||
"",
|
||||
identifier.slice(fragmentStart),
|
||||
];
|
||||
}
|
||||
|
||||
// Query and fragment
|
||||
return [
|
||||
identifier.slice(0, queryStart),
|
||||
identifier.slice(queryStart, fragmentStart),
|
||||
identifier.slice(fragmentStart),
|
||||
];
|
||||
}
|
||||
|
||||
module.exports.parseIdentifier = parseIdentifier;
|
||||
37
web/node_modules/enhanced-resolve/lib/util/memoize.js
generated
vendored
Normal file
37
web/node_modules/enhanced-resolve/lib/util/memoize.js
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
MIT License http://www.opensource.org/licenses/mit-license.php
|
||||
Author Tobias Koppers @sokra
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @typedef {() => T} FunctionReturning
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @param {FunctionReturning<T>} fn memorized function
|
||||
* @returns {FunctionReturning<T>} new function
|
||||
*/
|
||||
const memoize = (fn) => {
|
||||
let cache = false;
|
||||
/** @type {T | undefined} */
|
||||
let result;
|
||||
return () => {
|
||||
if (cache) {
|
||||
return /** @type {T} */ (result);
|
||||
}
|
||||
|
||||
result = fn();
|
||||
cache = true;
|
||||
// Allow to clean up memory for fn
|
||||
// and all dependent resources
|
||||
/** @type {FunctionReturning<T> | undefined} */
|
||||
(fn) = undefined;
|
||||
return /** @type {T} */ (result);
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = memoize;
|
||||
8
web/node_modules/enhanced-resolve/lib/util/module-browser.js
generated
vendored
Normal file
8
web/node_modules/enhanced-resolve/lib/util/module-browser.js
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
/*
|
||||
MIT License http://www.opensource.org/licenses/mit-license.php
|
||||
Author Tobias Koppers @sokra
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
module.exports = {};
|
||||
407
web/node_modules/enhanced-resolve/lib/util/path-browser.js
generated
vendored
Normal file
407
web/node_modules/enhanced-resolve/lib/util/path-browser.js
generated
vendored
Normal file
@@ -0,0 +1,407 @@
|
||||
/*
|
||||
MIT License http://www.opensource.org/licenses/mit-license.php
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
// A browser shim for the Node `path` builtin, exposing only the subset used by
|
||||
// `lib/util/path.js`: `posix.normalize`, `posix.dirname`, `win32.normalize`,
|
||||
// `win32.dirname` and a (posix) `basename`. It is a faithful port of Node's
|
||||
// `path` implementation and is wired in through the package `browser` field, so
|
||||
// Node, Deno and Bun keep using their native `path` and only browser bundles
|
||||
// use this. Inputs are always strings here, so Node's `validateString` guards
|
||||
// are omitted.
|
||||
|
||||
const CHAR_UPPERCASE_A = 65;
|
||||
const CHAR_UPPERCASE_Z = 90;
|
||||
const CHAR_LOWERCASE_A = 97;
|
||||
const CHAR_LOWERCASE_Z = 122;
|
||||
const CHAR_DOT = 46;
|
||||
const CHAR_FORWARD_SLASH = 47;
|
||||
const CHAR_BACKWARD_SLASH = 92;
|
||||
const CHAR_COLON = 58;
|
||||
|
||||
/**
|
||||
* @param {number} code char code
|
||||
* @returns {boolean} true for `/`
|
||||
*/
|
||||
const isPosixPathSeparator = (code) => code === CHAR_FORWARD_SLASH;
|
||||
|
||||
/**
|
||||
* @param {number} code char code
|
||||
* @returns {boolean} true for `/` or `\`
|
||||
*/
|
||||
const isPathSeparator = (code) =>
|
||||
code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH;
|
||||
|
||||
/**
|
||||
* @param {number} code char code
|
||||
* @returns {boolean} true for an ASCII letter (a windows device root)
|
||||
*/
|
||||
const isWindowsDeviceRoot = (code) =>
|
||||
(code >= CHAR_UPPERCASE_A && code <= CHAR_UPPERCASE_Z) ||
|
||||
(code >= CHAR_LOWERCASE_A && code <= CHAR_LOWERCASE_Z);
|
||||
|
||||
/**
|
||||
* Resolve `.` and `..` segments in a path. Ported from Node's internal
|
||||
* `normalizeString`.
|
||||
* @param {string} path path
|
||||
* @param {boolean} allowAboveRoot whether leading `..` may be kept
|
||||
* @param {string} separator path separator to emit
|
||||
* @param {(code: number) => boolean} isSeparator separator predicate
|
||||
* @returns {string} normalized path string (without root)
|
||||
*/
|
||||
function normalizeString(path, allowAboveRoot, separator, isSeparator) {
|
||||
let res = "";
|
||||
let lastSegmentLength = 0;
|
||||
let lastSlash = -1;
|
||||
let dots = 0;
|
||||
let code = 0;
|
||||
for (let i = 0; i <= path.length; ++i) {
|
||||
if (i < path.length) {
|
||||
code = path.charCodeAt(i);
|
||||
} else if (isSeparator(code)) {
|
||||
break;
|
||||
} else {
|
||||
code = CHAR_FORWARD_SLASH;
|
||||
}
|
||||
|
||||
if (isSeparator(code)) {
|
||||
if (lastSlash === i - 1 || dots === 1) {
|
||||
// NOOP
|
||||
} else if (dots === 2) {
|
||||
if (
|
||||
res.length < 2 ||
|
||||
lastSegmentLength !== 2 ||
|
||||
res.charCodeAt(res.length - 1) !== CHAR_DOT ||
|
||||
res.charCodeAt(res.length - 2) !== CHAR_DOT
|
||||
) {
|
||||
if (res.length > 2) {
|
||||
const lastSlashIndex = res.lastIndexOf(separator);
|
||||
if (lastSlashIndex === -1) {
|
||||
res = "";
|
||||
lastSegmentLength = 0;
|
||||
} else {
|
||||
res = res.slice(0, lastSlashIndex);
|
||||
lastSegmentLength = res.length - 1 - res.lastIndexOf(separator);
|
||||
}
|
||||
lastSlash = i;
|
||||
dots = 0;
|
||||
continue;
|
||||
} else if (res.length !== 0) {
|
||||
res = "";
|
||||
lastSegmentLength = 0;
|
||||
lastSlash = i;
|
||||
dots = 0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (allowAboveRoot) {
|
||||
res += res.length > 0 ? `${separator}..` : "..";
|
||||
lastSegmentLength = 2;
|
||||
}
|
||||
} else {
|
||||
if (res.length > 0) {
|
||||
res += `${separator}${path.slice(lastSlash + 1, i)}`;
|
||||
} else {
|
||||
res = path.slice(lastSlash + 1, i);
|
||||
}
|
||||
lastSegmentLength = i - lastSlash - 1;
|
||||
}
|
||||
lastSlash = i;
|
||||
dots = 0;
|
||||
} else if (code === CHAR_DOT && dots !== -1) {
|
||||
++dots;
|
||||
} else {
|
||||
dots = -1;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} path path
|
||||
* @returns {string} normalized posix path
|
||||
*/
|
||||
function posixNormalize(path) {
|
||||
if (path.length === 0) return ".";
|
||||
|
||||
const isAbsolute = path.charCodeAt(0) === CHAR_FORWARD_SLASH;
|
||||
const trailingSeparator =
|
||||
path.charCodeAt(path.length - 1) === CHAR_FORWARD_SLASH;
|
||||
|
||||
path = normalizeString(path, !isAbsolute, "/", isPosixPathSeparator);
|
||||
|
||||
if (path.length === 0) {
|
||||
if (isAbsolute) return "/";
|
||||
return trailingSeparator ? "./" : ".";
|
||||
}
|
||||
if (trailingSeparator) path += "/";
|
||||
|
||||
return isAbsolute ? `/${path}` : path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} path path
|
||||
* @returns {string} posix dirname
|
||||
*/
|
||||
function posixDirname(path) {
|
||||
if (path.length === 0) return ".";
|
||||
const hasRoot = path.charCodeAt(0) === CHAR_FORWARD_SLASH;
|
||||
let end = -1;
|
||||
let matchedSlash = true;
|
||||
for (let i = path.length - 1; i >= 1; --i) {
|
||||
if (path.charCodeAt(i) === CHAR_FORWARD_SLASH) {
|
||||
if (!matchedSlash) {
|
||||
end = i;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
matchedSlash = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (end === -1) return hasRoot ? "/" : ".";
|
||||
if (hasRoot && end === 1) return "//";
|
||||
return path.slice(0, end);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes drive paths (`C:\…`), UNC paths (`\\server\share\…`), DOS device
|
||||
* paths (`\\.\…`, `\\?\…`) and relative/normal segments like Node's
|
||||
* `path.win32.normalize`, including the CVE-2024-36139 colon-segment guard.
|
||||
*
|
||||
* Scope note: reserved Windows device names (`CON`, `COM1`, `LPT1`, …) are not
|
||||
* special-cased, so e.g. `\\.\COM1:` differs from Node. Such names cannot occur
|
||||
* in a browser (the only place this shim is used) and the resolver never routes
|
||||
* them to `win32.normalize`, so this does not affect resolution.
|
||||
* @param {string} path path
|
||||
* @returns {string} normalized win32 path
|
||||
*/
|
||||
function win32Normalize(path) {
|
||||
const len = path.length;
|
||||
if (len === 0) return ".";
|
||||
let rootEnd = 0;
|
||||
/** @type {string | undefined} */
|
||||
let device;
|
||||
let isAbsolute = false;
|
||||
const code = path.charCodeAt(0);
|
||||
|
||||
if (len === 1) {
|
||||
return isPosixPathSeparator(code) ? "\\" : path;
|
||||
}
|
||||
|
||||
if (isPathSeparator(code)) {
|
||||
// Possible UNC root; an initial separator means an absolute path.
|
||||
isAbsolute = true;
|
||||
|
||||
if (isPathSeparator(path.charCodeAt(1))) {
|
||||
// Matched double path separator at beginning
|
||||
let j = 2;
|
||||
let last = j;
|
||||
while (j < len && !isPathSeparator(path.charCodeAt(j))) j++;
|
||||
if (j < len && j !== last) {
|
||||
const firstPart = path.slice(last, j);
|
||||
last = j;
|
||||
while (j < len && isPathSeparator(path.charCodeAt(j))) j++;
|
||||
if (j < len && j !== last) {
|
||||
last = j;
|
||||
while (j < len && !isPathSeparator(path.charCodeAt(j))) j++;
|
||||
if (j === len || j !== last) {
|
||||
if (firstPart === "." || firstPart === "?") {
|
||||
// Device root, e.g. `\\.\pipe\…` or `\\?\C:\…`
|
||||
device = `\\\\${firstPart}`;
|
||||
rootEnd = 4;
|
||||
} else if (j === len) {
|
||||
// Matched a UNC root only
|
||||
return `\\\\${firstPart}\\${path.slice(last)}\\`;
|
||||
} else {
|
||||
// Matched a UNC root with leftovers
|
||||
device = `\\\\${firstPart}\\${path.slice(last, j)}`;
|
||||
rootEnd = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
rootEnd = 1;
|
||||
}
|
||||
} else if (isWindowsDeviceRoot(code) && path.charCodeAt(1) === CHAR_COLON) {
|
||||
// Possible device root
|
||||
device = path.slice(0, 2);
|
||||
rootEnd = 2;
|
||||
if (len > 2 && isPathSeparator(path.charCodeAt(2))) {
|
||||
isAbsolute = true;
|
||||
rootEnd = 3;
|
||||
}
|
||||
}
|
||||
|
||||
let tail =
|
||||
rootEnd < len
|
||||
? normalizeString(path.slice(rootEnd), !isAbsolute, "\\", isPathSeparator)
|
||||
: "";
|
||||
if (tail.length === 0 && !isAbsolute) tail = ".";
|
||||
if (tail.length > 0 && isPathSeparator(path.charCodeAt(len - 1))) {
|
||||
tail += "\\";
|
||||
}
|
||||
if (!isAbsolute && device === undefined && path.includes(":")) {
|
||||
// A relative path that wasn't resolved to a device must not turn into
|
||||
// something Windows could read as an absolute/drive path (CVE-2024-36139).
|
||||
if (
|
||||
tail.length >= 2 &&
|
||||
isWindowsDeviceRoot(tail.charCodeAt(0)) &&
|
||||
tail.charCodeAt(1) === CHAR_COLON
|
||||
) {
|
||||
return `.\\${tail}`;
|
||||
}
|
||||
let index = path.indexOf(":");
|
||||
do {
|
||||
if (index === len - 1 || isPathSeparator(path.charCodeAt(index + 1))) {
|
||||
return `.\\${tail}`;
|
||||
}
|
||||
} while ((index = path.indexOf(":", index + 1)) !== -1);
|
||||
}
|
||||
if (device === undefined) {
|
||||
return isAbsolute ? `\\${tail}` : tail;
|
||||
}
|
||||
return isAbsolute ? `${device}\\${tail}` : `${device}${tail}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} path path
|
||||
* @returns {string} win32 dirname
|
||||
*/
|
||||
function win32Dirname(path) {
|
||||
const len = path.length;
|
||||
if (len === 0) return ".";
|
||||
let rootEnd = -1;
|
||||
let offset = 0;
|
||||
const code = path.charCodeAt(0);
|
||||
|
||||
if (len === 1) {
|
||||
return isPathSeparator(code) ? path : ".";
|
||||
}
|
||||
|
||||
if (isPathSeparator(code)) {
|
||||
// Possible UNC root
|
||||
rootEnd = offset = 1;
|
||||
|
||||
if (isPathSeparator(path.charCodeAt(1))) {
|
||||
let j = 2;
|
||||
let last = j;
|
||||
while (j < len && !isPathSeparator(path.charCodeAt(j))) j++;
|
||||
if (j < len && j !== last) {
|
||||
last = j;
|
||||
while (j < len && isPathSeparator(path.charCodeAt(j))) j++;
|
||||
if (j < len && j !== last) {
|
||||
last = j;
|
||||
while (j < len && !isPathSeparator(path.charCodeAt(j))) j++;
|
||||
if (j === len) {
|
||||
// Matched a UNC root only
|
||||
return path;
|
||||
}
|
||||
if (j !== last) {
|
||||
// Matched a UNC root with leftovers
|
||||
rootEnd = offset = j + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (isWindowsDeviceRoot(code) && path.charCodeAt(1) === CHAR_COLON) {
|
||||
rootEnd = len > 2 && isPathSeparator(path.charCodeAt(2)) ? 3 : 2;
|
||||
offset = rootEnd;
|
||||
}
|
||||
|
||||
let end = -1;
|
||||
let matchedSlash = true;
|
||||
for (let i = len - 1; i >= offset; --i) {
|
||||
if (isPathSeparator(path.charCodeAt(i))) {
|
||||
if (!matchedSlash) {
|
||||
end = i;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
matchedSlash = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (end === -1) {
|
||||
if (rootEnd === -1) return ".";
|
||||
end = rootEnd;
|
||||
}
|
||||
return path.slice(0, end);
|
||||
}
|
||||
|
||||
/**
|
||||
* Posix `basename` — the browser is treated as a posix platform, matching how
|
||||
* Node picks the posix variant for `path.basename` on non-Windows systems.
|
||||
* @param {string} path path
|
||||
* @param {string=} suffix optional suffix to strip
|
||||
* @returns {string} basename
|
||||
*/
|
||||
function basename(path, suffix) {
|
||||
let start = 0;
|
||||
let end = -1;
|
||||
let matchedSlash = true;
|
||||
|
||||
if (
|
||||
suffix !== undefined &&
|
||||
suffix.length > 0 &&
|
||||
suffix.length <= path.length
|
||||
) {
|
||||
if (suffix === path) return "";
|
||||
let extIdx = suffix.length - 1;
|
||||
let firstNonSlashEnd = -1;
|
||||
for (let i = path.length - 1; i >= 0; --i) {
|
||||
const code = path.charCodeAt(i);
|
||||
if (code === CHAR_FORWARD_SLASH) {
|
||||
if (!matchedSlash) {
|
||||
start = i + 1;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (firstNonSlashEnd === -1) {
|
||||
matchedSlash = false;
|
||||
firstNonSlashEnd = i + 1;
|
||||
}
|
||||
if (extIdx >= 0) {
|
||||
if (code === suffix.charCodeAt(extIdx)) {
|
||||
if (--extIdx === -1) {
|
||||
end = i;
|
||||
}
|
||||
} else {
|
||||
extIdx = -1;
|
||||
end = firstNonSlashEnd;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (start === end) {
|
||||
end = firstNonSlashEnd;
|
||||
} else if (end === -1) {
|
||||
end = path.length;
|
||||
}
|
||||
return path.slice(start, end);
|
||||
}
|
||||
for (let i = path.length - 1; i >= 0; --i) {
|
||||
if (path.charCodeAt(i) === CHAR_FORWARD_SLASH) {
|
||||
if (!matchedSlash) {
|
||||
start = i + 1;
|
||||
break;
|
||||
}
|
||||
} else if (end === -1) {
|
||||
matchedSlash = false;
|
||||
end = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (end === -1) return "";
|
||||
return path.slice(start, end);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
basename,
|
||||
posix: { normalize: posixNormalize, dirname: posixDirname },
|
||||
win32: { normalize: win32Normalize, dirname: win32Dirname },
|
||||
};
|
||||
450
web/node_modules/enhanced-resolve/lib/util/path.js
generated
vendored
Normal file
450
web/node_modules/enhanced-resolve/lib/util/path.js
generated
vendored
Normal file
@@ -0,0 +1,450 @@
|
||||
/*
|
||||
MIT License http://www.opensource.org/licenses/mit-license.php
|
||||
Author Tobias Koppers @sokra
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const path = require("path");
|
||||
const { fileURLToPath } = require("url");
|
||||
|
||||
const CHAR_HASH = "#".charCodeAt(0);
|
||||
const CHAR_SLASH = "/".charCodeAt(0);
|
||||
const CHAR_BACKSLASH = "\\".charCodeAt(0);
|
||||
const CHAR_A = "A".charCodeAt(0);
|
||||
const CHAR_Z = "Z".charCodeAt(0);
|
||||
const CHAR_LOWER_A = "a".charCodeAt(0);
|
||||
const CHAR_LOWER_Z = "z".charCodeAt(0);
|
||||
const CHAR_DOT = ".".charCodeAt(0);
|
||||
const CHAR_COLON = ":".charCodeAt(0);
|
||||
|
||||
const posixNormalize = path.posix.normalize;
|
||||
const winNormalize = path.win32.normalize;
|
||||
|
||||
/**
|
||||
* @enum {number}
|
||||
*/
|
||||
const PathType = Object.freeze({
|
||||
Empty: 0,
|
||||
Normal: 1,
|
||||
Relative: 2,
|
||||
AbsoluteWin: 3,
|
||||
AbsolutePosix: 4,
|
||||
Internal: 5,
|
||||
});
|
||||
|
||||
const deprecatedInvalidSegmentRegEx =
|
||||
/(^|\\|\/)((\.|%2e)(\.|%2e)?|(n|%6e|%4e)(o|%6f|%4f)(d|%64|%44)(e|%65|%45)(_|%5f)(m|%6d|%4d)(o|%6f|%4f)(d|%64|%44)(u|%75|%55)(l|%6c|%4c)(e|%65|%45)(s|%73|%53))(\\|\/|$)/i;
|
||||
|
||||
const invalidSegmentRegEx =
|
||||
/(^|\\|\/)((\.|%2e)(\.|%2e)?|(n|%6e|%4e)(o|%6f|%4f)(d|%64|%44)(e|%65|%45)(_|%5f)(m|%6d|%4d)(o|%6f|%4f)(d|%64|%44)(u|%75|%55)(l|%6c|%4c)(e|%65|%45)(s|%73|%53))?(\\|\/|$)/i;
|
||||
|
||||
/**
|
||||
* @param {string} maybePath a path
|
||||
* @returns {PathType} type of path
|
||||
*/
|
||||
const getType = (maybePath) => {
|
||||
switch (maybePath.length) {
|
||||
case 0:
|
||||
return PathType.Empty;
|
||||
case 1: {
|
||||
const c0 = maybePath.charCodeAt(0);
|
||||
switch (c0) {
|
||||
case CHAR_DOT:
|
||||
return PathType.Relative;
|
||||
case CHAR_SLASH:
|
||||
return PathType.AbsolutePosix;
|
||||
case CHAR_HASH:
|
||||
return PathType.Internal;
|
||||
}
|
||||
return PathType.Normal;
|
||||
}
|
||||
case 2: {
|
||||
const c0 = maybePath.charCodeAt(0);
|
||||
switch (c0) {
|
||||
case CHAR_DOT: {
|
||||
const c1 = maybePath.charCodeAt(1);
|
||||
switch (c1) {
|
||||
case CHAR_DOT:
|
||||
case CHAR_SLASH:
|
||||
return PathType.Relative;
|
||||
}
|
||||
return PathType.Normal;
|
||||
}
|
||||
case CHAR_SLASH:
|
||||
return PathType.AbsolutePosix;
|
||||
case CHAR_HASH:
|
||||
return PathType.Internal;
|
||||
}
|
||||
const c1 = maybePath.charCodeAt(1);
|
||||
if (
|
||||
c1 === CHAR_COLON &&
|
||||
((c0 >= CHAR_A && c0 <= CHAR_Z) ||
|
||||
(c0 >= CHAR_LOWER_A && c0 <= CHAR_LOWER_Z))
|
||||
) {
|
||||
return PathType.AbsoluteWin;
|
||||
}
|
||||
if (c0 === CHAR_BACKSLASH && c1 === CHAR_BACKSLASH) {
|
||||
return PathType.AbsoluteWin;
|
||||
}
|
||||
return PathType.Normal;
|
||||
}
|
||||
}
|
||||
const c0 = maybePath.charCodeAt(0);
|
||||
switch (c0) {
|
||||
case CHAR_DOT: {
|
||||
const c1 = maybePath.charCodeAt(1);
|
||||
switch (c1) {
|
||||
case CHAR_SLASH:
|
||||
return PathType.Relative;
|
||||
case CHAR_DOT: {
|
||||
const c2 = maybePath.charCodeAt(2);
|
||||
if (c2 === CHAR_SLASH) return PathType.Relative;
|
||||
return PathType.Normal;
|
||||
}
|
||||
}
|
||||
return PathType.Normal;
|
||||
}
|
||||
case CHAR_SLASH:
|
||||
return PathType.AbsolutePosix;
|
||||
case CHAR_HASH:
|
||||
return PathType.Internal;
|
||||
}
|
||||
const c1 = maybePath.charCodeAt(1);
|
||||
if (c1 === CHAR_COLON) {
|
||||
const c2 = maybePath.charCodeAt(2);
|
||||
if (
|
||||
(c2 === CHAR_BACKSLASH || c2 === CHAR_SLASH) &&
|
||||
((c0 >= CHAR_A && c0 <= CHAR_Z) ||
|
||||
(c0 >= CHAR_LOWER_A && c0 <= CHAR_LOWER_Z))
|
||||
) {
|
||||
return PathType.AbsoluteWin;
|
||||
}
|
||||
}
|
||||
// Two leading backslashes root a UNC share (`\\server\share`) or a DOS
|
||||
// device path (`\\?\…`, `\\.\…`); `path.win32` reads either as absolute,
|
||||
// so both belong on `path.win32` and not on posix.
|
||||
if (c0 === CHAR_BACKSLASH && c1 === CHAR_BACKSLASH) {
|
||||
return PathType.AbsoluteWin;
|
||||
}
|
||||
return PathType.Normal;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} maybePath a path
|
||||
* @returns {string} the normalized path
|
||||
*/
|
||||
const normalize = (maybePath) => {
|
||||
switch (getType(maybePath)) {
|
||||
case PathType.Empty:
|
||||
return maybePath;
|
||||
case PathType.AbsoluteWin:
|
||||
return winNormalize(maybePath);
|
||||
case PathType.Relative: {
|
||||
const r = posixNormalize(maybePath);
|
||||
return getType(r) === PathType.Relative ? r : `./${r}`;
|
||||
}
|
||||
}
|
||||
return posixNormalize(maybePath);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} rootPath the root path
|
||||
* @param {string | undefined} request the request path
|
||||
* @returns {string} the joined path
|
||||
*/
|
||||
const join = (rootPath, request) => {
|
||||
if (!request) return normalize(rootPath);
|
||||
const requestType = getType(request);
|
||||
switch (requestType) {
|
||||
case PathType.AbsolutePosix:
|
||||
return posixNormalize(request);
|
||||
case PathType.AbsoluteWin:
|
||||
return winNormalize(request);
|
||||
}
|
||||
switch (getType(rootPath)) {
|
||||
case PathType.Normal:
|
||||
case PathType.Relative:
|
||||
case PathType.AbsolutePosix:
|
||||
return posixNormalize(`${rootPath}/${request}`);
|
||||
case PathType.AbsoluteWin:
|
||||
return winNormalize(`${rootPath}\\${request}`);
|
||||
}
|
||||
switch (requestType) {
|
||||
case PathType.Empty:
|
||||
return rootPath;
|
||||
case PathType.Relative: {
|
||||
const r = posixNormalize(rootPath);
|
||||
return getType(r) === PathType.Relative ? r : `./${r}`;
|
||||
}
|
||||
}
|
||||
return posixNormalize(rootPath);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} maybePath a path
|
||||
* @returns {string} the directory name
|
||||
*/
|
||||
const dirname = (maybePath) => {
|
||||
switch (getType(maybePath)) {
|
||||
case PathType.AbsoluteWin:
|
||||
return path.win32.dirname(maybePath);
|
||||
}
|
||||
return path.posix.dirname(maybePath);
|
||||
};
|
||||
|
||||
/** @typedef {{ fn: (rootPath: string, request: string) => string, cache: Map<string, Map<string, string | undefined>> }} CachedJoin */
|
||||
|
||||
/**
|
||||
* @returns {CachedJoin} cached join
|
||||
*/
|
||||
const createCachedJoin = () => {
|
||||
/** @type {CachedJoin["cache"]} */
|
||||
const cache = new Map();
|
||||
/** @type {CachedJoin["fn"]} */
|
||||
const fn = (rootPath, request) => {
|
||||
/** @type {string | undefined} */
|
||||
let cacheEntry;
|
||||
let inner = cache.get(rootPath);
|
||||
if (inner === undefined) {
|
||||
cache.set(rootPath, (inner = new Map()));
|
||||
} else {
|
||||
cacheEntry = inner.get(request);
|
||||
if (cacheEntry !== undefined) return cacheEntry;
|
||||
}
|
||||
cacheEntry = join(rootPath, request);
|
||||
inner.set(request, cacheEntry);
|
||||
return cacheEntry;
|
||||
};
|
||||
return { fn, cache };
|
||||
};
|
||||
|
||||
/** @typedef {{ fn: (maybePath: string) => string, cache: Map<string, string> }} CachedDirname */
|
||||
|
||||
/**
|
||||
* @returns {CachedDirname} cached dirname
|
||||
*/
|
||||
const createCachedDirname = () => {
|
||||
/** @type {CachedDirname["cache"]} */
|
||||
const cache = new Map();
|
||||
/** @type {CachedDirname["fn"]} */
|
||||
const fn = (maybePath) => {
|
||||
const cacheEntry = cache.get(maybePath);
|
||||
if (cacheEntry !== undefined) return cacheEntry;
|
||||
const result = dirname(maybePath);
|
||||
cache.set(maybePath, result);
|
||||
return result;
|
||||
};
|
||||
return { fn, cache };
|
||||
};
|
||||
|
||||
/** @typedef {{ fn: (maybePath: string, suffix?: string) => string, cache: Map<string, Map<string | undefined, string | undefined>> }} CachedBasename */
|
||||
|
||||
/**
|
||||
* @returns {CachedBasename} cached basename
|
||||
*/
|
||||
const createCachedBasename = () => {
|
||||
/** @type {CachedBasename["cache"]} */
|
||||
const cache = new Map();
|
||||
/** @type {CachedBasename["fn"]} */
|
||||
const fn = (maybePath, suffix) => {
|
||||
/** @type {string | undefined} */
|
||||
let cacheEntry;
|
||||
let inner = cache.get(maybePath);
|
||||
if (inner === undefined) {
|
||||
cache.set(maybePath, (inner = new Map()));
|
||||
} else {
|
||||
cacheEntry = inner.get(suffix);
|
||||
if (cacheEntry !== undefined) return cacheEntry;
|
||||
}
|
||||
cacheEntry = path.basename(maybePath, suffix);
|
||||
inner.set(suffix, cacheEntry);
|
||||
return cacheEntry;
|
||||
};
|
||||
return { fn, cache };
|
||||
};
|
||||
|
||||
/**
|
||||
* Whether `request` is a relative request — i.e. matches `^\.\.?(?:\/|$)`.
|
||||
*
|
||||
* This is called on every `doResolve` via `UnsafeCachePlugin` and
|
||||
* `getInnerRequest`, so the char-code form is meaningfully faster than the
|
||||
* equivalent regex test: no regex state machine, no string object churn.
|
||||
* @param {string} request request string
|
||||
* @returns {boolean} true if request is relative
|
||||
*/
|
||||
const isRelativeRequest = (request) => {
|
||||
const len = request.length;
|
||||
if (len === 0 || request.charCodeAt(0) !== CHAR_DOT) return false;
|
||||
if (len === 1) return true; // "."
|
||||
const c1 = request.charCodeAt(1);
|
||||
if (c1 === CHAR_SLASH) return true; // "./..."
|
||||
if (c1 !== CHAR_DOT) return false; // ".x..."
|
||||
if (len === 2) return true; // ".."
|
||||
return request.charCodeAt(2) === CHAR_SLASH; // "../..."
|
||||
};
|
||||
|
||||
/**
|
||||
* Whether this is a Windows path, in which `/` and `\` are interchangeable and
|
||||
* paths compare case-insensitively, as opposed to a posix path, in which `\` is
|
||||
* an ordinary filename character. Decided by the root — a drive letter or a
|
||||
* leading `\` — which is where `path.win32` and `path.posix` disagree about
|
||||
* `parse(maybePath).root`, and never by the host platform, since Windows paths
|
||||
* are resolved on posix hosts and in browsers too. A path starting with `//`
|
||||
* stays posix: `path.win32` reads it as a UNC root, but here it cannot be told
|
||||
* apart from a posix path, where `\` has to keep being a filename character.
|
||||
* @param {string} maybePath a path
|
||||
* @returns {boolean} true, when the path is a Windows path
|
||||
*/
|
||||
const isWindowsPath = (maybePath) => {
|
||||
const c0 = maybePath.charCodeAt(0);
|
||||
if (c0 === CHAR_BACKSLASH) return true;
|
||||
if (maybePath.charCodeAt(1) !== CHAR_COLON) return false;
|
||||
return (
|
||||
(c0 >= CHAR_A && c0 <= CHAR_Z) || (c0 >= CHAR_LOWER_A && c0 <= CHAR_LOWER_Z)
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {number} charCode a char code
|
||||
* @param {boolean} windowsPath whether `\` separates segments
|
||||
* @returns {boolean} true, when the char code separates path segments
|
||||
*/
|
||||
const isSeparator = (charCode, windowsPath) =>
|
||||
charCode === CHAR_SLASH || (windowsPath && charCode === CHAR_BACKSLASH);
|
||||
|
||||
/**
|
||||
* @param {string} parentPath parent directory path
|
||||
* @param {boolean} windowsPath whether `parentPath` is a Windows path
|
||||
* @returns {number} length of `parentPath` without its trailing separators
|
||||
*/
|
||||
const parentPathLength = (parentPath, windowsPath) => {
|
||||
let end = parentPath.length;
|
||||
while (end > 0 && isSeparator(parentPath.charCodeAt(end - 1), windowsPath)) {
|
||||
end--;
|
||||
}
|
||||
return end;
|
||||
};
|
||||
|
||||
/**
|
||||
* Cold path of `startsWithPath`: Node lowercases whole paths rather than single
|
||||
* characters, which only makes a difference outside of ASCII.
|
||||
* @param {string} parentPath parent directory path
|
||||
* @param {number} length number of characters to compare
|
||||
* @param {string} childPath child path to check
|
||||
* @returns {boolean} true, when both prefixes name the same Windows path
|
||||
*/
|
||||
const equalsWindowsPrefix = (parentPath, length, childPath) =>
|
||||
childPath.slice(0, length).replace(/\//g, "\\").toLowerCase() ===
|
||||
parentPath.slice(0, length).replace(/\//g, "\\").toLowerCase();
|
||||
|
||||
/**
|
||||
* @param {string} parentPath parent directory path
|
||||
* @param {number} length number of characters of `parentPath` to compare
|
||||
* @param {string} childPath child path to check
|
||||
* @param {boolean} windowsPath whether `parentPath` is a Windows path
|
||||
* @returns {boolean} true, when `childPath` starts with that prefix
|
||||
*/
|
||||
const startsWithPath = (parentPath, length, childPath, windowsPath) => {
|
||||
if (childPath.length < length) return false;
|
||||
// The common case is an exact prefix of a parent without trailing
|
||||
// separators, which `startsWith` answers natively and without a slice. For
|
||||
// a posix parent that is the whole answer, only a Windows one has more
|
||||
// spellings of the same path to try.
|
||||
if (length === parentPath.length) {
|
||||
if (childPath.startsWith(parentPath)) return true;
|
||||
if (!windowsPath) return false;
|
||||
}
|
||||
for (let i = 0; i < length; i++) {
|
||||
const childCharCode = childPath.charCodeAt(i);
|
||||
const parentCharCode = parentPath.charCodeAt(i);
|
||||
if (childCharCode === parentCharCode) continue;
|
||||
if (!windowsPath) return false;
|
||||
// Windows mixes `/` and `\` freely and compares case-insensitively.
|
||||
if (isSeparator(childCharCode, true) && isSeparator(parentCharCode, true)) {
|
||||
continue;
|
||||
}
|
||||
if (childCharCode > 127 || parentCharCode > 127) {
|
||||
return equalsWindowsPrefix(parentPath, length, childPath);
|
||||
}
|
||||
const childLower =
|
||||
childCharCode >= CHAR_A && childCharCode <= CHAR_Z
|
||||
? childCharCode + 32
|
||||
: childCharCode;
|
||||
const parentLower =
|
||||
parentCharCode >= CHAR_A && parentCharCode <= CHAR_Z
|
||||
? parentCharCode + 32
|
||||
: parentCharCode;
|
||||
if (childLower !== parentLower) return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Whether childPath is parentPath itself or a path under it, the answer node's
|
||||
* `relative(parentPath, childPath)` gives: not escaping upward and not
|
||||
* absolute. A trailing separator on the parent is not part of the boundary, so
|
||||
* `/a/b/` contains exactly what `/a/b` contains.
|
||||
* @param {string} parentPath parent directory path
|
||||
* @param {string} childPath child path to check
|
||||
* @returns {boolean} true if childPath is parentPath or is under it
|
||||
*/
|
||||
const isInside = (parentPath, childPath) => {
|
||||
const windowsPath = isWindowsPath(parentPath);
|
||||
const length = parentPathLength(parentPath, windowsPath);
|
||||
if (!startsWithPath(parentPath, length, childPath, windowsPath)) return false;
|
||||
// The parent itself, or a segment boundary right after it so that `/a/b`
|
||||
// does not contain the sibling `/a/b-other`.
|
||||
return (
|
||||
childPath.length === length ||
|
||||
isSeparator(childPath.charCodeAt(length), windowsPath)
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if childPath is a subdirectory of parentPath. Compares like `isInside`,
|
||||
* except that a path is not a subpath of itself.
|
||||
*
|
||||
* Called from `TsconfigPathsPlugin._selectPathsDataForContext` inside a loop
|
||||
* over every tsconfig-paths context on every resolve, so it's worth keeping
|
||||
* cheap: a native `startsWith` plus a separator char check answers it, and the
|
||||
* character loop only runs for a Windows path that the prefix test missed.
|
||||
* @param {string} parentPath parent directory path
|
||||
* @param {string} childPath child path to check
|
||||
* @returns {boolean} true if childPath is under parentPath
|
||||
*/
|
||||
const isSubPath = (parentPath, childPath) => {
|
||||
const windowsPath = isWindowsPath(parentPath);
|
||||
const length = parentPathLength(parentPath, windowsPath);
|
||||
if (childPath.length <= length) return false;
|
||||
if (!startsWithPath(parentPath, length, childPath, windowsPath)) return false;
|
||||
return isSeparator(childPath.charCodeAt(length), windowsPath);
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert a `file:` `URL` instance to a filesystem path; any other input
|
||||
* (including plain strings) is returned unchanged. Mirrors Node's `fs`, which
|
||||
* treats strings as literal paths and only `URL` objects as URLs (see
|
||||
* nodejs/node#17658) — so a directory literally named `file:` is never
|
||||
* mistaken for a URL.
|
||||
* @param {string | URL} maybeURL a path string or a `file:` `URL` instance
|
||||
* @returns {string} a filesystem path
|
||||
*/
|
||||
const toPath = (maybeURL) =>
|
||||
maybeURL instanceof URL ? fileURLToPath(maybeURL) : maybeURL;
|
||||
|
||||
module.exports.PathType = PathType;
|
||||
module.exports.createCachedBasename = createCachedBasename;
|
||||
module.exports.createCachedDirname = createCachedDirname;
|
||||
module.exports.createCachedJoin = createCachedJoin;
|
||||
module.exports.deprecatedInvalidSegmentRegEx = deprecatedInvalidSegmentRegEx;
|
||||
module.exports.dirname = dirname;
|
||||
module.exports.getType = getType;
|
||||
module.exports.invalidSegmentRegEx = invalidSegmentRegEx;
|
||||
module.exports.isInside = isInside;
|
||||
module.exports.isRelativeRequest = isRelativeRequest;
|
||||
module.exports.isSubPath = isSubPath;
|
||||
module.exports.isWindowsPath = isWindowsPath;
|
||||
module.exports.join = join;
|
||||
module.exports.normalize = normalize;
|
||||
module.exports.toPath = toPath;
|
||||
23
web/node_modules/enhanced-resolve/lib/util/process-browser.js
generated
vendored
Normal file
23
web/node_modules/enhanced-resolve/lib/util/process-browser.js
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
MIT License http://www.opensource.org/licenses/mit-license.php
|
||||
Author Tobias Koppers @sokra
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
/**
|
||||
* @type {Record<string, string>}
|
||||
*/
|
||||
versions: {},
|
||||
// eslint-disable-next-line jsdoc/reject-function-type
|
||||
/** @param {Function} fn function */
|
||||
nextTick(fn) {
|
||||
// eslint-disable-next-line prefer-rest-params
|
||||
const args = Array.prototype.slice.call(arguments, 1);
|
||||
Promise.resolve().then(() => {
|
||||
// eslint-disable-next-line prefer-spread
|
||||
fn.apply(null, args);
|
||||
});
|
||||
},
|
||||
};
|
||||
177
web/node_modules/enhanced-resolve/lib/util/strip-json-comments.js
generated
vendored
Normal file
177
web/node_modules/enhanced-resolve/lib/util/strip-json-comments.js
generated
vendored
Normal file
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
MIT License http://www.opensource.org/licenses/mit-license.php
|
||||
Author Natsu @xiaoxiaojx
|
||||
|
||||
This file contains code ported from strip-json-comments:
|
||||
https://github.com/sindresorhus/strip-json-comments
|
||||
Original license: MIT
|
||||
Original author: Sindre Sorhus
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* @typedef {object} StripJsonCommentsOptions
|
||||
* @property {boolean=} whitespace Replace comments with whitespace
|
||||
* @property {boolean=} trailingCommas Strip trailing commas
|
||||
*/
|
||||
|
||||
const singleComment = Symbol("singleComment");
|
||||
const multiComment = Symbol("multiComment");
|
||||
|
||||
/**
|
||||
* Strip without whitespace (returns empty string)
|
||||
* @param {string} _string Unused
|
||||
* @param {number} _start Unused
|
||||
* @param {number} _end Unused
|
||||
* @returns {string} Empty string for all input
|
||||
*/
|
||||
const stripWithoutWhitespace = (_string, _start, _end) => "";
|
||||
|
||||
/**
|
||||
* Replace all characters except ASCII spaces, tabs and line endings with regular spaces to ensure valid JSON output.
|
||||
* @param {string} string String to process
|
||||
* @param {number} start Start index
|
||||
* @param {number} end End index
|
||||
* @returns {string} Processed string with comments replaced by whitespace
|
||||
*/
|
||||
const stripWithWhitespace = (string, start, end) =>
|
||||
string.slice(start, end).replace(/[^ \t\r\n]/g, " ");
|
||||
|
||||
/**
|
||||
* Check if a quote is escaped
|
||||
* @param {string} jsonString JSON string
|
||||
* @param {number} quotePosition Position of the quote
|
||||
* @returns {boolean} True if the quote at the given position is escaped
|
||||
*/
|
||||
const isEscaped = (jsonString, quotePosition) => {
|
||||
let index = quotePosition - 1;
|
||||
let backslashCount = 0;
|
||||
|
||||
while (jsonString[index] === "\\") {
|
||||
index -= 1;
|
||||
backslashCount += 1;
|
||||
}
|
||||
|
||||
return Boolean(backslashCount % 2);
|
||||
};
|
||||
|
||||
/**
|
||||
* Strip comments from JSON string
|
||||
* @param {string} jsonString JSON string with potential comments
|
||||
* @param {StripJsonCommentsOptions} options Options
|
||||
* @returns {string} JSON string without comments
|
||||
*/
|
||||
function stripJsonComments(
|
||||
jsonString,
|
||||
{ whitespace = true, trailingCommas = false } = {},
|
||||
) {
|
||||
if (typeof jsonString !== "string") {
|
||||
throw new TypeError(
|
||||
`Expected argument \`jsonString\` to be a \`string\`, got \`${typeof jsonString}\``,
|
||||
);
|
||||
}
|
||||
|
||||
const strip = whitespace ? stripWithWhitespace : stripWithoutWhitespace;
|
||||
|
||||
let isInsideString = false;
|
||||
/** @type {false | typeof singleComment | typeof multiComment} */
|
||||
let isInsideComment = false;
|
||||
let offset = 0;
|
||||
let buffer = "";
|
||||
let result = "";
|
||||
let commaIndex = -1;
|
||||
|
||||
for (let index = 0; index < jsonString.length; index++) {
|
||||
const currentCharacter = jsonString[index];
|
||||
const nextCharacter = jsonString[index + 1];
|
||||
|
||||
if (!isInsideComment && currentCharacter === '"') {
|
||||
// Enter or exit string
|
||||
const escaped = isEscaped(jsonString, index);
|
||||
if (!escaped) {
|
||||
isInsideString = !isInsideString;
|
||||
}
|
||||
}
|
||||
|
||||
if (isInsideString) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isInsideComment && currentCharacter + nextCharacter === "//") {
|
||||
// Enter single-line comment
|
||||
buffer += jsonString.slice(offset, index);
|
||||
offset = index;
|
||||
isInsideComment = singleComment;
|
||||
index++;
|
||||
} else if (
|
||||
isInsideComment === singleComment &&
|
||||
currentCharacter + nextCharacter === "\r\n"
|
||||
) {
|
||||
// Exit single-line comment via \r\n
|
||||
index++;
|
||||
isInsideComment = false;
|
||||
buffer += strip(jsonString, offset, index);
|
||||
offset = index;
|
||||
continue;
|
||||
} else if (isInsideComment === singleComment && currentCharacter === "\n") {
|
||||
// Exit single-line comment via \n
|
||||
isInsideComment = false;
|
||||
buffer += strip(jsonString, offset, index);
|
||||
offset = index;
|
||||
} else if (!isInsideComment && currentCharacter + nextCharacter === "/*") {
|
||||
// Enter multiline comment
|
||||
buffer += jsonString.slice(offset, index);
|
||||
offset = index;
|
||||
isInsideComment = multiComment;
|
||||
index++;
|
||||
continue;
|
||||
} else if (
|
||||
isInsideComment === multiComment &&
|
||||
currentCharacter + nextCharacter === "*/"
|
||||
) {
|
||||
// Exit multiline comment
|
||||
index++;
|
||||
isInsideComment = false;
|
||||
buffer += strip(jsonString, offset, index + 1);
|
||||
offset = index + 1;
|
||||
continue;
|
||||
} else if (trailingCommas && !isInsideComment) {
|
||||
if (commaIndex !== -1) {
|
||||
if (currentCharacter === "}" || currentCharacter === "]") {
|
||||
// Strip trailing comma
|
||||
buffer += jsonString.slice(offset, index);
|
||||
result += strip(buffer, 0, 1) + buffer.slice(1);
|
||||
buffer = "";
|
||||
offset = index;
|
||||
commaIndex = -1;
|
||||
} else if (
|
||||
currentCharacter !== " " &&
|
||||
currentCharacter !== "\t" &&
|
||||
currentCharacter !== "\r" &&
|
||||
currentCharacter !== "\n"
|
||||
) {
|
||||
// Hit non-whitespace following a comma; comma is not trailing
|
||||
buffer += jsonString.slice(offset, index);
|
||||
offset = index;
|
||||
commaIndex = -1;
|
||||
}
|
||||
} else if (currentCharacter === ",") {
|
||||
// Flush buffer prior to this point, and save new comma index
|
||||
result += buffer + jsonString.slice(offset, index);
|
||||
buffer = "";
|
||||
offset = index;
|
||||
commaIndex = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const remaining =
|
||||
isInsideComment === singleComment
|
||||
? strip(jsonString, offset, jsonString.length)
|
||||
: jsonString.slice(offset);
|
||||
|
||||
return result + buffer + remaining;
|
||||
}
|
||||
|
||||
module.exports = stripJsonComments;
|
||||
14
web/node_modules/enhanced-resolve/lib/util/url-browser.js
generated
vendored
Normal file
14
web/node_modules/enhanced-resolve/lib/util/url-browser.js
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
MIT License http://www.opensource.org/licenses/mit-license.php
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
// Browser shim for the Node `url` builtin, providing the `fileURLToPath` the
|
||||
// resolver uses. Node, Deno and Bun use the native `url` (so they get the fully
|
||||
// correct implementation); only browser bundles use this, wired in through the
|
||||
// package `browser` field. The implementation is a URL-based port — see
|
||||
// ./fileURLToPath.
|
||||
module.exports = {
|
||||
fileURLToPath: require("./fileURLToPath"),
|
||||
};
|
||||
Reference in New Issue
Block a user