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
package/README.md
ADDED
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
<!--lint disable awesome-heading awesome-github awesome-toc double-link -->
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<br>
|
|
5
|
+
<img width="400" src="./react-casper-context.png" alt="react casper context logo">
|
|
6
|
+
<br><br>
|
|
7
|
+
</p>
|
|
8
|
+
|
|
9
|
+
<h1 align="center">React Casper Context</h1>
|
|
10
|
+
|
|
11
|
+
<h3 align="center">
|
|
12
|
+
🚀 Declare it like a normal JavaScript variable. Use it anywhere like React Context.
|
|
13
|
+
</h3>
|
|
14
|
+
|
|
15
|
+
<p align="center">
|
|
16
|
+
Casper Context is a Babel compile-time plugin that transforms specially prefixed variables into fully functional React Context API state — automatically.
|
|
17
|
+
<br/>
|
|
18
|
+
No providers. No boilerplate. No complex setup.
|
|
19
|
+
<br/>
|
|
20
|
+
Just declare → use → update.
|
|
21
|
+
</p>
|
|
22
|
+
|
|
23
|
+
<p align="center">
|
|
24
|
+
<a href="https://chic-duckanoo-aa0a41.netlify.app/">
|
|
25
|
+
<img src="https://img.shields.io/badge/Live%20Demo-Open-green?style=for-the-badge" />
|
|
26
|
+
</a>
|
|
27
|
+
</p>
|
|
28
|
+
|
|
29
|
+
<!--lint ignore-->
|
|
30
|
+
|
|
31
|
+
## ✨ Quick Example
|
|
32
|
+
### ✅ Declare Context Variable
|
|
33
|
+
|
|
34
|
+
```jsx
|
|
35
|
+
import React from 'react'
|
|
36
|
+
|
|
37
|
+
function App() {
|
|
38
|
+
|
|
39
|
+
let _$_appMessage = 'Say Hi to the Casper';
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<>
|
|
43
|
+
<CasperHero />
|
|
44
|
+
<MessageBox />
|
|
45
|
+
</>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### ✅ Use Anywhere In Component Tree
|
|
51
|
+
|
|
52
|
+
```jsx
|
|
53
|
+
import React from 'react'
|
|
54
|
+
|
|
55
|
+
function CasperHero() {
|
|
56
|
+
|
|
57
|
+
return <h1>{_$_appMessage}</h1>;
|
|
58
|
+
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### ✅ Update Like Normal Variable
|
|
64
|
+
|
|
65
|
+
```jsx
|
|
66
|
+
import React from 'react';
|
|
67
|
+
|
|
68
|
+
function MessageBox() {
|
|
69
|
+
|
|
70
|
+
const handleChange = () => {
|
|
71
|
+
_$_appMessage = 'Hi Casper';
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<>
|
|
76
|
+
|
|
77
|
+
<button onClick={handleChange}>
|
|
78
|
+
Change
|
|
79
|
+
</button>
|
|
80
|
+
|
|
81
|
+
</>;
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
That’s it. Under the hood, the plugin rewrites your code to use the native React Context API. It is 100% React-compliant at runtime.
|
|
86
|
+
|
|
87
|
+
### ✨ Features
|
|
88
|
+
|
|
89
|
+
- **Zero Boilerplate:** No more createContext, useContext, or wrapping components manually.
|
|
90
|
+
- **Automatic Reactivity:** When a casper variable changes, all components using it re-render automatically..
|
|
91
|
+
- **Scoped & Global:** Accessible in the component where it's declared and any nested child component.
|
|
92
|
+
- **Native Performance:** Since it compiles to the native React Context API, there is zero overhead compared to writing Context manually.
|
|
93
|
+
- **Standard Syntax:** Use familiar assignment syntax to update global state.
|
|
94
|
+
|
|
95
|
+
### 📦 Installation
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
npm install casper-context --save-dev
|
|
99
|
+
```
|
|
100
|
+
### ⚙️ Setup
|
|
101
|
+
|
|
102
|
+
#### 1️⃣ Add Babel Plugin
|
|
103
|
+
Add it to your `.babelrc` or `babel.config.js`
|
|
104
|
+
|
|
105
|
+
```json
|
|
106
|
+
{
|
|
107
|
+
"plugins": ["casper-context"]
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
#### 2️⃣ CRA Users (Important)
|
|
111
|
+
Create React App does not support direct Babel modification. You must use CRACO.
|
|
112
|
+
|
|
113
|
+
Install CRACO:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
npm install @craco/craco
|
|
117
|
+
```
|
|
118
|
+
Then configure CRACO to inject the Casper Context Babel plugin in package.json
|
|
119
|
+
|
|
120
|
+
```json
|
|
121
|
+
"scripts": {
|
|
122
|
+
"start": "craco start",
|
|
123
|
+
"build": "craco build",
|
|
124
|
+
"test": "craco test",
|
|
125
|
+
"eject": "react-scripts eject"
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
#### 3️⃣ ESLint Setup (Required)
|
|
129
|
+
To avoid `undefined` variable warnings
|
|
130
|
+
```js
|
|
131
|
+
globals: {
|
|
132
|
+
...require('./casper-eslint.global.js')
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### 🔧 Custom Configuration
|
|
137
|
+
|
|
138
|
+
By default, the plugin identifies variables using the `_$_` prefix.
|
|
139
|
+
|
|
140
|
+
```javascript
|
|
141
|
+
// Default usage
|
|
142
|
+
let _$_myName = 'Jhone';
|
|
143
|
+
```
|
|
144
|
+
###### Custom Prefix
|
|
145
|
+
If you encounter naming collisions or simply prefer a different identifier, you can customize the prefix. Create a `.casperctxrc.json` file in your **project root** and specify your preferred prefix
|
|
146
|
+
```json
|
|
147
|
+
{
|
|
148
|
+
"prefix": "CCTX"
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
Now, the plugin will look for your custom string instead
|
|
152
|
+
```jsx
|
|
153
|
+
// With custom configuration
|
|
154
|
+
let CCTXmyName = 'Jhone';
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## 📜 The Golden Rules
|
|
158
|
+
|
|
159
|
+
To ensure Casper Context transforms your code correctly, please follow these core principles:
|
|
160
|
+
|
|
161
|
+
### 1. Naming & Prefix
|
|
162
|
+
Every Casper variable **must** start with the defined prefix (Default: `_$_`).
|
|
163
|
+
* **Correct:** `let _$_userName = 'John';`
|
|
164
|
+
* **Incorrect:** `let userName = 'John';`
|
|
165
|
+
|
|
166
|
+
### 2. React Import Requirement
|
|
167
|
+
Any file that declares, reads, or writes a Casper variable **must** import React. This is required because the compiled code relies on `React.useContext` and `React.useState`.
|
|
168
|
+
* Any valid import works:
|
|
169
|
+
* `import React from 'react';`
|
|
170
|
+
* `import * as React from 'react';`
|
|
171
|
+
|
|
172
|
+
### 3. Follow React Hook Rules
|
|
173
|
+
Since Casper variables are converted into Hooks under the hood, they must follow the [Rules of Hooks](https://react.dev/warnings/rules-of-hooks):
|
|
174
|
+
* Only declare variables at the **top level** of your React function component.
|
|
175
|
+
* Do **not** declare them inside loops, conditions, or nested functions.
|
|
176
|
+
|
|
177
|
+
### 4. Global Uniqueness (Naming)
|
|
178
|
+
Currently, Casper variables are treated as globally unique identifiers. You must ensure that every Casper variable name is unique across your entire project, even if they are in different components.
|
|
179
|
+
* **Example:** If you have `_$_user` in `ComponentA`, do not use `_$_user` in `ComponentB`. Use `_$_adminUser` or `_$_customerUser` instead.
|
|
180
|
+
|
|
181
|
+
### 5. Component-Based Context Grouping
|
|
182
|
+
Contexts are grouped by the component where the variables are declared.
|
|
183
|
+
* All `_$_` variables declared in the **same component** share the **same Context**.
|
|
184
|
+
* To create **separate Contexts**, declare your variables in **different components**.
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
#### Example:
|
|
189
|
+
**Context A (Admin Scope):**
|
|
190
|
+
```javascript
|
|
191
|
+
function Admin() {
|
|
192
|
+
let _$_adminName = 'Jakie';
|
|
193
|
+
let _$_adminAge = 34;
|
|
194
|
+
// These belong to the 'Admin' Context
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
**Context B (Customer Scope):**
|
|
198
|
+
```javascript
|
|
199
|
+
function Customer() {
|
|
200
|
+
let _$_customerName = 'Scot';
|
|
201
|
+
let _$_customerAge = 25;
|
|
202
|
+
// These belong to the 'Customer' Context
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## 🛠 Troubleshooting
|
|
207
|
+
|
|
208
|
+
If your variables are not becoming reactive or you see errors in the console, check the following:
|
|
209
|
+
|
|
210
|
+
### 1. Variables are not transforming
|
|
211
|
+
* **Check the Prefix**: Ensure your variable starts exactly with your prefix (Default: `_$_`).
|
|
212
|
+
* **Declaration Keyword**: Use `let` or `var` for variables you intend to update. Using `const` will prevent you from reassigning the value later.
|
|
213
|
+
* **Babel Cache**: Babel often caches transforms. Try restarting your dev server or clearing the cache (e.g., `rm -rf node_modules/.cache`).
|
|
214
|
+
|
|
215
|
+
### 2. "Variable is not defined" (ESLint Errors)
|
|
216
|
+
Because Casper Context injects variables at compile-time, ESLint might think they are undefined.
|
|
217
|
+
* **Solution**: The plugin automatically generates a `casper-eslint.global.js` file in your root. Reference this in your `.eslintrc.js`:
|
|
218
|
+
```javascript
|
|
219
|
+
const casperGlobals = require('./casper-eslint.global.js');
|
|
220
|
+
module.exports = {
|
|
221
|
+
globals: { ...casperGlobals }
|
|
222
|
+
};
|
|
223
|
+
### 3. Changes to `.casperctxrc.json` not reflecting
|
|
224
|
+
Babel reads the configuration once when the process starts.
|
|
225
|
+
* **Solution**: If you change your custom prefix in `.casperctxrc.json`, you **must restart** your build tool (Vite, Webpack, or Next.js).
|
|
226
|
+
### 4. Component not re-rendering
|
|
227
|
+
* **Scope**: Ensure the variable is declared within a React Component or a file that is part of the Babel transformation path.
|
|
228
|
+
|
|
229
|
+
* **Hooks Rules**: Remember that under the hood, this becomes a React Hook. Do not declare `_$_` variables inside nested loops or conditional if statements.
|
|
230
|
+
## 📝 Debugging
|
|
231
|
+
If you are still having trouble, enable debug mode in your `.casperctxrc.json`
|
|
232
|
+
```json
|
|
233
|
+
{
|
|
234
|
+
"prefix": "_$_",
|
|
235
|
+
"debug": true,
|
|
236
|
+
"debug_levels": ['reset','info','warn','error', 'debug', 'trace']
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
This will generate a `.casperctx.debug.log` file in your project root, detailing exactly how the plugin is mapping your variables.
|
|
240
|
+
|
|
241
|
+
### 🗺 Roadmap
|
|
242
|
+
- ✅ CRA Support
|
|
243
|
+
- ✅ Babel + Webpack Support
|
|
244
|
+
- 🚧 Vite Integration
|
|
245
|
+
- 🚧 Next.js Integration
|
|
246
|
+
- 🚧 TypeScript Improvements
|
|
247
|
+
- 🚧 Developer Tools Integration
|
|
248
|
+
|
|
249
|
+
### 🤝 Contributing
|
|
250
|
+
Contributions, suggestions, and issues are welcome.
|
|
251
|
+
|
|
252
|
+
### 📜 License
|
|
253
|
+
MIT License
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
var _plugin = _interopRequireDefault(require("./plugin"));
|
|
8
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
9
|
+
var _default = exports.default = _plugin.default;
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = postProcess;
|
|
7
|
+
var _fs = _interopRequireDefault(require("fs"));
|
|
8
|
+
var _path = _interopRequireDefault(require("path"));
|
|
9
|
+
var _utilityHelpers = require("../utils/utilityHelpers");
|
|
10
|
+
var _constants = require("../utils/constants");
|
|
11
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
12
|
+
/**
|
|
13
|
+
* @fileoverview Environment Initialization and File Orchestration.
|
|
14
|
+
* This module is responsible for the physical creation and synchronization of
|
|
15
|
+
* the Casper Context infrastructure, including the global context bridge and
|
|
16
|
+
* ESLint configuration overrides.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Path Utilities & Constants
|
|
21
|
+
* @description Leverages resolved system paths and core constants to ensure
|
|
22
|
+
* write operations target the correct project directories.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @module Logger
|
|
27
|
+
* @description Provides a controlled logging interface for the Babel transformation process.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @important
|
|
32
|
+
* The following imports are critical for the library's lifecycle:
|
|
33
|
+
* - `CONTEXT_FILE_PATH`: The destination for the auto-generated React Context logic.
|
|
34
|
+
* - `ESLINT_GLOBAL_JS_PATH`: The file that prevents 'no-undef' errors for _$_ variables.
|
|
35
|
+
* - `UNICODE_UTF8`: Standard encoding used for all `fs.writeFileSync` operations.
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Generates a JavaScript module file that exports React context instances
|
|
40
|
+
* based on the registered variables in the `virtualRegistry`.
|
|
41
|
+
*
|
|
42
|
+
* This function creates or overwrites the file at `CONTEXT_FILE_PATH`, injecting
|
|
43
|
+
* `'use strict'` directives, ES module compatibility boilerplate, and context
|
|
44
|
+
* initialization code. Each registered component context is turned into a
|
|
45
|
+
* `React.createContext` with default values derived from the registry.
|
|
46
|
+
*
|
|
47
|
+
* @param {Object<string, Object>} virtualRegistry - The in-memory registry mapping
|
|
48
|
+
* component hashes to their context metadata:
|
|
49
|
+
* ```js
|
|
50
|
+
* {
|
|
51
|
+
* [componentHash]: {
|
|
52
|
+
* ctxName: string, // Generated context name
|
|
53
|
+
* varNames: string[], // List of variable names registered for this context
|
|
54
|
+
* defaults: Record<string, any> // Default values for each variable
|
|
55
|
+
* }
|
|
56
|
+
* }
|
|
57
|
+
* ```
|
|
58
|
+
*
|
|
59
|
+
* @returns {void}
|
|
60
|
+
* This function writes to the filesystem directly and does not return a value.
|
|
61
|
+
*
|
|
62
|
+
* @important
|
|
63
|
+
* - The generated module follows CommonJS export style with ES module compatibility.
|
|
64
|
+
* - All variables registered in `virtualRegistry` are included in their respective
|
|
65
|
+
* `createContext` objects. Variables without a default value are set to `_CCTX_UNDEFINED`.
|
|
66
|
+
* - The function overwrites any existing file at `CONTEXT_FILE_PATH`.
|
|
67
|
+
* - React is imported as `_react` and used for `createContext` calls.
|
|
68
|
+
* - The `_CCTX_CMP_NAME_PREFIX` is stripped from exported context names for clarity.
|
|
69
|
+
* - Errors are silently caught; consider adding logging for debugging or dev builds.
|
|
70
|
+
* - The generated content is formatted with line breaks and indentation for readability.
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```js
|
|
74
|
+
* const virtualRegistry = {
|
|
75
|
+
* 'abc123': {
|
|
76
|
+
* ctxName: '_CCTX_abc123',
|
|
77
|
+
* varNames: ['count', 'enabled'],
|
|
78
|
+
* defaults: { count: 0, enabled: true }
|
|
79
|
+
* }
|
|
80
|
+
* };
|
|
81
|
+
* generateContextContent(virtualRegistry);
|
|
82
|
+
* // Produces a file exporting a React context with default { count: 0, enabled: true }
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
function generateContextContent(virtualRegistry) {
|
|
86
|
+
try {
|
|
87
|
+
_fs.default.writeFileSync(_utilityHelpers.CONTEXT_FILE_PATH, _constants._CCTX_EMPTY, _constants.UNICODE_UTF8);
|
|
88
|
+
let contextNames = [];
|
|
89
|
+
let content = `'use strict';\n\nObject.defineProperty(exports, '__esModule', {\n value: true\n});\n`;
|
|
90
|
+
if (Object.keys(virtualRegistry).length === 0) return;
|
|
91
|
+
Object.values(virtualRegistry).forEach(entry => {
|
|
92
|
+
if (!entry.ctxName) return;
|
|
93
|
+
contextNames.push(entry.ctxName.replace(_constants._CCTX_CMP_NAME_PREFIX, _constants._CCTX_EMPTY));
|
|
94
|
+
});
|
|
95
|
+
content += `exports.${contextNames.join(' = exports.')} = void 0;\n`;
|
|
96
|
+
content += `var _react = require('react');\n`;
|
|
97
|
+
Object.values(virtualRegistry).forEach(entry => {
|
|
98
|
+
if (!entry.ctxName) return;
|
|
99
|
+
const {
|
|
100
|
+
ctxName,
|
|
101
|
+
varNames,
|
|
102
|
+
defaults
|
|
103
|
+
} = entry;
|
|
104
|
+
const defaultObjProps = varNames.map(name => {
|
|
105
|
+
const val = defaults[name] !== undefined ? JSON.stringify(defaults[name]) : _constants._CCTX_UNDEFINED;
|
|
106
|
+
return ` ${name}: ${val}`;
|
|
107
|
+
}).join(',\n');
|
|
108
|
+
content += `const ${ctxName.replace(_constants._CCTX_CMP_NAME_PREFIX, _constants._CCTX_EMPTY)} = exports.${ctxName.replace(_constants._CCTX_CMP_NAME_PREFIX, _constants._CCTX_EMPTY)} = /*#__PURE__*/(0, _react.createContext)({\n${defaultObjProps}\n});\n`;
|
|
109
|
+
});
|
|
110
|
+
_fs.default.writeFileSync(_utilityHelpers.CONTEXT_FILE_PATH, content, _constants.UNICODE_UTF8);
|
|
111
|
+
} catch (e) {
|
|
112
|
+
(0, _utilityHelpers.log)('error', '[::generateContextContent::]', e.message);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Generates or updates the ESLint global variables configuration file
|
|
118
|
+
* based on the current virtual registry.
|
|
119
|
+
*
|
|
120
|
+
* This function reads all variable names registered in `virtualRegistry`
|
|
121
|
+
* and writes them as read-only globals in a JavaScript module (`ESLINT_GLOBAL_JS_PATH`).
|
|
122
|
+
* It compares the newly generated content with the existing file and only
|
|
123
|
+
* overwrites if changes are detected. Finally, it updates the modification
|
|
124
|
+
* timestamp of the ESLint configuration file to trigger linting updates if needed.
|
|
125
|
+
*
|
|
126
|
+
* @param {Object<string, Object>} virtualRegistry - The in-memory registry of component
|
|
127
|
+
* variables. Each entry should contain:
|
|
128
|
+
* ```js
|
|
129
|
+
* {
|
|
130
|
+
* varNames: string[], // List of variable names for the component/context
|
|
131
|
+
* ctxName?: string, // Optional context name
|
|
132
|
+
* defaults?: Record<string, any> // Optional default values
|
|
133
|
+
* }
|
|
134
|
+
* ```
|
|
135
|
+
* @param {Object<string, Object>} [prevVirtualRegistrySnap] - Optional snapshot of
|
|
136
|
+
* the previous virtual registry state. Currently unused, but can be used
|
|
137
|
+
* to detect changes or optimize writes.
|
|
138
|
+
*
|
|
139
|
+
* @returns {void}
|
|
140
|
+
* Writes or updates the ESLint global JS file on disk and updates the
|
|
141
|
+
* corresponding ESLint configuration file's timestamps.
|
|
142
|
+
*
|
|
143
|
+
* @important
|
|
144
|
+
* - All variables are marked as read-only in the ESLint globals (`CASPER_READ_ONLY_TYPE`).
|
|
145
|
+
* - File writes are **atomic**: the target file is cleared before writing new content.
|
|
146
|
+
* - If no variables exist in the registry, the function exits early without writing.
|
|
147
|
+
* - Errors are silently caught; consider adding logging for development.
|
|
148
|
+
* - Changes are only applied if the content differs from the current file, avoiding
|
|
149
|
+
* unnecessary filesystem writes.
|
|
150
|
+
* - The file starts with a comment indicating it is auto-generated by CasperContext.
|
|
151
|
+
*
|
|
152
|
+
* @example
|
|
153
|
+
* ```js
|
|
154
|
+
* const virtualRegistry = {
|
|
155
|
+
* 'abc123': {
|
|
156
|
+
* varNames: ['count', 'enabled'],
|
|
157
|
+
* ctxName: '_CCTX_abc123',
|
|
158
|
+
* defaults: { count: 0, enabled: true }
|
|
159
|
+
* }
|
|
160
|
+
* };
|
|
161
|
+
* generateLintGlobalJS(virtualRegistry);
|
|
162
|
+
* // Writes ESLint globals file:
|
|
163
|
+
* // module.exports = {
|
|
164
|
+
* // "count": "readonly",
|
|
165
|
+
* // "enabled": "readonly"
|
|
166
|
+
* // };
|
|
167
|
+
* ```
|
|
168
|
+
*/
|
|
169
|
+
function generateLintGlobalJS(virtualRegistry, prevVirtualRegistrySnap) {
|
|
170
|
+
try {
|
|
171
|
+
if (Object.keys(virtualRegistry).length === 0) return;
|
|
172
|
+
const currentCnt = _fs.default.readFileSync(_utilityHelpers.ESLINT_GLOBAL_JS_PATH, _constants.UNICODE_UTF8);
|
|
173
|
+
const content = Object.values(virtualRegistry).flatMap(entry => entry.varNames || []).map(name => ` "${name}": "${_constants.CASPER_READ_ONLY_TYPE}"`).join(',\n');
|
|
174
|
+
const fileContent = `// ****** generated by CasperContext ******
|
|
175
|
+
module.exports = {\n${content}\n};\n`;
|
|
176
|
+
if (currentCnt !== fileContent) {
|
|
177
|
+
_fs.default.writeFileSync(_utilityHelpers.ESLINT_GLOBAL_JS_PATH, _constants._CCTX_EMPTY, _constants.UNICODE_UTF8);
|
|
178
|
+
_fs.default.writeFileSync(_utilityHelpers.ESLINT_GLOBAL_JS_PATH, fileContent, _constants.UNICODE_UTF8);
|
|
179
|
+
_fs.default.utimesSync(_path.default.join(process.cwd(), _constants.ESLINT_RC_FILE), new Date(), new Date());
|
|
180
|
+
}
|
|
181
|
+
} catch (e) {
|
|
182
|
+
(0, _utilityHelpers.log)('error', '[::generateLintGlobalJS::]', e.message);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Performs post-processing steps after AST transformations on a file.
|
|
188
|
+
*
|
|
189
|
+
* This function generates or updates auxiliary files based on the current
|
|
190
|
+
* state of the virtual registry. Specifically:
|
|
191
|
+
* 1. `generateContextContent` – creates the module exporting React context
|
|
192
|
+
* instances with default values.
|
|
193
|
+
* 2. `generateLintGlobalJS` – updates the ESLint global variables file
|
|
194
|
+
* with read-only entries for all registered variables.
|
|
195
|
+
*
|
|
196
|
+
* @param {string} file - The path of the file that was processed. Currently unused,
|
|
197
|
+
* but provided for future enhancements or logging purposes.
|
|
198
|
+
* @param {Object} t - Babel types helper (`@babel/types`) used in AST operations.
|
|
199
|
+
* @param {Object<string, Object>} virtualRegistry - The in-memory registry
|
|
200
|
+
* of component/context variables, structured as:
|
|
201
|
+
* ```js
|
|
202
|
+
* {
|
|
203
|
+
* [componentHash]: {
|
|
204
|
+
* ctxName: string,
|
|
205
|
+
* varNames: string[],
|
|
206
|
+
* defaults: Record<string, any>
|
|
207
|
+
* }
|
|
208
|
+
* }
|
|
209
|
+
* ```
|
|
210
|
+
* @param {Object<string, Object>} [prevVirtualRegistrySnap] - Optional snapshot
|
|
211
|
+
* of the previous virtual registry state, used to detect changes and optimize writes.
|
|
212
|
+
*
|
|
213
|
+
* @returns {void}
|
|
214
|
+
* This function does not return a value; its effect is to update filesystem
|
|
215
|
+
* files (`CONTEXT_FILE_PATH` and `ESLINT_GLOBAL_JS_PATH`) based on the registry.
|
|
216
|
+
*
|
|
217
|
+
* @important
|
|
218
|
+
* - Errors are silently caught; consider adding logging for debugging.
|
|
219
|
+
* - Both `generateContextContent` and `generateLintGlobalJS` may overwrite
|
|
220
|
+
* existing files.
|
|
221
|
+
* - The `file` parameter is not currently used internally but is included
|
|
222
|
+
* for potential future processing logic.
|
|
223
|
+
*
|
|
224
|
+
* @example
|
|
225
|
+
* ```js
|
|
226
|
+
* import postProcess from './postProcess';
|
|
227
|
+
*
|
|
228
|
+
* postProcess('src/App.js', t, virtualRegistry, prevVirtualRegistrySnap);
|
|
229
|
+
* // Generates context and ESLint globals files based on the current registry
|
|
230
|
+
* ```
|
|
231
|
+
*/
|
|
232
|
+
function postProcess(file, t, virtualRegistry, prevVirtualRegistrySnap) {
|
|
233
|
+
try {
|
|
234
|
+
generateContextContent(virtualRegistry);
|
|
235
|
+
generateLintGlobalJS(virtualRegistry, prevVirtualRegistrySnap);
|
|
236
|
+
} catch (e) {
|
|
237
|
+
(0, _utilityHelpers.log)('error', '[::postProcess::]', e.message);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = preProcess;
|
|
7
|
+
var _fs = _interopRequireDefault(require("fs"));
|
|
8
|
+
var _path = _interopRequireDefault(require("path"));
|
|
9
|
+
var _utilityHelpers = require("../utils/utilityHelpers");
|
|
10
|
+
var _constants = require("../utils/constants");
|
|
11
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
12
|
+
/**
|
|
13
|
+
* @fileoverview File System Orchestration for Casper Context.
|
|
14
|
+
* This module manages the lifecycle of the generated context files, handling
|
|
15
|
+
* the reading, writing, and directory verification required to sync the
|
|
16
|
+
* Babel transformation with the physical project structure.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Path & State Dependencies
|
|
21
|
+
* @description
|
|
22
|
+
* - CONTEXT_FILE_PATH: The absolute resolved destination for the generated React Context.
|
|
23
|
+
* - _CCTX_EMPTY: Used as a safe fallback or initializer for file content buffers.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @module Logger
|
|
28
|
+
* @description Provides a controlled logging interface for the Babel transformation process.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @important
|
|
33
|
+
* All file operations in this module should prioritize synchronous methods (`fs.writeFileSync`, etc.)
|
|
34
|
+
* during the Babel build-step to ensure the file system is in sync before the
|
|
35
|
+
* bundling process (Webpack/Vite) begins.
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Initializes the scope context file and directory for Casper context tracking.
|
|
40
|
+
*
|
|
41
|
+
* This function ensures that the directory containing the context file exists,
|
|
42
|
+
* and then creates an empty context file at `CONTEXT_FILE_PATH` if it does not
|
|
43
|
+
* already exist. It is intended to prepare the filesystem for storing generated
|
|
44
|
+
* React context instances and related metadata.
|
|
45
|
+
*
|
|
46
|
+
* @returns {void}
|
|
47
|
+
* This function does not return a value; its effect is purely filesystem-based.
|
|
48
|
+
*
|
|
49
|
+
* @important
|
|
50
|
+
* - Uses `fs.mkdirSync` with `{ recursive: true }` to create parent directories as needed.
|
|
51
|
+
* - Writes an empty file at `CONTEXT_FILE_PATH` with the `{ flag: 'wx' }` option,
|
|
52
|
+
* which means the file is only created if it does not already exist.
|
|
53
|
+
* - Errors are silently caught, so existing files or directories will not cause failures.
|
|
54
|
+
* - Intended to be called before processing files (`preProcess`) or generating context content.
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```js
|
|
58
|
+
* createScopeContext();
|
|
59
|
+
* // Ensures the context directory and empty context file exist
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
function createScopeContext() {
|
|
63
|
+
try {
|
|
64
|
+
const dirPath = _path.default.dirname(_utilityHelpers.CONTEXT_FILE_PATH);
|
|
65
|
+
_fs.default.mkdirSync(dirPath, {
|
|
66
|
+
recursive: true
|
|
67
|
+
});
|
|
68
|
+
_fs.default.writeFileSync(_utilityHelpers.CONTEXT_FILE_PATH, _constants._CCTX_EMPTY, {
|
|
69
|
+
flag: 'wx'
|
|
70
|
+
});
|
|
71
|
+
} catch (e) {
|
|
72
|
+
(0, _utilityHelpers.log)('error', '[::createScopeContext::]', e.message);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Performs pre-processing steps before AST transformations on a file.
|
|
78
|
+
*
|
|
79
|
+
* This function sets up the necessary runtime or plugin context before
|
|
80
|
+
* any file transformations occur. Currently, it calls `createScopeContext()`
|
|
81
|
+
* to initialize the global scope or virtual registry for tracking component
|
|
82
|
+
* variables and contexts.
|
|
83
|
+
*
|
|
84
|
+
* @param {string} file - The path of the file that will be processed. Currently unused,
|
|
85
|
+
* but provided for future enhancements or logging purposes.
|
|
86
|
+
* @param {Object} t - Babel types helper (`@babel/types`) used for AST operations.
|
|
87
|
+
*
|
|
88
|
+
* @returns {void}
|
|
89
|
+
* This function does not return a value; its effect is to initialize internal
|
|
90
|
+
* context structures for later processing.
|
|
91
|
+
*
|
|
92
|
+
* @important
|
|
93
|
+
* - Errors are silently caught; consider adding logging for debugging.
|
|
94
|
+
* - Currently, the function only calls `createScopeContext()`, but can be
|
|
95
|
+
* extended for additional pre-processing tasks.
|
|
96
|
+
* - The `file` parameter is not used internally but may be relevant in
|
|
97
|
+
* future versions for file-specific setup.
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* ```js
|
|
101
|
+
* import preProcess from './preProcess';
|
|
102
|
+
*
|
|
103
|
+
* preProcess('src/App.js', t);
|
|
104
|
+
* // Initializes context for virtual registry and component tracking
|
|
105
|
+
* ```
|
|
106
|
+
*/
|
|
107
|
+
function preProcess(file, t) {
|
|
108
|
+
try {
|
|
109
|
+
createScopeContext();
|
|
110
|
+
} catch (e) {
|
|
111
|
+
(0, _utilityHelpers.log)('error', '[::preProcess::]', e.message);
|
|
112
|
+
}
|
|
113
|
+
}
|
package/dist/plugin.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = _default;
|
|
7
|
+
var _constants = require("./utils/constants");
|
|
8
|
+
var _pre = _interopRequireDefault(require("./lifecycle/pre"));
|
|
9
|
+
var _post = _interopRequireDefault(require("./lifecycle/post"));
|
|
10
|
+
var _VariableDeclaration = _interopRequireDefault(require("./visitors/VariableDeclaration"));
|
|
11
|
+
var _AssignmentExpression = _interopRequireDefault(require("./visitors/AssignmentExpression"));
|
|
12
|
+
var _Identifier = _interopRequireDefault(require("./visitors/Identifier"));
|
|
13
|
+
var _Program = require("./visitors/Program");
|
|
14
|
+
var _FunctionDeclaration = require("./visitors/FunctionDeclaration");
|
|
15
|
+
var _utilityHelpers = require("./utils/utilityHelpers");
|
|
16
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
17
|
+
const virtualRegistry = {};
|
|
18
|
+
const prevVirtualRegistrySnap = {};
|
|
19
|
+
function _default({
|
|
20
|
+
types: t
|
|
21
|
+
}) {
|
|
22
|
+
const config = (0, _utilityHelpers.readCasperConfig)();
|
|
23
|
+
const seen = new WeakSet();
|
|
24
|
+
return {
|
|
25
|
+
name: _constants.PLUGIN_NAME,
|
|
26
|
+
pre(file) {
|
|
27
|
+
_pre.default.call(this, file, t);
|
|
28
|
+
},
|
|
29
|
+
visitor: {
|
|
30
|
+
Program: {
|
|
31
|
+
enter(path, state) {
|
|
32
|
+
_Program.programEnter.call(this, path, state, t, virtualRegistry, config);
|
|
33
|
+
},
|
|
34
|
+
exit(path, state) {
|
|
35
|
+
_Program.programExit.call(this, path, state, t);
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
VariableDeclarator(path, state) {
|
|
39
|
+
_VariableDeclaration.default.call(this, path, state, t, virtualRegistry);
|
|
40
|
+
},
|
|
41
|
+
AssignmentExpression(path, state) {
|
|
42
|
+
_AssignmentExpression.default.call(this, path, state, t, virtualRegistry);
|
|
43
|
+
},
|
|
44
|
+
Identifier(path, state) {
|
|
45
|
+
_Identifier.default.call(this, path, state, t, seen, virtualRegistry);
|
|
46
|
+
},
|
|
47
|
+
FunctionDeclaration: {
|
|
48
|
+
exit(path, state) {
|
|
49
|
+
_FunctionDeclaration.functionDeclarationExit.call(this, path, state, t, virtualRegistry);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
post(file) {
|
|
54
|
+
_post.default.call(this, file, t, virtualRegistry, prevVirtualRegistrySnap);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";
|