@oamm/textor 1.0.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 +399 -0
- package/dist/bin/textor.d.ts +2 -0
- package/dist/bin/textor.js +3819 -0
- package/dist/bin/textor.js.map +1 -0
- package/dist/index.cjs +3202 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.js +3190 -0
- package/dist/index.js.map +1 -0
- package/package.json +71 -0
package/README.md
ADDED
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
# Textor
|
|
2
|
+
|
|
3
|
+
A safe, deterministic scaffolding and refactoring CLI tool for Astro + modern frontend projects.
|
|
4
|
+
|
|
5
|
+
Textor enforces strict separation of concerns:
|
|
6
|
+
- **Pages** = Thin routing adapters
|
|
7
|
+
- **Features** = Self-contained implementation modules
|
|
8
|
+
- **Components** = Reusable UI primitives
|
|
9
|
+
|
|
10
|
+
The tool is designed to be production-grade, principal-engineer approved, and optimized for large teams and long-lived codebases.
|
|
11
|
+
|
|
12
|
+
## π Core Principles
|
|
13
|
+
|
|
14
|
+
1. **Explicit over implicit** β No magic behavior.
|
|
15
|
+
2. **Safe by default** β Never delete or overwrite hand-written code.
|
|
16
|
+
3. **Configurable over hardcoded** β Everything driven by configuration.
|
|
17
|
+
4. **Reversible operations** β Add, move, and remove operations are always supported.
|
|
18
|
+
5. **Deterministic output** β Same input always produces the same structure.
|
|
19
|
+
|
|
20
|
+
## π Installation
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
# In your Astro project
|
|
24
|
+
pnpm add -D @oamm/textor
|
|
25
|
+
|
|
26
|
+
# Initialize configuration
|
|
27
|
+
pnpm textor init
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
This creates .textor/config.json with default settings and initializes the state tracker.
|
|
31
|
+
|
|
32
|
+
## ποΈ Scaffolding Presets
|
|
33
|
+
|
|
34
|
+
Control structural complexity using presets. Presets apply to both add-section and create-component.
|
|
35
|
+
|
|
36
|
+
- **minimal**: Lean defaults, minimal folders. Ideal for simple components and features.
|
|
37
|
+
- **standard**: Balanced structure. Includes hooks, tests, and types by default.
|
|
38
|
+
- **senior**: Full enterprise-grade layout including API, services, schemas, and documentation.
|
|
39
|
+
|
|
40
|
+
Select a preset via the --preset flag:
|
|
41
|
+
```bash
|
|
42
|
+
pnpm textor add-section /users users/catalog --preset senior
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## πΊοΈ Routing Mode
|
|
46
|
+
|
|
47
|
+
Textor supports explicit routing strategies to avoid ambiguity in Astro projects.
|
|
48
|
+
|
|
49
|
+
- **flat**: /users β src/pages/users.astro (Default)
|
|
50
|
+
- **nested**: /users β src/pages/users/index.astro
|
|
51
|
+
|
|
52
|
+
Configure this in .textor/config.json:
|
|
53
|
+
```json
|
|
54
|
+
{
|
|
55
|
+
"routing": {
|
|
56
|
+
"mode": "nested",
|
|
57
|
+
"indexFile": "index.astro"
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Textor supports multiple frameworks (React, Astro). By default, components are created as React components (`.tsx`), while features and pages are Astro components (`.astro`).
|
|
63
|
+
|
|
64
|
+
Configure this in `.textor/config.json`:
|
|
65
|
+
```json
|
|
66
|
+
{
|
|
67
|
+
"components": {
|
|
68
|
+
"framework": "react"
|
|
69
|
+
},
|
|
70
|
+
"features": {
|
|
71
|
+
"framework": "astro"
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## π¦ Feature Entry Strategy
|
|
77
|
+
|
|
78
|
+
Avoid filename collisions for feature entry files.
|
|
79
|
+
|
|
80
|
+
- **pascal**: src/features/users/catalog/UsersCatalog.astro (Default)
|
|
81
|
+
- **index**: src/features/users/catalog/index.astro
|
|
82
|
+
|
|
83
|
+
Configure this in .textor/config.json:
|
|
84
|
+
```json
|
|
85
|
+
{
|
|
86
|
+
"features": {
|
|
87
|
+
"entry": "pascal"
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## π‘οΈ Safety & File Tracking
|
|
93
|
+
|
|
94
|
+
Textor uses a multi-layered safety approach to protect your codebase.
|
|
95
|
+
|
|
96
|
+
### 1. File Signatures
|
|
97
|
+
Every file generated by Textor includes a signature (e.g., <!-- @generated by Textor -->). Textor will refuse to delete or move any file missing this signature unless --force is used.
|
|
98
|
+
|
|
99
|
+
### 2. State Tracking & Hashing
|
|
100
|
+
Textor maintains a .textor/state.json file that tracks:
|
|
101
|
+
- File paths and kind (route, feature, component)
|
|
102
|
+
- Templates used
|
|
103
|
+
- **Content hashes**
|
|
104
|
+
- Creation timestamps
|
|
105
|
+
|
|
106
|
+
### 3. Integrity Verification
|
|
107
|
+
Safe deletion and moves require:
|
|
108
|
+
1. Valid Textor signature in the file.
|
|
109
|
+
2. Presence of the file in the state.
|
|
110
|
+
3. **Hash match** (verifying the file hasn't been manually edited).
|
|
111
|
+
|
|
112
|
+
If you have manually edited a Textor-generated file and wish to remove or move it, use --accept-changes or --force.
|
|
113
|
+
|
|
114
|
+
## π οΈ Commands
|
|
115
|
+
|
|
116
|
+
### add-section
|
|
117
|
+
Create a route + feature binding.
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
pnpm textor add-section <route> <featurePath> [options]
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**Options:**
|
|
124
|
+
- --preset <name>: scaffolding preset (minimal, standard, senior)
|
|
125
|
+
- --layout <name>: Layout component name (use "none" for no layout)
|
|
126
|
+
- --name <name>: Custom name for easier state lookup
|
|
127
|
+
- --endpoint: Create an API endpoint (.ts) instead of an Astro page
|
|
128
|
+
- --dry-run: Preview changes without writing to disk
|
|
129
|
+
|
|
130
|
+
### create-component
|
|
131
|
+
Create reusable UI components.
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
pnpm textor create-component <ComponentName> [options]
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
**Options:**
|
|
138
|
+
- --preset <name>: scaffolding preset (minimal, standard, senior)
|
|
139
|
+
- --dry-run: Preview changes without writing to disk
|
|
140
|
+
|
|
141
|
+
### move-section
|
|
142
|
+
Move and rename sections safely. Textor performs **deep renaming**: if the feature name changes, all internal files and component signatures are updated automatically.
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
# Using state lookup
|
|
146
|
+
pnpm textor move-section /old /new
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### remove-section / remove-component
|
|
150
|
+
Safely remove Textor-managed modules.
|
|
151
|
+
|
|
152
|
+
### list-sections
|
|
153
|
+
List all Textor-managed modules, including their architectural capabilities (API, Hooks, etc.).
|
|
154
|
+
|
|
155
|
+
### status
|
|
156
|
+
Show drift between state and disk. This is a read-only command used to build trust and identify manual changes or missing files.
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
pnpm textor status
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
**Categories:**
|
|
163
|
+
- **SYNCED**: File content matches state exactly.
|
|
164
|
+
- **MODIFIED**: File exists but content differs from state hash.
|
|
165
|
+
- **MISSING**: File is registered in state but missing on disk.
|
|
166
|
+
- **UNTRACKED**: File has a Textor signature but is not in state.
|
|
167
|
+
- **ORPHANED**: File is in a managed directory but has no Textor signature and is not in state.
|
|
168
|
+
|
|
169
|
+
### validate-state
|
|
170
|
+
Validate that the state file matches the project files.
|
|
171
|
+
|
|
172
|
+
### sync
|
|
173
|
+
Synchronize the state with the actual files in managed directories. This is useful for including existing files into Textor's state or updating hashes after manual edits.
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
pnpm textor sync [options]
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
**Options:**
|
|
180
|
+
- `--include-all`: Include all files in managed directories, even without the Textor signature.
|
|
181
|
+
- `--force`: Update hashes for modified files even if they don't have the Textor signature.
|
|
182
|
+
- `--dry-run`: Preview what would be synchronized without making changes.
|
|
183
|
+
|
|
184
|
+
### adopt
|
|
185
|
+
Adopt untracked files into Textor's state and add the Textor signature to them. This is the recommended way to bring manually created components or sections under Textor's management.
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
pnpm textor adopt <path-or-identifier> [options]
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
**Options:**
|
|
192
|
+
- `--all`: Scan all managed directories for untracked files and adopt them.
|
|
193
|
+
- `--dry-run`: Preview which files would be adopted and modified.
|
|
194
|
+
|
|
195
|
+
**Examples:**
|
|
196
|
+
```bash
|
|
197
|
+
# Adopt a specific component directory
|
|
198
|
+
pnpm textor adopt src/components/MyNewButton
|
|
199
|
+
|
|
200
|
+
# Adopt a section by its route
|
|
201
|
+
pnpm textor adopt /users
|
|
202
|
+
|
|
203
|
+
# Adopt all untracked files in the project
|
|
204
|
+
pnpm textor adopt --all
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## ποΈ Technical Architecture
|
|
208
|
+
|
|
209
|
+
Textor is designed with enterprise-grade robustness, moving beyond simple scaffolding to provide a reliable refactoring engine.
|
|
210
|
+
|
|
211
|
+
### 1. Atomic State Operations
|
|
212
|
+
State updates to `.textor/state.json` are atomic. Textor writes to a temporary file (`state.json.tmp`) and performs a cross-platform rename to the final destination. This prevents state corruption during crashes or interrupted operations.
|
|
213
|
+
|
|
214
|
+
### 2. Robust Hashing & Normalization
|
|
215
|
+
Textor uses SHA-256 for file integrity. It supports multiple normalization strategies:
|
|
216
|
+
- **normalizeEOL** (Default): Converts `\r\n` to `\n` before hashing. Ensures Git "autocrlf" settings don't trigger false alerts.
|
|
217
|
+
- **stripGeneratedRegions**: Hashes only the content within `@generated by Textor:begin` and `@generated by Textor:end` markers. This allows developers to add hand-written code outside these regions without breaking Textor's integrity checks.
|
|
218
|
+
- **none**: Strict hashing of the entire file.
|
|
219
|
+
|
|
220
|
+
### 3. Explicit Ownership Model
|
|
221
|
+
Every file in the state has an **owner**.
|
|
222
|
+
- A **Section** owns its route adapter and its associated feature directory.
|
|
223
|
+
- A **Component** owns its specific component directory.
|
|
224
|
+
Textor enforces ownership boundaries to prevent accidental deletion of shared resources and to ensure deep refactors only touch relevant files. Use `--force` to override ownership checks.
|
|
225
|
+
|
|
226
|
+
### 4. Kind Inference & Rules
|
|
227
|
+
Textor can infer the "kind" of a file (e.g., `route`, `feature`, `component-file`) during synchronization. You can define custom rules in `.textor/config.json`:
|
|
228
|
+
```json
|
|
229
|
+
{
|
|
230
|
+
"kindRules": [
|
|
231
|
+
{ "match": "src/features/custom/**", "kind": "custom-logic" },
|
|
232
|
+
{ "match": "**/special.ts", "kind": "special-file" }
|
|
233
|
+
]
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### 5. Git Safety Integration
|
|
238
|
+
Textor integrates with Git to provide a "safety net":
|
|
239
|
+
- `requireCleanRepo`: When enabled, Textor refuses to perform destructive operations (remove/move) if the repository has uncommitted changes.
|
|
240
|
+
- `stageChanges`: When enabled, Textor automatically stages (`git add`) all created or modified files after a successful command.
|
|
241
|
+
|
|
242
|
+
### 6. Scoped & Repo-wide Import Rewriting
|
|
243
|
+
When moving or renaming sections, Textor performs scoped AST-like updates:
|
|
244
|
+
- **Scope**: Updates imports in Textor-managed files and route adapters by default.
|
|
245
|
+
- **Repo-wide**: Use the `--scan` flag to scan the entire repository for imports that need to be updated.
|
|
246
|
+
- **Exclusions**: String literals, markdown documentation (unless registered), and complex dynamic imports are preserved to avoid breaking hand-written logic.
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
## βοΈ Configuration
|
|
251
|
+
|
|
252
|
+
The .textor/config.json file allows full control over the tool's behavior.
|
|
253
|
+
|
|
254
|
+
```json
|
|
255
|
+
{
|
|
256
|
+
"paths": {
|
|
257
|
+
"pages": "src/pages",
|
|
258
|
+
"features": "src/features",
|
|
259
|
+
"components": "src/components",
|
|
260
|
+
"layouts": "src/layouts"
|
|
261
|
+
},
|
|
262
|
+
"routing": {
|
|
263
|
+
"mode": "flat",
|
|
264
|
+
"indexFile": "index.astro"
|
|
265
|
+
},
|
|
266
|
+
"importAliases": {
|
|
267
|
+
},
|
|
268
|
+
"naming": {
|
|
269
|
+
"routeExtension": ".astro",
|
|
270
|
+
"featureExtension": ".astro",
|
|
271
|
+
"componentExtension": ".tsx",
|
|
272
|
+
"hookExtension": ".ts",
|
|
273
|
+
"testExtension": ".test.tsx"
|
|
274
|
+
},
|
|
275
|
+
"signatures": {
|
|
276
|
+
"astro": "<!-- @generated by Textor -->",
|
|
277
|
+
"typescript": "// @generated by Textor",
|
|
278
|
+
"javascript": "// @generated by Textor",
|
|
279
|
+
"tsx": "// @generated by Textor"
|
|
280
|
+
},
|
|
281
|
+
"features": {
|
|
282
|
+
"framework": "astro",
|
|
283
|
+
"entry": "pascal",
|
|
284
|
+
"createSubComponentsDir": true,
|
|
285
|
+
"createScriptsDir": true,
|
|
286
|
+
"scriptsIndexFile": "scripts/index.ts",
|
|
287
|
+
"createApi": false,
|
|
288
|
+
"createServices": false,
|
|
289
|
+
"createSchemas": false,
|
|
290
|
+
"createHooks": false,
|
|
291
|
+
"createContext": false,
|
|
292
|
+
"createTests": false,
|
|
293
|
+
"createTypes": false,
|
|
294
|
+
"createReadme": false,
|
|
295
|
+
"createStories": false,
|
|
296
|
+
"createIndex": false
|
|
297
|
+
},
|
|
298
|
+
"components": {
|
|
299
|
+
"framework": "react",
|
|
300
|
+
"createSubComponentsDir": true,
|
|
301
|
+
"createContext": true,
|
|
302
|
+
"createHook": true,
|
|
303
|
+
"createTests": true,
|
|
304
|
+
"createConfig": true,
|
|
305
|
+
"createConstants": true,
|
|
306
|
+
"createTypes": true,
|
|
307
|
+
"createApi": false,
|
|
308
|
+
"createServices": false,
|
|
309
|
+
"createSchemas": false,
|
|
310
|
+
"createReadme": false,
|
|
311
|
+
"createStories": false
|
|
312
|
+
},
|
|
313
|
+
"formatting": {
|
|
314
|
+
"tool": "none"
|
|
315
|
+
},
|
|
316
|
+
"hashing": {
|
|
317
|
+
"normalization": "normalizeEOL"
|
|
318
|
+
},
|
|
319
|
+
"git": {
|
|
320
|
+
"requireCleanRepo": false,
|
|
321
|
+
"stageChanges": false
|
|
322
|
+
},
|
|
323
|
+
"defaultPreset": "standard",
|
|
324
|
+
"presets": {
|
|
325
|
+
"minimal": {
|
|
326
|
+
"features": { "createSubComponentsDir": false, "createScriptsDir": false },
|
|
327
|
+
"components": {
|
|
328
|
+
"createSubComponentsDir": false,
|
|
329
|
+
"createContext": false,
|
|
330
|
+
"createHook": false,
|
|
331
|
+
"createTests": false,
|
|
332
|
+
"createConfig": false,
|
|
333
|
+
"createConstants": false,
|
|
334
|
+
"createTypes": false
|
|
335
|
+
}
|
|
336
|
+
},
|
|
337
|
+
"standard": {
|
|
338
|
+
"features": { "createSubComponentsDir": true, "createScriptsDir": true },
|
|
339
|
+
"components": {
|
|
340
|
+
"createSubComponentsDir": true,
|
|
341
|
+
"createContext": true,
|
|
342
|
+
"createHook": true,
|
|
343
|
+
"createTests": true,
|
|
344
|
+
"createConfig": true,
|
|
345
|
+
"createConstants": true,
|
|
346
|
+
"createTypes": true
|
|
347
|
+
}
|
|
348
|
+
},
|
|
349
|
+
"senior": {
|
|
350
|
+
"features": {
|
|
351
|
+
"createSubComponentsDir": true,
|
|
352
|
+
"createScriptsDir": true,
|
|
353
|
+
"createApi": true,
|
|
354
|
+
"createServices": true,
|
|
355
|
+
"createSchemas": true,
|
|
356
|
+
"createHooks": true,
|
|
357
|
+
"createContext": true,
|
|
358
|
+
"createTests": true,
|
|
359
|
+
"createTypes": true,
|
|
360
|
+
"createReadme": true,
|
|
361
|
+
"createStories": true,
|
|
362
|
+
"createIndex": true
|
|
363
|
+
},
|
|
364
|
+
"components": {
|
|
365
|
+
"createSubComponentsDir": true,
|
|
366
|
+
"createContext": true,
|
|
367
|
+
"createHook": true,
|
|
368
|
+
"createTests": true,
|
|
369
|
+
"createConfig": true,
|
|
370
|
+
"createConstants": true,
|
|
371
|
+
"createTypes": true,
|
|
372
|
+
"createApi": true,
|
|
373
|
+
"createServices": true,
|
|
374
|
+
"createSchemas": true,
|
|
375
|
+
"createReadme": true,
|
|
376
|
+
"createStories": true
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
```
|
|
382
|
+
*Supported formatting tools: prettier, biome, none.*
|
|
383
|
+
|
|
384
|
+
## π Template Overrides
|
|
385
|
+
|
|
386
|
+
Customize generated code by placing templates in .textor/templates/. Supported templates:
|
|
387
|
+
- route.astro
|
|
388
|
+
- feature.astro
|
|
389
|
+
- component.astro
|
|
390
|
+
- hook.ts
|
|
391
|
+
- context.tsx
|
|
392
|
+
- test.tsx
|
|
393
|
+
- index.ts
|
|
394
|
+
|
|
395
|
+
Templates use {{variable}} syntax for placeholder replacement.
|
|
396
|
+
|
|
397
|
+
---
|
|
398
|
+
|
|
399
|
+
Textor is designed to be a tool you trust to refactor a 3-year-old production codebase without fear.
|