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,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Transformation Logic & Node Orchestration.
|
|
3
|
+
* This module integrates AST helpers with utility validators to perform
|
|
4
|
+
* the actual replacement of custom global variables (`_$_`) with
|
|
5
|
+
* React-compatible State and Context setters.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Core Constants
|
|
10
|
+
* @description
|
|
11
|
+
* - IDENTIFIER: Used to validate if the left-hand side of an assignment is a variable.
|
|
12
|
+
* - _CCTX_EMPTY: Fallback value for filenames or uninitialized state strings.
|
|
13
|
+
*/
|
|
14
|
+
import { IDENTIFIER, _CCTX_EMPTY } from '../utils/constants';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Utility & Validation Helpers
|
|
18
|
+
* @description
|
|
19
|
+
* - findContextByVar: Maps a `_$_` variable to its corresponding Context instance name.
|
|
20
|
+
* - isExcludeFile: Security/Performance gate to prevent processing ignored files.
|
|
21
|
+
*/
|
|
22
|
+
import { findContextByVar, isExcludeFile } from '../utils/utilityHelpers';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* AST Transformation Utilities
|
|
26
|
+
* @description The functional "engine" of the plugin that manipulates the Babel tree.
|
|
27
|
+
* - buildSpreadObject: Creates the updated state object for setters.
|
|
28
|
+
* - replaceWithSetState: Handles local component state updates.
|
|
29
|
+
* - buildUseContextInstance: Injects `useContext` hooks when variables are cross-component.
|
|
30
|
+
* - replaceWithContextSetState: Handles global/context-based state updates.
|
|
31
|
+
* - getComponentName: Identifies the immediate parent function.
|
|
32
|
+
* - getRootParentComponent: Recursively climbs the tree to find the top-level React Component.
|
|
33
|
+
*/
|
|
34
|
+
import { buildSpreadObject, replaceWithSetState, buildUseContextInstance, replaceWithContextSetState, getComponentName, getRootParentComponent } from '../utils/astHelpers';
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @module Logger
|
|
38
|
+
* @description Provides a controlled logging interface for the Babel transformation process.
|
|
39
|
+
*/
|
|
40
|
+
import { log } from '../utils/utilityHelpers';
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Babel visitor function for handling assignment expressions in the AST.
|
|
44
|
+
*
|
|
45
|
+
* This visitor inspects assignments in the code and replaces or augments them
|
|
46
|
+
* with state/context updates for variables registered in the `virtualRegistry`.
|
|
47
|
+
* It handles both direct component state updates and global/shared context updates.
|
|
48
|
+
*
|
|
49
|
+
* @param {NodePath} path - The Babel AST path representing the current node.
|
|
50
|
+
* @param {Object} state - Plugin state, including file info, config, and import metadata.
|
|
51
|
+
* @param {Object} t - Babel types helper (`@babel/types`) used to generate AST nodes.
|
|
52
|
+
* @param {Object<string, Object>} virtualRegistry - Registry of components and their
|
|
53
|
+
* registered variables/context info.
|
|
54
|
+
*
|
|
55
|
+
* @returns {void}
|
|
56
|
+
* Updates AST nodes in place and sets plugin state flags; no return value.
|
|
57
|
+
*
|
|
58
|
+
* @important
|
|
59
|
+
* - Only handles assignments where the left-hand identifier starts with the configured prefix.
|
|
60
|
+
* - Supports both direct component `useState` updates and context-based updates.
|
|
61
|
+
* - Automatically marks that a global context is needed (`state.needsGblContext = true`).
|
|
62
|
+
* - Injects `useState` import if missing.
|
|
63
|
+
* - Silent error handling; consider logging `e` for debugging.
|
|
64
|
+
*/
|
|
65
|
+
export default function assignmentExpressionVisitor(path, state, t, virtualRegistry) {
|
|
66
|
+
try {
|
|
67
|
+
const fileName = state.filename || _CCTX_EMPTY;
|
|
68
|
+
if (!isExcludeFile(fileName, this.opts)) return;
|
|
69
|
+
if (path.node.left.type === IDENTIFIER) {
|
|
70
|
+
if (path.node.left.name?.startsWith(state.casperConfig.prefix)) {
|
|
71
|
+
state.needsGblContext = true;
|
|
72
|
+
const ctxName = findContextByVar(path.node.left.name, virtualRegistry);
|
|
73
|
+
if (!ctxName) return;
|
|
74
|
+
let { currentFuncParent, componentName } = getRootParentComponent(path);
|
|
75
|
+
if (!componentName) return;
|
|
76
|
+
let isSameCMP = false;
|
|
77
|
+
if (ctxName.startsWith(componentName)) {
|
|
78
|
+
isSameCMP = true;
|
|
79
|
+
}
|
|
80
|
+
state.needsGblContext = true;
|
|
81
|
+
if (
|
|
82
|
+
!state.importState.reactId &&
|
|
83
|
+
!state.importState.useStateId
|
|
84
|
+
) {
|
|
85
|
+
state.needUseStateImport = true
|
|
86
|
+
}
|
|
87
|
+
const updateFunction = buildSpreadObject(path, t);
|
|
88
|
+
if (isSameCMP) {
|
|
89
|
+
replaceWithSetState(path, t, ctxName, updateFunction);
|
|
90
|
+
} else {
|
|
91
|
+
buildUseContextInstance(currentFuncParent, state, t, ctxName);
|
|
92
|
+
replaceWithContextSetState(path, t, ctxName, updateFunction);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
} catch (e) {
|
|
98
|
+
log('error', '[::assignmentExpressionVisitor::]', e.message);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Logic Orchestration for Function Transformation.
|
|
3
|
+
* This module coordinates the 'Exit' phase of function traversal. It integrates
|
|
4
|
+
* specialized visitors for variable declarations and return statements to
|
|
5
|
+
* aggregate state metadata before injecting final React Hook declarations.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Validation & Hashing Utilities
|
|
10
|
+
* @description
|
|
11
|
+
* - isExcludeFile: Determines if the current file should be bypassed based on plugin configuration.
|
|
12
|
+
* - getFilePathHASH: Generates a unique identifier based on the file path to prevent naming collisions.
|
|
13
|
+
*/
|
|
14
|
+
import { isExcludeFile, getFilePathHASH } from '../utils/utilityHelpers';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Specialized Sub-Visitors
|
|
18
|
+
* @description These visitors are manually invoked during the function's traversal
|
|
19
|
+
* to target specific node types within the component body.
|
|
20
|
+
* - functionReturnVariableDelarationVisitor: Extracts state-relevant variable data.
|
|
21
|
+
* - functionDeclarationReturnStatementVisitor: Processes JSX or return values for context binding.
|
|
22
|
+
*/
|
|
23
|
+
import { functionReturnVariableDelarationVisitor } from './VariableDeclaration';
|
|
24
|
+
import { functionDeclarationReturnStatementVisitor } from './ReturnStatement';
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* AST Construction Helpers
|
|
28
|
+
* @description
|
|
29
|
+
* - buildCtxUseStateDeclaration: Physically constructs and injects the `useState`
|
|
30
|
+
* node into the Abstract Syntax Tree.
|
|
31
|
+
*/
|
|
32
|
+
import { buildCtxUseStateDeclaration } from '../utils/astHelpers';
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Core Constants
|
|
36
|
+
* @description
|
|
37
|
+
* - _CCTX_EMPTY: Provides a safe string fallback for file naming and path resolution.
|
|
38
|
+
*/
|
|
39
|
+
import { _CCTX_EMPTY } from '../utils/constants';
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @module Logger
|
|
43
|
+
* @description Provides a controlled logging interface for the Babel transformation process.
|
|
44
|
+
*/
|
|
45
|
+
import { log } from '../utils/utilityHelpers';
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @important
|
|
49
|
+
* The coordination between these imports ensures that state is only injected
|
|
50
|
+
* once per component, and only if the component contains variables prefixed
|
|
51
|
+
* with the library's global identifier.
|
|
52
|
+
*/
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Babel visitor exit handler for `FunctionDeclaration` nodes.
|
|
56
|
+
*
|
|
57
|
+
* This function is invoked when exiting a function declaration during AST traversal.
|
|
58
|
+
* It inspects the function for any state variables registered in the `virtualRegistry`
|
|
59
|
+
* and transforms them into React `useState` declarations or context state as needed.
|
|
60
|
+
*
|
|
61
|
+
* @param {NodePath} path - The Babel AST path representing the current function declaration.
|
|
62
|
+
* @param {Object} state - Plugin state, including file info, config, and import metadata.
|
|
63
|
+
* @param {Object} t - Babel types helper (`@babel/types`) used to generate AST nodes.
|
|
64
|
+
* @param {Object<string, Object>} virtualRegistry - Registry of components and their
|
|
65
|
+
* registered variables/context info. Each key is `${componentName}_${fileHash}`.
|
|
66
|
+
*
|
|
67
|
+
* @returns {void}
|
|
68
|
+
* Modifies AST nodes in place to inject state declarations and context usage;
|
|
69
|
+
* does not return a value.
|
|
70
|
+
*
|
|
71
|
+
* @important
|
|
72
|
+
* - Skips files excluded by `isExcludeFile`.
|
|
73
|
+
* - Only processes functions with a valid name.
|
|
74
|
+
* - Collects local state variable declarations and return statements using
|
|
75
|
+
* `functionReturnVariableDelarationVisitor` and `functionDeclarationReturnStatementVisitor`.
|
|
76
|
+
* - Converts collected variables into an object expression for `buildCtxUseStateDeclaration`.
|
|
77
|
+
* - Silent error handling; errors are caught but ignored.
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* ```js
|
|
81
|
+
* // During Babel traversal:
|
|
82
|
+
* functionDeclarationExit(path, state, t, virtualRegistry);
|
|
83
|
+
* // Injects useState or context declarations for local state variables in the function
|
|
84
|
+
* ```
|
|
85
|
+
*/
|
|
86
|
+
export function functionDeclarationExit(path, state, t, virtualRegistry) {
|
|
87
|
+
try {
|
|
88
|
+
const fileName = state.filename || _CCTX_EMPTY;
|
|
89
|
+
if (!isExcludeFile(fileName, this.opts)) return;
|
|
90
|
+
const name = path.node.id?.name;
|
|
91
|
+
if (!name) return;
|
|
92
|
+
const filePathHASH = getFilePathHASH(fileName);
|
|
93
|
+
const key = `${name}_${filePathHASH}`;
|
|
94
|
+
const entry = virtualRegistry[key];
|
|
95
|
+
if (!entry || !entry.varNames.length) return;
|
|
96
|
+
const localStateVars = [];
|
|
97
|
+
path.traverse({
|
|
98
|
+
VariableDeclarator(varPath) {
|
|
99
|
+
functionReturnVariableDelarationVisitor.call(this, varPath, state, t, localStateVars);
|
|
100
|
+
},
|
|
101
|
+
ReturnStatement(retPath) {
|
|
102
|
+
functionDeclarationReturnStatementVisitor.call(this, retPath, state, t, entry);
|
|
103
|
+
},
|
|
104
|
+
})
|
|
105
|
+
if (localStateVars.length === 0) return;
|
|
106
|
+
const objProps = localStateVars.map(v =>
|
|
107
|
+
t.objectProperty(t.identifier(v.name), v.init || t.nullLiteral())
|
|
108
|
+
);
|
|
109
|
+
buildCtxUseStateDeclaration(path, t, state, objProps, key);
|
|
110
|
+
|
|
111
|
+
} catch (e) {
|
|
112
|
+
log('error', '[::functionDeclarationExit::]', e.message);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Variable Reference & Usage Orchestration.
|
|
3
|
+
* This module manages the detection and transformation of custom context variables
|
|
4
|
+
* when they are referenced in expressions. It handles the replacement of `_$_`
|
|
5
|
+
* identifiers with either local state accessors or injected Context hooks.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* AST Node & Property Constants
|
|
10
|
+
* @description Identifiers for specific nodes and keys used to navigate the AST
|
|
11
|
+
* during variable resolution and replacement.
|
|
12
|
+
*/
|
|
13
|
+
import {
|
|
14
|
+
VARIABLE_DECLARATOR, // Target for initial variable definitions
|
|
15
|
+
_CCTX_ID, // Key for the identifier in a declaration
|
|
16
|
+
ASSIGNMENT_EXPRESSION, // Target for variable updates
|
|
17
|
+
_CCTX_LEFT, // The 'write' side of an assignment
|
|
18
|
+
UPDATE_EXPRESSION, // Target for increments/decrements (e.g., ++, --)
|
|
19
|
+
_CCTX_ARGUMENT, // The variable inside an update expression
|
|
20
|
+
_CCTX_KEY, // Property key in object patterns
|
|
21
|
+
_CCTX_UNKNOW, // Fallback for unresolved identifiers
|
|
22
|
+
_CCTX_EMPTY // Default string initializer
|
|
23
|
+
} from '../utils/constants';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Validation & Discovery Utilities
|
|
27
|
+
* @description
|
|
28
|
+
* - isExcludeFile: Ensures the transformation doesn't run on ignored directories.
|
|
29
|
+
* - findContextByVar: Locates the specific Context instance associated with a variable name.
|
|
30
|
+
*/
|
|
31
|
+
import { isExcludeFile, findContextByVar } from '../utils/utilityHelpers';
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* AST Transformation & Scope Helpers
|
|
35
|
+
* @description Functions that physically modify the code and resolve component hierarchy.
|
|
36
|
+
* - replaceWithContextState: Replaces a reference with a Context-based accessor.
|
|
37
|
+
* - replaceWithState: Replaces a reference with a local `useState` accessor.
|
|
38
|
+
* - buildUseContextInstance: Injects the `useContext` hook if the variable is defined elsewhere.
|
|
39
|
+
* - getRootParentComponent: Finds the top-level React Component to ensure hooks are valid.
|
|
40
|
+
*/
|
|
41
|
+
import { replaceWithContextState, replaceWithState, buildUseContextInstance, getRootParentComponent } from '../utils/astHelpers';
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @module Logger
|
|
45
|
+
* @description Provides a controlled logging interface for the Babel transformation process.
|
|
46
|
+
*/
|
|
47
|
+
import { log } from '../utils/utilityHelpers';
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @important
|
|
51
|
+
* This module is highly sensitive to Scope. It must distinguish between a variable
|
|
52
|
+
* being "written to" (Assignment) and "read from" (Reference) to prevent
|
|
53
|
+
* infinite loops in the Babel transformation.
|
|
54
|
+
*/
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Babel visitor for handling identifier nodes in the AST.
|
|
58
|
+
*
|
|
59
|
+
* This visitor inspects identifiers that start with the configured Casper prefix.
|
|
60
|
+
* It determines whether the identifier corresponds to a registered component or
|
|
61
|
+
* context variable and replaces it with the appropriate state or context access
|
|
62
|
+
* expression. This handles both component-local state and global/shared context usage.
|
|
63
|
+
*
|
|
64
|
+
* @param {NodePath} path - The Babel AST path representing the current identifier node.
|
|
65
|
+
* @param {Object} state - Plugin state, including file info, configuration, and import metadata.
|
|
66
|
+
* @param {Object} t - Babel types helper (`@babel/types`) used to generate AST nodes.
|
|
67
|
+
* @param {Set<Node>} seen - A Set tracking identifiers that have already been processed to avoid duplicates.
|
|
68
|
+
* @param {Object<string, Object>} virtualRegistry - Registry of components and their
|
|
69
|
+
* registered variables/context info.
|
|
70
|
+
*
|
|
71
|
+
* @returns {void}
|
|
72
|
+
* Modifies AST nodes in place by replacing identifiers with either direct state access
|
|
73
|
+
* or context-based access. Does not return a value.
|
|
74
|
+
*
|
|
75
|
+
* @important
|
|
76
|
+
* - Skips identifiers that are part of declarations, assignments, object keys, or JSX expressions.
|
|
77
|
+
* - Only processes referenced identifiers matching the configured Casper prefix.
|
|
78
|
+
* - Sets `state.needsGblContext` when global context usage is required.
|
|
79
|
+
* - Automatically injects `useState` import if missing.
|
|
80
|
+
* - Differentiates between same-component state and context usage across components.
|
|
81
|
+
* - Errors are silently caught.
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* ```js
|
|
85
|
+
* import identifierVisitor from './identifierVisitor';
|
|
86
|
+
*
|
|
87
|
+
* identifierVisitor(path, state, t, seen, virtualRegistry);
|
|
88
|
+
* // Replaces matching identifiers with state or context references
|
|
89
|
+
* ```
|
|
90
|
+
*/
|
|
91
|
+
export default function identifierVisitor (path, state, t, seen, virtualRegistry) {
|
|
92
|
+
try {
|
|
93
|
+
const fileName = state.filename || _CCTX_EMPTY;
|
|
94
|
+
if (!isExcludeFile(fileName, this.opts)) return;
|
|
95
|
+
if (path.node?.name?.startsWith(state.casperConfig.prefix)) {
|
|
96
|
+
if (
|
|
97
|
+
(path.parent.type === VARIABLE_DECLARATOR && path.parentKey === _CCTX_ID) ||
|
|
98
|
+
(path.parent.type === ASSIGNMENT_EXPRESSION && path.parentKey === _CCTX_LEFT) ||
|
|
99
|
+
(path.parent.type === UPDATE_EXPRESSION && path.parentKey === _CCTX_ARGUMENT)
|
|
100
|
+
) return;
|
|
101
|
+
if (
|
|
102
|
+
path.parentPath.isObjectProperty() &&
|
|
103
|
+
path.parentKey === _CCTX_KEY &&
|
|
104
|
+
!path.parent.computed
|
|
105
|
+
) return;
|
|
106
|
+
if (path.findParent(p =>
|
|
107
|
+
p.isJSXExpressionContainer() ||
|
|
108
|
+
p.isJSXAttribute() ||
|
|
109
|
+
p.isJSXOpeningElement() ||
|
|
110
|
+
p.isJSXClosingElement() ||
|
|
111
|
+
p.isJSXMemberExpression()
|
|
112
|
+
)) return;
|
|
113
|
+
if (!path.isReferencedIdentifier()) return;
|
|
114
|
+
if (!seen.has(path.node)) {
|
|
115
|
+
seen.add(path.node);
|
|
116
|
+
state.needsGblContext = true;
|
|
117
|
+
let {currentFuncParent,componentName} = getRootParentComponent(path);
|
|
118
|
+
const ctxName = findContextByVar(path.node.name, virtualRegistry);
|
|
119
|
+
if (!ctxName) return;
|
|
120
|
+
let isSameCMP = false;
|
|
121
|
+
if (ctxName.startsWith(componentName)) {
|
|
122
|
+
isSameCMP = true;
|
|
123
|
+
}
|
|
124
|
+
state.needsGblContext = true;
|
|
125
|
+
if (
|
|
126
|
+
!state.importState.reactId &&
|
|
127
|
+
!state.importState.useStateId
|
|
128
|
+
) {
|
|
129
|
+
state.needUseStateImport = true
|
|
130
|
+
}
|
|
131
|
+
if (isSameCMP) {
|
|
132
|
+
replaceWithState(path, t, ctxName);
|
|
133
|
+
} else {
|
|
134
|
+
buildUseContextInstance(currentFuncParent, state, t, ctxName);
|
|
135
|
+
replaceWithContextState(path, t, ctxName);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
} catch (e) {
|
|
140
|
+
log('error', '[::identifierVisitor::]', e.message);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Library Initialization & Dependency Injection Orchestrator.
|
|
3
|
+
* This module manages the "Prep Phase" of the transformation, ensuring that
|
|
4
|
+
* required React hooks and internal global context bridges are correctly
|
|
5
|
+
* imported or required before any code modifications occur.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Dependency & Alias Constants
|
|
10
|
+
* @description Identifiers for React and internal library references used to
|
|
11
|
+
* avoid naming collisions with user-defined variables.
|
|
12
|
+
*/
|
|
13
|
+
import {
|
|
14
|
+
_CCTX_REACT, // Internal alias for the React object
|
|
15
|
+
_CCTX_EMPTY, // Safe string fallback for paths/names
|
|
16
|
+
_CCTX_UNDUS_CORE_REACT, // Normalized identifier for the React import
|
|
17
|
+
_CCTX_UNDUS_CORE_GBL_CONTEXT, // Identifier for the auto-generated global context
|
|
18
|
+
REACT_IMPORT_CORE_NAME, // Literal 'react' package name
|
|
19
|
+
REACT_IMPORT_USE_STATE_HOOKS_NAME // Literal 'useState' hook name
|
|
20
|
+
} from '../utils/constants';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* File System & Lifecycle Utilities
|
|
24
|
+
* @description
|
|
25
|
+
* - CONTEXT_FILE_PATH: The absolute path to the generated context bridge.
|
|
26
|
+
* - resetVarsForFile: Cleanup utility to clear tracking caches between file traversals.
|
|
27
|
+
* - isExcludeFile: Security gate to prevent transformation of ignored files.
|
|
28
|
+
* - getFilePathHASH: Generates a unique, stable ID for the current file's state bucket.
|
|
29
|
+
*/
|
|
30
|
+
import { CONTEXT_FILE_PATH, resetVarsForFile, isExcludeFile, getFilePathHASH } from '../utils/utilityHelpers';
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* AST Injection Helpers
|
|
34
|
+
* @description
|
|
35
|
+
* - buildRequireDeclaration: Injects CommonJS `require` statements at the top of the file.
|
|
36
|
+
*/
|
|
37
|
+
import { buildRequireDeclaration } from '../utils/astHelpers';
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @module Logger
|
|
41
|
+
* @description Provides a controlled logging interface for the Babel transformation process.
|
|
42
|
+
*/
|
|
43
|
+
import { log } from '../utils/utilityHelpers';
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* @important
|
|
47
|
+
* This module is responsible for "Stateful Reset." Every time a new file is
|
|
48
|
+
* entered, `resetVarsForFile` must be called to ensure that context variables
|
|
49
|
+
* from the previous file do not leak into the current transformation scope.
|
|
50
|
+
*/
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Resolves and collects React import information from a file's AST.
|
|
54
|
+
*
|
|
55
|
+
* This function inspects all import declarations in the given file's AST
|
|
56
|
+
* and identifies the React import. It tracks whether React was imported as
|
|
57
|
+
* a default import, named import, or namespace import, and whether `useState`
|
|
58
|
+
* is explicitly imported.
|
|
59
|
+
*
|
|
60
|
+
* @param {NodePath} path - The Babel AST path representing the file/program node.
|
|
61
|
+
* @param {Object} state - Plugin state object where resolved import information
|
|
62
|
+
* will be stored under `state.importState`.
|
|
63
|
+
* @param {Object} t - Babel types helper (`@babel/types`) used for AST type checks.
|
|
64
|
+
*
|
|
65
|
+
* @returns {void}
|
|
66
|
+
* Updates `state.importState` with the following structure:
|
|
67
|
+
* ```js
|
|
68
|
+
* {
|
|
69
|
+
* reactId: Identifier | null, // Local identifier for React import
|
|
70
|
+
* useStateId: Identifier | null, // Local identifier for useState import
|
|
71
|
+
* isDefault: boolean, // True if React is default-imported
|
|
72
|
+
* isNamed: boolean, // True if useState is explicitly named-imported
|
|
73
|
+
* isNamespace: boolean, // True if React is namespace-imported
|
|
74
|
+
* reactImportPath: ImportDeclaration | null, // AST node of React import
|
|
75
|
+
* }
|
|
76
|
+
* ```
|
|
77
|
+
*
|
|
78
|
+
* @important
|
|
79
|
+
* - Only resolves imports where `source.value` equals `REACT_IMPORT_CORE_NAME`.
|
|
80
|
+
* - Handles three import forms:
|
|
81
|
+
* - `import React from 'react'`
|
|
82
|
+
* - `import { useState } from 'react'`
|
|
83
|
+
* - `import * as ReactNS from 'react'`
|
|
84
|
+
* - Errors are silently caught; unresolved imports will leave `state.importState` with nulls.
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* ```js
|
|
88
|
+
* importStateResolver(path, state, t);
|
|
89
|
+
* console.log(state.importState.reactId); // Identifier for React import
|
|
90
|
+
* console.log(state.importState.useStateId); // Identifier for useState if imported
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
93
|
+
function importStateResolver(path, state, t) {
|
|
94
|
+
try {
|
|
95
|
+
const importState = {
|
|
96
|
+
reactId: null,
|
|
97
|
+
useStateId: null,
|
|
98
|
+
isDefault: false,
|
|
99
|
+
isNamed: false,
|
|
100
|
+
isNamespace: false,
|
|
101
|
+
reactImportPath: null,
|
|
102
|
+
};
|
|
103
|
+
path.node.body.forEach(node => {
|
|
104
|
+
if (!t.isImportDeclaration(node)) return;
|
|
105
|
+
if (node.source.value !== REACT_IMPORT_CORE_NAME) return;
|
|
106
|
+
importState.reactImportPath = node;
|
|
107
|
+
node.specifiers.forEach(spec => {
|
|
108
|
+
|
|
109
|
+
// import React from 'react'
|
|
110
|
+
if (t.isImportDefaultSpecifier(spec)) {
|
|
111
|
+
importState.reactId = spec.local;
|
|
112
|
+
importState.isDefault = true;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// import { useState } from 'react'
|
|
116
|
+
if (t.isImportSpecifier(spec)) {
|
|
117
|
+
if (spec.imported.name === REACT_IMPORT_USE_STATE_HOOKS_NAME) {
|
|
118
|
+
importState.useStateId = spec.local;
|
|
119
|
+
importState.isNamed = true;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// import * as ReactNS from 'react'
|
|
124
|
+
if (t.isImportNamespaceSpecifier(spec)) {
|
|
125
|
+
importState.reactId = spec.local;
|
|
126
|
+
importState.isNamespace = true;
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
state.importState = importState;
|
|
131
|
+
} catch (e) {
|
|
132
|
+
log('error', '[::importStateResolver::]', e.message);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Resets the variable registry for the current file during AST traversal.
|
|
138
|
+
*
|
|
139
|
+
* This function clears any registered state variables in the `virtualRegistry`
|
|
140
|
+
* that are associated with the current file. It is useful for ensuring that
|
|
141
|
+
* state tracking does not leak across files when processing multiple files
|
|
142
|
+
* in the plugin.
|
|
143
|
+
*
|
|
144
|
+
* @param {Object} state - Plugin state, including the filename and plugin options.
|
|
145
|
+
* @param {Object<string, Object>} virtualRegistry - Registry of components and
|
|
146
|
+
* their registered variables/context info.
|
|
147
|
+
*
|
|
148
|
+
* @returns {void}
|
|
149
|
+
* Updates the `virtualRegistry` in place, resetting all variable names and
|
|
150
|
+
* default values associated with the current file.
|
|
151
|
+
*
|
|
152
|
+
* @important
|
|
153
|
+
* - Skips files excluded by `isExcludeFile`.
|
|
154
|
+
* - Uses a hash of the filename to identify the relevant registry entries.
|
|
155
|
+
* - Errors are silently caught.
|
|
156
|
+
*
|
|
157
|
+
* @example
|
|
158
|
+
* ```js
|
|
159
|
+
* resetRegisteryProcess(state, virtualRegistry);
|
|
160
|
+
* // Clears all state variables for the current file in the virtualRegistry
|
|
161
|
+
* ```
|
|
162
|
+
*/
|
|
163
|
+
function resetRegisteryProcess(state, virtualRegistry) {
|
|
164
|
+
try {
|
|
165
|
+
const fileName = state.filename || _CCTX_EMPTY;
|
|
166
|
+
if (!isExcludeFile(fileName, this.opts)) return;
|
|
167
|
+
const hash = getFilePathHASH(fileName);
|
|
168
|
+
resetVarsForFile(virtualRegistry, hash);
|
|
169
|
+
} catch (e) {
|
|
170
|
+
log('error', '[::resetRegisteryProcess::]', e.message);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Babel visitor handler for the `Program` node when entering a file.
|
|
176
|
+
*
|
|
177
|
+
* This function initializes plugin state for the file, resolves React imports,
|
|
178
|
+
* and resets the variable registry for the current file. It is typically used
|
|
179
|
+
* at the beginning of processing each file to ensure a clean state.
|
|
180
|
+
*
|
|
181
|
+
* @param {NodePath} path - The Babel AST path representing the Program node.
|
|
182
|
+
* @param {Object} state - Plugin state, including filename, import metadata, and configuration.
|
|
183
|
+
* @param {Object} t - Babel types helper (`@babel/types`) used to generate or check AST nodes.
|
|
184
|
+
* @param {Object<string, Object>} virtualRegistry - Registry of components and their
|
|
185
|
+
* registered variables/context info.
|
|
186
|
+
* @param {Object} config - The plugin configuration for the current file.
|
|
187
|
+
*
|
|
188
|
+
* @returns {void}
|
|
189
|
+
* - Updates `state.casperConfig` with the provided config.
|
|
190
|
+
* - Populates `state.importState` with resolved React imports.
|
|
191
|
+
* - Resets the variable registry for the current file in `virtualRegistry`.
|
|
192
|
+
*
|
|
193
|
+
* @important
|
|
194
|
+
* - Must be called at the entry of each Program node to ensure correct setup.
|
|
195
|
+
* - Errors are silently caught; consider logging during debugging.
|
|
196
|
+
*
|
|
197
|
+
* @example
|
|
198
|
+
* ```js
|
|
199
|
+
* programEnter(path, state, t, virtualRegistry, pluginConfig);
|
|
200
|
+
* // Initializes plugin state, resolves React imports, resets file-specific registry
|
|
201
|
+
* ```
|
|
202
|
+
*/
|
|
203
|
+
export function programEnter(path, state, t, virtualRegistry, config) {
|
|
204
|
+
try {
|
|
205
|
+
state.casperConfig = config
|
|
206
|
+
importStateResolver(path, state, t);
|
|
207
|
+
resetRegisteryProcess.call(this, state, virtualRegistry);
|
|
208
|
+
} catch (e) {
|
|
209
|
+
log('error', '[::programEnter::]', e.message);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Injects required imports for React and global context if they are missing.
|
|
215
|
+
*
|
|
216
|
+
* This function ensures that the necessary modules are imported at the top of
|
|
217
|
+
* the file when the AST traversal detects that `useState` or global context usage
|
|
218
|
+
* is needed. It adds the imports only when required, avoiding duplicate or
|
|
219
|
+
* unnecessary imports.
|
|
220
|
+
*
|
|
221
|
+
* @param {NodePath} path - The Babel AST path where imports should be injected.
|
|
222
|
+
* @param {Object} state - Plugin state, containing flags `needUseStateImport` and `needsGblContext`.
|
|
223
|
+
* @param {Object} t - Babel types helper (`@babel/types`) used to generate AST nodes.
|
|
224
|
+
*
|
|
225
|
+
* @returns {void}
|
|
226
|
+
* - Conditionally injects `React` import if `useState` is required.
|
|
227
|
+
* - Conditionally injects global context import if any global context is used.
|
|
228
|
+
* - Uses `buildRequireDeclaration` to insert the import statements at the top of the file.
|
|
229
|
+
*
|
|
230
|
+
* @important
|
|
231
|
+
* - Checks `state.needUseStateImport` and `state.needsGblContext` to determine necessity.
|
|
232
|
+
* - Errors are silently caught; no exception is thrown if import insertion fails.
|
|
233
|
+
*
|
|
234
|
+
* @example
|
|
235
|
+
* ```js
|
|
236
|
+
* bindMissingImport(path, state, t);
|
|
237
|
+
* // Injects React and global context imports if they are missing
|
|
238
|
+
* ```
|
|
239
|
+
*/
|
|
240
|
+
function bindMissingImport(path, state, t) {
|
|
241
|
+
try {
|
|
242
|
+
if (state?.needUseStateImport) buildRequireDeclaration(path, t, _CCTX_UNDUS_CORE_REACT, _CCTX_REACT);
|
|
243
|
+
if (state?.needsGblContext) buildRequireDeclaration(path, t, _CCTX_UNDUS_CORE_GBL_CONTEXT, CONTEXT_FILE_PATH);
|
|
244
|
+
} catch (e) {
|
|
245
|
+
log('error', '[::bindMissingImport::]', e.message);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Babel visitor handler for the `Program` node when exiting a file.
|
|
251
|
+
*
|
|
252
|
+
* This function is called after the AST traversal of the entire file is complete.
|
|
253
|
+
* Its primary purpose is to inject any missing imports for React or global context
|
|
254
|
+
* that were flagged as required during traversal.
|
|
255
|
+
*
|
|
256
|
+
* @param {NodePath} path - The Babel AST path representing the Program node.
|
|
257
|
+
* @param {Object} state - Plugin state, including flags `needUseStateImport` and `needsGblContext`.
|
|
258
|
+
* @param {Object} t - Babel types helper (`@babel/types`) used to generate AST nodes.
|
|
259
|
+
*
|
|
260
|
+
* @returns {void}
|
|
261
|
+
* Modifies the AST in place by calling `bindMissingImport` to insert necessary imports.
|
|
262
|
+
*
|
|
263
|
+
* @important
|
|
264
|
+
* - Should be paired with `programEnter` at the start of traversal.
|
|
265
|
+
* - Only injects imports if flagged during traversal (`state.needUseStateImport` or `state.needsGblContext`).
|
|
266
|
+
* - Errors are silently caught; AST modifications fail gracefully if errors occur.
|
|
267
|
+
*
|
|
268
|
+
* @example
|
|
269
|
+
* ```js
|
|
270
|
+
* programExit(path, state, t);
|
|
271
|
+
* // Ensures React and global context imports are added if needed
|
|
272
|
+
* ```
|
|
273
|
+
*/
|
|
274
|
+
export function programExit(path, state, t) {
|
|
275
|
+
try {
|
|
276
|
+
bindMissingImport(path, state, t);
|
|
277
|
+
} catch (e) {
|
|
278
|
+
log('error', '[::programExit::]', e.message);
|
|
279
|
+
}
|
|
280
|
+
}
|