Implement JQHTML function cache ID system and fix bundle compilation Implement underscore prefix for system tables Fix JS syntax linter to support decorators and grant exception to Task system SPA: Update planning docs and wishlists with remaining features SPA: Document Navigation API abandonment and future enhancements Implement SPA browser integration with History API (Phase 1) Convert contacts view page to SPA action Convert clients pages to SPA actions and document conversion procedure SPA: Merge GET parameters and update documentation Implement SPA route URL generation in JavaScript and PHP Implement SPA bootstrap controller architecture Add SPA routing manual page (rsx:man spa) Add SPA routing documentation to CLAUDE.md Phase 4 Complete: Client-side SPA routing implementation Update get_routes() consumers for unified route structure Complete SPA Phase 3: PHP-side route type detection and is_spa flag Restore unified routes structure and Manifest_Query class Refactor route indexing and add SPA infrastructure Phase 3 Complete: SPA route registration in manifest Implement SPA Phase 2: Extract router code and test decorators Rename Jqhtml_Component to Component and complete SPA foundation setup 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
200 lines
5.2 KiB
JavaScript
200 lines
5.2 KiB
JavaScript
/*
|
|
MIT License http://www.opensource.org/licenses/mit-license.php
|
|
Author Tobias Koppers @sokra
|
|
*/
|
|
|
|
"use strict";
|
|
|
|
/** @typedef {import("./Dependency")} Dependency */
|
|
/** @typedef {import("./Dependency").GetConditionFn} GetConditionFn */
|
|
/** @typedef {import("./Module")} Module */
|
|
/** @typedef {import("./util/runtime").RuntimeSpec} RuntimeSpec */
|
|
|
|
/**
|
|
* Module itself is not connected, but transitive modules are connected transitively.
|
|
*/
|
|
const TRANSITIVE_ONLY = Symbol("transitive only");
|
|
|
|
/**
|
|
* While determining the active state, this flag is used to signal a circular connection.
|
|
*/
|
|
const CIRCULAR_CONNECTION = Symbol("circular connection");
|
|
|
|
/** @typedef {boolean | typeof TRANSITIVE_ONLY | typeof CIRCULAR_CONNECTION} ConnectionState */
|
|
|
|
/**
|
|
* @param {ConnectionState} a first
|
|
* @param {ConnectionState} b second
|
|
* @returns {ConnectionState} merged
|
|
*/
|
|
const addConnectionStates = (a, b) => {
|
|
if (a === true || b === true) return true;
|
|
if (a === false) return b;
|
|
if (b === false) return a;
|
|
if (a === TRANSITIVE_ONLY) return b;
|
|
if (b === TRANSITIVE_ONLY) return a;
|
|
return a;
|
|
};
|
|
|
|
/**
|
|
* @param {ConnectionState} a first
|
|
* @param {ConnectionState} b second
|
|
* @returns {ConnectionState} intersected
|
|
*/
|
|
const intersectConnectionStates = (a, b) => {
|
|
if (a === false || b === false) return false;
|
|
if (a === true) return b;
|
|
if (b === true) return a;
|
|
if (a === CIRCULAR_CONNECTION) return b;
|
|
if (b === CIRCULAR_CONNECTION) return a;
|
|
return a;
|
|
};
|
|
|
|
class ModuleGraphConnection {
|
|
/**
|
|
* @param {Module|null} originModule the referencing module
|
|
* @param {Dependency|null} dependency the referencing dependency
|
|
* @param {Module} module the referenced module
|
|
* @param {string=} explanation some extra detail
|
|
* @param {boolean=} weak the reference is weak
|
|
* @param {false | null | GetConditionFn | undefined} condition condition for the connection
|
|
*/
|
|
constructor(
|
|
originModule,
|
|
dependency,
|
|
module,
|
|
explanation,
|
|
weak = false,
|
|
condition = undefined
|
|
) {
|
|
this.originModule = originModule;
|
|
this.resolvedOriginModule = originModule;
|
|
this.dependency = dependency;
|
|
this.resolvedModule = module;
|
|
this.module = module;
|
|
this.weak = weak;
|
|
this.conditional = Boolean(condition);
|
|
this._active = condition !== false;
|
|
this.condition = condition || undefined;
|
|
/** @type {Set<string> | undefined} */
|
|
this.explanations = undefined;
|
|
if (explanation) {
|
|
this.explanations = new Set();
|
|
this.explanations.add(explanation);
|
|
}
|
|
}
|
|
|
|
clone() {
|
|
const clone = new ModuleGraphConnection(
|
|
this.resolvedOriginModule,
|
|
this.dependency,
|
|
this.resolvedModule,
|
|
undefined,
|
|
this.weak,
|
|
this.condition
|
|
);
|
|
clone.originModule = this.originModule;
|
|
clone.module = this.module;
|
|
clone.conditional = this.conditional;
|
|
clone._active = this._active;
|
|
if (this.explanations) clone.explanations = new Set(this.explanations);
|
|
return clone;
|
|
}
|
|
|
|
/**
|
|
* @param {GetConditionFn} condition condition for the connection
|
|
* @returns {void}
|
|
*/
|
|
addCondition(condition) {
|
|
if (this.conditional) {
|
|
const old =
|
|
/** @type {GetConditionFn} */
|
|
(this.condition);
|
|
/** @type {GetConditionFn} */
|
|
(this.condition) = (c, r) =>
|
|
intersectConnectionStates(old(c, r), condition(c, r));
|
|
} else if (this._active) {
|
|
this.conditional = true;
|
|
this.condition = condition;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {string} explanation the explanation to add
|
|
* @returns {void}
|
|
*/
|
|
addExplanation(explanation) {
|
|
if (this.explanations === undefined) {
|
|
this.explanations = new Set();
|
|
}
|
|
this.explanations.add(explanation);
|
|
}
|
|
|
|
get explanation() {
|
|
if (this.explanations === undefined) return "";
|
|
return [...this.explanations].join(" ");
|
|
}
|
|
|
|
/**
|
|
* @param {RuntimeSpec} runtime the runtime
|
|
* @returns {boolean} true, if the connection is active
|
|
*/
|
|
isActive(runtime) {
|
|
if (!this.conditional) return this._active;
|
|
|
|
return (
|
|
/** @type {GetConditionFn} */ (this.condition)(this, runtime) !== false
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @param {RuntimeSpec} runtime the runtime
|
|
* @returns {boolean} true, if the connection is active
|
|
*/
|
|
isTargetActive(runtime) {
|
|
if (!this.conditional) return this._active;
|
|
return (
|
|
/** @type {GetConditionFn} */ (this.condition)(this, runtime) === true
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @param {RuntimeSpec} runtime the runtime
|
|
* @returns {ConnectionState} true: fully active, false: inactive, TRANSITIVE: direct module inactive, but transitive connection maybe active
|
|
*/
|
|
getActiveState(runtime) {
|
|
if (!this.conditional) return this._active;
|
|
return /** @type {GetConditionFn} */ (this.condition)(this, runtime);
|
|
}
|
|
|
|
/**
|
|
* @param {boolean} value active or not
|
|
* @returns {void}
|
|
*/
|
|
setActive(value) {
|
|
this.conditional = false;
|
|
this._active = value;
|
|
}
|
|
|
|
// TODO webpack 5 remove
|
|
get active() {
|
|
throw new Error("Use getActiveState instead");
|
|
}
|
|
|
|
set active(value) {
|
|
throw new Error("Use setActive instead");
|
|
}
|
|
}
|
|
|
|
/** @typedef {typeof TRANSITIVE_ONLY} TRANSITIVE_ONLY */
|
|
/** @typedef {typeof CIRCULAR_CONNECTION} CIRCULAR_CONNECTION */
|
|
|
|
module.exports = ModuleGraphConnection;
|
|
module.exports.CIRCULAR_CONNECTION = /** @type {typeof CIRCULAR_CONNECTION} */ (
|
|
CIRCULAR_CONNECTION
|
|
);
|
|
module.exports.TRANSITIVE_ONLY = /** @type {typeof TRANSITIVE_ONLY} */ (
|
|
TRANSITIVE_ONLY
|
|
);
|
|
module.exports.addConnectionStates = addConnectionStates;
|