casper-context 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +253 -0
- package/dist/index.js +9 -0
- package/dist/lifecycle/post.js +239 -0
- package/dist/lifecycle/pre.js +113 -0
- package/dist/plugin.js +57 -0
- package/dist/transforms/autoContextTransform.js +1 -0
- package/dist/transforms/contextTransform.js +1 -0
- package/dist/transforms/stateTransform.js +1 -0
- package/dist/types/plugin.d.js +1 -0
- package/dist/utils/astHelpers.js +644 -0
- package/dist/utils/constants.js +111 -0
- package/dist/utils/names.js +1 -0
- package/dist/utils/scope.js +1 -0
- package/dist/utils/utilityHelpers.js +606 -0
- package/dist/visitors/AssignmentExpression.js +104 -0
- package/dist/visitors/CallExpression.js +1 -0
- package/dist/visitors/FunctionDeclaration.js +116 -0
- package/dist/visitors/Identifier.js +123 -0
- package/dist/visitors/JSXElement.js +1 -0
- package/dist/visitors/Program.js +278 -0
- package/dist/visitors/ReturnStatement.js +81 -0
- package/dist/visitors/VariableDeclaration.js +209 -0
- package/package.json +60 -0
- package/src/index.js +2 -0
- package/src/lifecycle/post.js +237 -0
- package/src/lifecycle/pre.js +103 -0
- package/src/plugin.js +51 -0
- package/src/transforms/autoContextTransform.js +0 -0
- package/src/transforms/contextTransform.js +0 -0
- package/src/transforms/stateTransform.js +0 -0
- package/src/types/plugin.d.ts +0 -0
- package/src/utils/astHelpers.js +767 -0
- package/src/utils/constants.js +102 -0
- package/src/utils/names.js +0 -0
- package/src/utils/scope.js +0 -0
- package/src/utils/utilityHelpers.js +636 -0
- package/src/visitors/AssignmentExpression.js +100 -0
- package/src/visitors/CallExpression.js +0 -0
- package/src/visitors/FunctionDeclaration.js +114 -0
- package/src/visitors/Identifier.js +142 -0
- package/src/visitors/JSXElement.js +0 -0
- package/src/visitors/Program.js +280 -0
- package/src/visitors/ReturnStatement.js +75 -0
- package/src/visitors/VariableDeclaration.js +216 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.functionDeclarationReturnStatementVisitor = functionDeclarationReturnStatementVisitor;
|
|
7
|
+
var _constants = require("../utils/constants");
|
|
8
|
+
var _astHelpers = require("../utils/astHelpers");
|
|
9
|
+
var _utilityHelpers = require("../utils/utilityHelpers");
|
|
10
|
+
/**
|
|
11
|
+
* @fileoverview Application Root & Provider Orchestration.
|
|
12
|
+
* This module manages the final wrapping phase of the AST transformation.
|
|
13
|
+
* It ensures that the transformed component tree is encapsulated within
|
|
14
|
+
* the custom Casper Context Provider, enabling global state distribution.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Core Logic Constants
|
|
19
|
+
* @description
|
|
20
|
+
* - _CCTX_CMP_NAME_PREFIX: The prefix used to identify and generate unique
|
|
21
|
+
* Context Provider instances (e.g., '_$_ctx_').
|
|
22
|
+
* - _CCTX_EMPTY: A safe fallback initializer for string-based AST properties.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* AST Transformation Helpers
|
|
27
|
+
* @description
|
|
28
|
+
* - buildCtxProvider: A structural helper that wraps a JSX element or
|
|
29
|
+
* Function body with a `<Context.Provider>` component, mapping internal
|
|
30
|
+
* state to the Provider's `value` prop.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @module Logger
|
|
35
|
+
* @description Provides a controlled logging interface for the Babel transformation process.
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @important
|
|
40
|
+
* The `buildCtxProvider` function is typically invoked during the `Program.exit`
|
|
41
|
+
* or `ExportDefaultDeclaration` phase to ensure all nested transformations
|
|
42
|
+
* are complete before the final Provider wrapping occurs.
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Visitor for `ReturnStatement` nodes inside a function declaration.
|
|
47
|
+
*
|
|
48
|
+
* This function inspects the return statement of a component function
|
|
49
|
+
* and wraps its returned JSX or value with the corresponding context provider.
|
|
50
|
+
* It ensures that the component correctly provides state and context to its children.
|
|
51
|
+
*
|
|
52
|
+
* @param {NodePath} path - The Babel AST path representing the `ReturnStatement` node.
|
|
53
|
+
* @param {Object} state - Plugin state, including import information and configuration.
|
|
54
|
+
* @param {Object} t - Babel types helper (`@babel/types`) used to generate AST nodes.
|
|
55
|
+
* @param {Object} entry - Registry entry for the component, containing `ctxName` and variable names.
|
|
56
|
+
*
|
|
57
|
+
* @returns {void}
|
|
58
|
+
* Modifies the return statement node in place, replacing it with a `React.createElement`
|
|
59
|
+
* call that wraps the original return value with a context provider.
|
|
60
|
+
*
|
|
61
|
+
* @important
|
|
62
|
+
* - Only processes return statements that have a non-null argument.
|
|
63
|
+
* - Uses `buildCtxProvider` to generate the provider wrapper.
|
|
64
|
+
* - Errors are silently caught; no changes occur if an exception is thrown.
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```js
|
|
68
|
+
* functionDeclarationReturnStatementVisitor(path, state, t, entry);
|
|
69
|
+
* // Wraps the return value of a component function with its context provider
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
function functionDeclarationReturnStatementVisitor(path, state, t, entry) {
|
|
73
|
+
try {
|
|
74
|
+
const returnNode = path.node.argument;
|
|
75
|
+
if (!returnNode) return;
|
|
76
|
+
const stateName = entry.ctxName.replace(_constants._CCTX_CMP_NAME_PREFIX, _constants._CCTX_EMPTY);
|
|
77
|
+
(0, _astHelpers.buildCtxProvider)(path, t, returnNode, state, stateName);
|
|
78
|
+
} catch (e) {
|
|
79
|
+
(0, _utilityHelpers.log)('error', '[::functionDeclarationReturnStatementVisitor::]', e.message);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = variableDeclarationVisitor;
|
|
7
|
+
exports.functionReturnVariableDelarationVisitor = functionReturnVariableDelarationVisitor;
|
|
8
|
+
var _constants = require("../utils/constants");
|
|
9
|
+
var _utilityHelpers = require("../utils/utilityHelpers");
|
|
10
|
+
var _astHelpers = require("../utils/astHelpers");
|
|
11
|
+
/**
|
|
12
|
+
* @fileoverview Variable Registration & Classification Logic.
|
|
13
|
+
* This module is responsible for the initial discovery phase of the transformation.
|
|
14
|
+
* It identifies custom context variables during their declaration, classifies their
|
|
15
|
+
* initial values, and registers them within the global tracking registry.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* AST Literal & Expression Constants
|
|
20
|
+
* @description These constants are used to evaluate the 'init' value of a variable.
|
|
21
|
+
* Your library uses these to distinguish between simple state and complex logic.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Registration & Lifecycle Utilities
|
|
26
|
+
* @description
|
|
27
|
+
* - isExcludeFile: Prevents tracking variables in ignored files or directories.
|
|
28
|
+
* - registerVariable: The core method that saves variable metadata to the virtualRegistry.
|
|
29
|
+
* - getFilePathHASH: Ensures variables are scoped to a unique file ID to prevent collisions.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Scope & Inheritance Helpers
|
|
34
|
+
* @description
|
|
35
|
+
* - getInheritantDecComponent: Traverses upward from a variable declaration to find
|
|
36
|
+
* the parent component name, ensuring the variable is correctly scoped to its owner.
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @module Logger
|
|
41
|
+
* @description Provides a controlled logging interface for the Babel transformation process.
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* @important
|
|
46
|
+
* **Classification Note:** When a variable is registered, its "Literal Type" determines
|
|
47
|
+
* how the subsequent `useState` hook is initialized. Non-literal initializers
|
|
48
|
+
* may require different handling during the `functionDeclarationExit` phase.
|
|
49
|
+
*/
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Extracts the initial value of a variable from a Babel AST `VariableDeclarator` node.
|
|
53
|
+
*
|
|
54
|
+
* This function inspects the `init` property of a variable declaration and
|
|
55
|
+
* attempts to determine its literal value. It handles primitive literals,
|
|
56
|
+
* arrays, and objects with literal values. Non-literal or complex expressions
|
|
57
|
+
* are flagged as `NON_LITERAL`.
|
|
58
|
+
*
|
|
59
|
+
* @param {NodePath} path - Babel AST path for a `VariableDeclarator` node.
|
|
60
|
+
*
|
|
61
|
+
* @returns {any} The initial value of the variable:
|
|
62
|
+
* - `number` for numeric literals
|
|
63
|
+
* - `string` for string literals
|
|
64
|
+
* - `boolean` for boolean literals
|
|
65
|
+
* - `Array` of literal values for array expressions
|
|
66
|
+
* - `Object` of literal key-value pairs for object expressions
|
|
67
|
+
* - `NON_LITERAL` for other expressions or complex initializers
|
|
68
|
+
* - `undefined` if the variable has no initializer
|
|
69
|
+
*
|
|
70
|
+
* @important
|
|
71
|
+
* - Only inspects direct literal values. Nested expressions inside arrays or objects
|
|
72
|
+
* that are not literals will be ignored or marked as `null`.
|
|
73
|
+
* - Errors during traversal or invalid AST nodes will return the current `initValue`.
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* ```js
|
|
77
|
+
* // For "const a = 5;" → returns 5
|
|
78
|
+
* // For "const b = [1, 2];" → returns [1, 2]
|
|
79
|
+
* // For "const c = { x: 1, y: 'a' };" → returns { x: 1, y: 'a' }
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
function getVariableInitValue(path) {
|
|
83
|
+
let initValue = undefined;
|
|
84
|
+
try {
|
|
85
|
+
if (path.node.init) {
|
|
86
|
+
const valNode = path.node.init;
|
|
87
|
+
if (valNode.type === _constants.NUMERIC_LITERAL || valNode.type === _constants.STRING_LITERAL || valNode.type === _constants.BOOLEAN_LITERAL) {
|
|
88
|
+
initValue = valNode.value;
|
|
89
|
+
} else if (valNode.type === _constants.ARRAY_EXPRESSION) {
|
|
90
|
+
initValue = valNode.elements.map(e => e.value ?? null);
|
|
91
|
+
} else if (valNode.type === _constants.OBJECT_EXPRESSION) {
|
|
92
|
+
const obj = {};
|
|
93
|
+
for (const prop of valNode.properties) {
|
|
94
|
+
if (prop.key && prop.value && prop.value.value !== undefined) {
|
|
95
|
+
obj[prop.key.name || prop.key.value] = prop.value.value;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
initValue = obj;
|
|
99
|
+
} else {
|
|
100
|
+
initValue = NON_LITERAL;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return initValue;
|
|
104
|
+
} catch (e) {
|
|
105
|
+
return initValue;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Visitor for `VariableDeclarator` nodes to register state variables in the virtual registry.
|
|
111
|
+
*
|
|
112
|
+
* This function checks if a variable's name matches the plugin's configured prefix.
|
|
113
|
+
* If so, it extracts the initial value, identifies the enclosing component, and
|
|
114
|
+
* registers the variable in the `virtualRegistry`. It also ensures React and global
|
|
115
|
+
* context imports are flagged if needed.
|
|
116
|
+
*
|
|
117
|
+
* @param {NodePath} path - Babel AST path for a `VariableDeclarator` node.
|
|
118
|
+
* @param {Object} state - Plugin state, including filename, import metadata, and configuration flags.
|
|
119
|
+
* @param {Object} t - Babel types helper (`@babel/types`) used to inspect or create AST nodes.
|
|
120
|
+
* @param {Object<string, Object>} virtualRegistry - Registry of components and their registered variables.
|
|
121
|
+
*
|
|
122
|
+
* @returns {void}
|
|
123
|
+
* - Registers the variable in the `virtualRegistry` under the component hash.
|
|
124
|
+
* - Sets `state.needsGblContext` if the variable belongs to a global context.
|
|
125
|
+
* - Flags `state.needUseStateImport` if `useState` is not yet imported.
|
|
126
|
+
*
|
|
127
|
+
* @important
|
|
128
|
+
* - Only processes variables whose names start with the configured prefix.
|
|
129
|
+
* - Uses `getVariableInitValue` to determine the variable's initial value.
|
|
130
|
+
* - Errors are silently caught; no action is taken if an exception occurs.
|
|
131
|
+
*
|
|
132
|
+
* @example
|
|
133
|
+
* ```js
|
|
134
|
+
* variableDeclarationVisitor(path, state, t, virtualRegistry);
|
|
135
|
+
* // Registers a prefixed variable in the virtual registry with its initial value
|
|
136
|
+
* ```
|
|
137
|
+
*/
|
|
138
|
+
function variableDeclarationVisitor(path, state, t, virtualRegistry) {
|
|
139
|
+
try {
|
|
140
|
+
const fileName = state.filename || _constants._CCTX_EMPTY;
|
|
141
|
+
if (!(0, _utilityHelpers.isExcludeFile)(fileName, this.opts)) return;
|
|
142
|
+
if (path.node.id.name?.startsWith(state.casperConfig.prefix)) {
|
|
143
|
+
const inheritantCMP = (0, _astHelpers.getInheritantDecComponent)(path);
|
|
144
|
+
if (!inheritantCMP) return;
|
|
145
|
+
const filePathHash = (0, _utilityHelpers.getFilePathHASH)(fileName);
|
|
146
|
+
let _init_value = getVariableInitValue(path);
|
|
147
|
+
state.needsGblContext = true;
|
|
148
|
+
if (!state.importState.reactId && !state.importState.useStateId) {
|
|
149
|
+
state.needUseStateImport = true;
|
|
150
|
+
}
|
|
151
|
+
(0, _utilityHelpers.registerVariable)(`${inheritantCMP}_${filePathHash}`, path.node.id.name, _init_value, virtualRegistry);
|
|
152
|
+
}
|
|
153
|
+
} catch (e) {
|
|
154
|
+
(0, _utilityHelpers.log)('error', '[::variableDeclarationVisitor::]', e.message);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Visitor for `VariableDeclarator` nodes inside a function's body to collect state variables.
|
|
160
|
+
*
|
|
161
|
+
* This function inspects variable declarations within a component function and collects
|
|
162
|
+
* those whose names start with the plugin's configured prefix. Collected variables are
|
|
163
|
+
* added to `localStateVars` for later use in context or state initialization.
|
|
164
|
+
* The original declaration line is removed from the AST to prevent duplication.
|
|
165
|
+
*
|
|
166
|
+
* @param {NodePath} path - Babel AST path for a `VariableDeclarator` node.
|
|
167
|
+
* @param {Object} state - Plugin state, containing the configuration and flags.
|
|
168
|
+
* @param {Object} t - Babel types helper (`@babel/types`) used to inspect and manipulate AST nodes.
|
|
169
|
+
* @param {Array<Object>} localStateVars - Array to collect state variables with `{ name, init }`.
|
|
170
|
+
*
|
|
171
|
+
* @returns {void}
|
|
172
|
+
* - Pushes matched variables into `localStateVars`.
|
|
173
|
+
* - Removes the original variable declaration or the specific declarator from the AST.
|
|
174
|
+
*
|
|
175
|
+
* @important
|
|
176
|
+
* - Only processes identifiers whose names match the configured prefix.
|
|
177
|
+
* - Safely handles declarations with multiple declarators by removing only the matched one.
|
|
178
|
+
* - Errors are silently caught; no action occurs if an exception is thrown.
|
|
179
|
+
*
|
|
180
|
+
* @example
|
|
181
|
+
* ```js
|
|
182
|
+
* const localStateVars = [];
|
|
183
|
+
* functionReturnVariableDelarationVisitor(path, state, t, localStateVars);
|
|
184
|
+
* // localStateVars now contains the prefixed variables from the function
|
|
185
|
+
* ```
|
|
186
|
+
*/
|
|
187
|
+
function functionReturnVariableDelarationVisitor(path, state, t, localStateVars) {
|
|
188
|
+
try {
|
|
189
|
+
const id = path.node.id;
|
|
190
|
+
const init = path.node.init;
|
|
191
|
+
if (!t.isIdentifier(id)) return;
|
|
192
|
+
if (id.name.startsWith(state.casperConfig.prefix)) {
|
|
193
|
+
localStateVars.push({
|
|
194
|
+
name: id.name,
|
|
195
|
+
init
|
|
196
|
+
});
|
|
197
|
+
// remove declaration line
|
|
198
|
+
if (t.isVariableDeclaration(path.parent)) {
|
|
199
|
+
if (path.parent.declarations.length === 1) {
|
|
200
|
+
path.parentPath.remove();
|
|
201
|
+
} else {
|
|
202
|
+
path.remove();
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
} catch (e) {
|
|
207
|
+
(0, _utilityHelpers.log)('error', '[::functionReturnVariableDelarationVisitor::]', e.message);
|
|
208
|
+
}
|
|
209
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "casper-context",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CasperContext - the friendly, invisible React Context API via Babel. Declare a variable, use it globally.",
|
|
5
|
+
"author": "Ruwan Chamara",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"main": "dist/index.js",
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"src"
|
|
12
|
+
],
|
|
13
|
+
"keywords": [
|
|
14
|
+
"babel-plugin",
|
|
15
|
+
"babel",
|
|
16
|
+
"react",
|
|
17
|
+
"react-context",
|
|
18
|
+
"global-state",
|
|
19
|
+
"casper-context",
|
|
20
|
+
"casper",
|
|
21
|
+
"react-casper-context",
|
|
22
|
+
"react-casper",
|
|
23
|
+
"compiler",
|
|
24
|
+
"ast",
|
|
25
|
+
"transform",
|
|
26
|
+
"state-management"
|
|
27
|
+
],
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "https://github.com/yourname/casper-context.git"
|
|
31
|
+
},
|
|
32
|
+
"bugs": {
|
|
33
|
+
"url": "https://github.com/yourname/casper-context/issues"
|
|
34
|
+
},
|
|
35
|
+
"homepage": "https://github.com/yourname/casper-context#readme",
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "babel src --out-dir dist --extensions .ts,.js",
|
|
38
|
+
"dev": "babel src --out-dir dist --extensions .ts,.js --watch",
|
|
39
|
+
"clean": "rm -rf dist",
|
|
40
|
+
"test": "node tests/run.js",
|
|
41
|
+
"lint": "eslint src --ext .ts,.js",
|
|
42
|
+
"prepublishOnly": "npm run clean && npm run build"
|
|
43
|
+
},
|
|
44
|
+
"peerDependencies": {
|
|
45
|
+
"@babel/core": "^7.0.0"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@babel/cli": "^7.25.0",
|
|
49
|
+
"@babel/core": "^7.25.0",
|
|
50
|
+
"@babel/parser": "^7.25.0",
|
|
51
|
+
"@babel/preset-env": "^7.25.0",
|
|
52
|
+
"@babel/preset-typescript": "^7.25.0",
|
|
53
|
+
"@babel/traverse": "^7.25.0",
|
|
54
|
+
"@babel/types": "^7.25.0",
|
|
55
|
+
"@typescript-eslint/eslint-plugin": "^7.0.0",
|
|
56
|
+
"@typescript-eslint/parser": "^7.0.0",
|
|
57
|
+
"eslint": "^9.0.0",
|
|
58
|
+
"typescript": "^5.4.0"
|
|
59
|
+
}
|
|
60
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Environment Initialization and File Orchestration.
|
|
3
|
+
* This module is responsible for the physical creation and synchronization of
|
|
4
|
+
* the Casper Context infrastructure, including the global context bridge and
|
|
5
|
+
* ESLint configuration overrides.
|
|
6
|
+
*/
|
|
7
|
+
import fs from 'fs';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Path Utilities & Constants
|
|
12
|
+
* @description Leverages resolved system paths and core constants to ensure
|
|
13
|
+
* write operations target the correct project directories.
|
|
14
|
+
*/
|
|
15
|
+
import { CONTEXT_FILE_PATH, ESLINT_GLOBAL_JS_PATH } from '../utils/utilityHelpers';
|
|
16
|
+
|
|
17
|
+
import { _CCTX_CMP_NAME_PREFIX, _CCTX_EMPTY, _CCTX_UNDEFINED, UNICODE_UTF8, CASPER_READ_ONLY_TYPE, ESLINT_RC_FILE } from '../utils/constants';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @module Logger
|
|
21
|
+
* @description Provides a controlled logging interface for the Babel transformation process.
|
|
22
|
+
*/
|
|
23
|
+
import { log } from '../utils/utilityHelpers';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @important
|
|
27
|
+
* The following imports are critical for the library's lifecycle:
|
|
28
|
+
* - `CONTEXT_FILE_PATH`: The destination for the auto-generated React Context logic.
|
|
29
|
+
* - `ESLINT_GLOBAL_JS_PATH`: The file that prevents 'no-undef' errors for _$_ variables.
|
|
30
|
+
* - `UNICODE_UTF8`: Standard encoding used for all `fs.writeFileSync` operations.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Generates a JavaScript module file that exports React context instances
|
|
35
|
+
* based on the registered variables in the `virtualRegistry`.
|
|
36
|
+
*
|
|
37
|
+
* This function creates or overwrites the file at `CONTEXT_FILE_PATH`, injecting
|
|
38
|
+
* `'use strict'` directives, ES module compatibility boilerplate, and context
|
|
39
|
+
* initialization code. Each registered component context is turned into a
|
|
40
|
+
* `React.createContext` with default values derived from the registry.
|
|
41
|
+
*
|
|
42
|
+
* @param {Object<string, Object>} virtualRegistry - The in-memory registry mapping
|
|
43
|
+
* component hashes to their context metadata:
|
|
44
|
+
* ```js
|
|
45
|
+
* {
|
|
46
|
+
* [componentHash]: {
|
|
47
|
+
* ctxName: string, // Generated context name
|
|
48
|
+
* varNames: string[], // List of variable names registered for this context
|
|
49
|
+
* defaults: Record<string, any> // Default values for each variable
|
|
50
|
+
* }
|
|
51
|
+
* }
|
|
52
|
+
* ```
|
|
53
|
+
*
|
|
54
|
+
* @returns {void}
|
|
55
|
+
* This function writes to the filesystem directly and does not return a value.
|
|
56
|
+
*
|
|
57
|
+
* @important
|
|
58
|
+
* - The generated module follows CommonJS export style with ES module compatibility.
|
|
59
|
+
* - All variables registered in `virtualRegistry` are included in their respective
|
|
60
|
+
* `createContext` objects. Variables without a default value are set to `_CCTX_UNDEFINED`.
|
|
61
|
+
* - The function overwrites any existing file at `CONTEXT_FILE_PATH`.
|
|
62
|
+
* - React is imported as `_react` and used for `createContext` calls.
|
|
63
|
+
* - The `_CCTX_CMP_NAME_PREFIX` is stripped from exported context names for clarity.
|
|
64
|
+
* - Errors are silently caught; consider adding logging for debugging or dev builds.
|
|
65
|
+
* - The generated content is formatted with line breaks and indentation for readability.
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* ```js
|
|
69
|
+
* const virtualRegistry = {
|
|
70
|
+
* 'abc123': {
|
|
71
|
+
* ctxName: '_CCTX_abc123',
|
|
72
|
+
* varNames: ['count', 'enabled'],
|
|
73
|
+
* defaults: { count: 0, enabled: true }
|
|
74
|
+
* }
|
|
75
|
+
* };
|
|
76
|
+
* generateContextContent(virtualRegistry);
|
|
77
|
+
* // Produces a file exporting a React context with default { count: 0, enabled: true }
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
80
|
+
function generateContextContent(virtualRegistry) {
|
|
81
|
+
try {
|
|
82
|
+
fs.writeFileSync(CONTEXT_FILE_PATH, _CCTX_EMPTY, UNICODE_UTF8);
|
|
83
|
+
let contextNames = [];
|
|
84
|
+
let content = `'use strict';\n\nObject.defineProperty(exports, '__esModule', {\n value: true\n});\n`;
|
|
85
|
+
if (Object.keys(virtualRegistry).length === 0) return
|
|
86
|
+
Object.values(virtualRegistry).forEach(entry => {
|
|
87
|
+
if (!entry.ctxName) return;
|
|
88
|
+
contextNames.push(entry.ctxName.replace(_CCTX_CMP_NAME_PREFIX, _CCTX_EMPTY));
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
content += `exports.${contextNames.join(' = exports.')} = void 0;\n`;
|
|
92
|
+
content += `var _react = require('react');\n`;
|
|
93
|
+
|
|
94
|
+
Object.values(virtualRegistry).forEach(entry => {
|
|
95
|
+
if (!entry.ctxName) return;
|
|
96
|
+
const { ctxName, varNames, defaults } = entry;
|
|
97
|
+
const defaultObjProps = varNames.map(name => {
|
|
98
|
+
const val = defaults[name] !== undefined ? JSON.stringify(defaults[name]) : _CCTX_UNDEFINED;
|
|
99
|
+
return ` ${name}: ${val}`;
|
|
100
|
+
}).join(',\n');
|
|
101
|
+
content += `const ${ctxName.replace(_CCTX_CMP_NAME_PREFIX, _CCTX_EMPTY)} = exports.${ctxName.replace(_CCTX_CMP_NAME_PREFIX, _CCTX_EMPTY)} = /*#__PURE__*/(0, _react.createContext)({\n${defaultObjProps}\n});\n`;
|
|
102
|
+
});
|
|
103
|
+
fs.writeFileSync(CONTEXT_FILE_PATH, content, UNICODE_UTF8);
|
|
104
|
+
} catch (e) {
|
|
105
|
+
log('error', '[::generateContextContent::]', e.message);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Generates or updates the ESLint global variables configuration file
|
|
111
|
+
* based on the current virtual registry.
|
|
112
|
+
*
|
|
113
|
+
* This function reads all variable names registered in `virtualRegistry`
|
|
114
|
+
* and writes them as read-only globals in a JavaScript module (`ESLINT_GLOBAL_JS_PATH`).
|
|
115
|
+
* It compares the newly generated content with the existing file and only
|
|
116
|
+
* overwrites if changes are detected. Finally, it updates the modification
|
|
117
|
+
* timestamp of the ESLint configuration file to trigger linting updates if needed.
|
|
118
|
+
*
|
|
119
|
+
* @param {Object<string, Object>} virtualRegistry - The in-memory registry of component
|
|
120
|
+
* variables. Each entry should contain:
|
|
121
|
+
* ```js
|
|
122
|
+
* {
|
|
123
|
+
* varNames: string[], // List of variable names for the component/context
|
|
124
|
+
* ctxName?: string, // Optional context name
|
|
125
|
+
* defaults?: Record<string, any> // Optional default values
|
|
126
|
+
* }
|
|
127
|
+
* ```
|
|
128
|
+
* @param {Object<string, Object>} [prevVirtualRegistrySnap] - Optional snapshot of
|
|
129
|
+
* the previous virtual registry state. Currently unused, but can be used
|
|
130
|
+
* to detect changes or optimize writes.
|
|
131
|
+
*
|
|
132
|
+
* @returns {void}
|
|
133
|
+
* Writes or updates the ESLint global JS file on disk and updates the
|
|
134
|
+
* corresponding ESLint configuration file's timestamps.
|
|
135
|
+
*
|
|
136
|
+
* @important
|
|
137
|
+
* - All variables are marked as read-only in the ESLint globals (`CASPER_READ_ONLY_TYPE`).
|
|
138
|
+
* - File writes are **atomic**: the target file is cleared before writing new content.
|
|
139
|
+
* - If no variables exist in the registry, the function exits early without writing.
|
|
140
|
+
* - Errors are silently caught; consider adding logging for development.
|
|
141
|
+
* - Changes are only applied if the content differs from the current file, avoiding
|
|
142
|
+
* unnecessary filesystem writes.
|
|
143
|
+
* - The file starts with a comment indicating it is auto-generated by CasperContext.
|
|
144
|
+
*
|
|
145
|
+
* @example
|
|
146
|
+
* ```js
|
|
147
|
+
* const virtualRegistry = {
|
|
148
|
+
* 'abc123': {
|
|
149
|
+
* varNames: ['count', 'enabled'],
|
|
150
|
+
* ctxName: '_CCTX_abc123',
|
|
151
|
+
* defaults: { count: 0, enabled: true }
|
|
152
|
+
* }
|
|
153
|
+
* };
|
|
154
|
+
* generateLintGlobalJS(virtualRegistry);
|
|
155
|
+
* // Writes ESLint globals file:
|
|
156
|
+
* // module.exports = {
|
|
157
|
+
* // "count": "readonly",
|
|
158
|
+
* // "enabled": "readonly"
|
|
159
|
+
* // };
|
|
160
|
+
* ```
|
|
161
|
+
*/
|
|
162
|
+
function generateLintGlobalJS(virtualRegistry, prevVirtualRegistrySnap) {
|
|
163
|
+
try {
|
|
164
|
+
if (Object.keys(virtualRegistry).length === 0) return
|
|
165
|
+
const currentCnt = fs.readFileSync(ESLINT_GLOBAL_JS_PATH, UNICODE_UTF8);
|
|
166
|
+
const content = Object.values(virtualRegistry)
|
|
167
|
+
.flatMap(entry => entry.varNames || [])
|
|
168
|
+
.map(name => ` "${name}": "${CASPER_READ_ONLY_TYPE}"`)
|
|
169
|
+
.join(',\n');
|
|
170
|
+
const fileContent = `// ****** generated by CasperContext ******
|
|
171
|
+
module.exports = {\n${content}\n};\n`;
|
|
172
|
+
if (currentCnt !== fileContent) {
|
|
173
|
+
fs.writeFileSync(ESLINT_GLOBAL_JS_PATH, _CCTX_EMPTY, UNICODE_UTF8);
|
|
174
|
+
fs.writeFileSync(ESLINT_GLOBAL_JS_PATH, fileContent, UNICODE_UTF8);
|
|
175
|
+
fs.utimesSync(path.join(process.cwd(), ESLINT_RC_FILE), new Date(), new Date());
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
} catch (e) {
|
|
179
|
+
log('error', '[::generateLintGlobalJS::]', e.message);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Performs post-processing steps after AST transformations on a file.
|
|
186
|
+
*
|
|
187
|
+
* This function generates or updates auxiliary files based on the current
|
|
188
|
+
* state of the virtual registry. Specifically:
|
|
189
|
+
* 1. `generateContextContent` – creates the module exporting React context
|
|
190
|
+
* instances with default values.
|
|
191
|
+
* 2. `generateLintGlobalJS` – updates the ESLint global variables file
|
|
192
|
+
* with read-only entries for all registered variables.
|
|
193
|
+
*
|
|
194
|
+
* @param {string} file - The path of the file that was processed. Currently unused,
|
|
195
|
+
* but provided for future enhancements or logging purposes.
|
|
196
|
+
* @param {Object} t - Babel types helper (`@babel/types`) used in AST operations.
|
|
197
|
+
* @param {Object<string, Object>} virtualRegistry - The in-memory registry
|
|
198
|
+
* of component/context variables, structured as:
|
|
199
|
+
* ```js
|
|
200
|
+
* {
|
|
201
|
+
* [componentHash]: {
|
|
202
|
+
* ctxName: string,
|
|
203
|
+
* varNames: string[],
|
|
204
|
+
* defaults: Record<string, any>
|
|
205
|
+
* }
|
|
206
|
+
* }
|
|
207
|
+
* ```
|
|
208
|
+
* @param {Object<string, Object>} [prevVirtualRegistrySnap] - Optional snapshot
|
|
209
|
+
* of the previous virtual registry state, used to detect changes and optimize writes.
|
|
210
|
+
*
|
|
211
|
+
* @returns {void}
|
|
212
|
+
* This function does not return a value; its effect is to update filesystem
|
|
213
|
+
* files (`CONTEXT_FILE_PATH` and `ESLINT_GLOBAL_JS_PATH`) based on the registry.
|
|
214
|
+
*
|
|
215
|
+
* @important
|
|
216
|
+
* - Errors are silently caught; consider adding logging for debugging.
|
|
217
|
+
* - Both `generateContextContent` and `generateLintGlobalJS` may overwrite
|
|
218
|
+
* existing files.
|
|
219
|
+
* - The `file` parameter is not currently used internally but is included
|
|
220
|
+
* for potential future processing logic.
|
|
221
|
+
*
|
|
222
|
+
* @example
|
|
223
|
+
* ```js
|
|
224
|
+
* import postProcess from './postProcess';
|
|
225
|
+
*
|
|
226
|
+
* postProcess('src/App.js', t, virtualRegistry, prevVirtualRegistrySnap);
|
|
227
|
+
* // Generates context and ESLint globals files based on the current registry
|
|
228
|
+
* ```
|
|
229
|
+
*/
|
|
230
|
+
export default function postProcess(file, t, virtualRegistry, prevVirtualRegistrySnap) {
|
|
231
|
+
try {
|
|
232
|
+
generateContextContent(virtualRegistry)
|
|
233
|
+
generateLintGlobalJS(virtualRegistry, prevVirtualRegistrySnap)
|
|
234
|
+
} catch (e) {
|
|
235
|
+
log('error', '[::postProcess::]', e.message)
|
|
236
|
+
}
|
|
237
|
+
}
|