@lowdefy/codemods 0.0.0-experimental-20260318092212

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.
@@ -0,0 +1,149 @@
1
+ # Migration: `meta.styles` → Direct CSS Imports (Custom Block Plugins)
2
+
3
+ ## Context
4
+
5
+ The `meta.styles` pipeline is removed in Lowdefy v4.8. Custom block plugins that used `meta.styles = ['style.less']` to register stylesheets must switch to direct `import './style.css'` statements in the block component file.
6
+
7
+ This migration targets local plugin source code (JS/TS), not YAML configs.
8
+
9
+ ## Scope
10
+
11
+ `plugins` — scan JS/TS files in local plugin directories.
12
+
13
+ ## What to Do
14
+
15
+ For each custom block plugin:
16
+
17
+ ### 1. Remove `meta.styles` from block component files
18
+
19
+ Find lines like:
20
+
21
+ ```javascript
22
+ BlockName.meta = {
23
+ category: 'display',
24
+ styles: ['style.less'],
25
+ };
26
+ ```
27
+
28
+ Remove the `styles` entry:
29
+
30
+ ```javascript
31
+ BlockName.meta = {
32
+ category: 'display',
33
+ };
34
+ ```
35
+
36
+ ### 2. Remove styles aggregation from `types.js`
37
+
38
+ Find patterns like:
39
+
40
+ ```javascript
41
+ const styles = {};
42
+ styles.BlockName = Block.meta.styles;
43
+ export { styles };
44
+ ```
45
+
46
+ Remove the `styles` variable, the loop/assignments, and the `styles` export.
47
+
48
+ ### 3. Handle `.less` files
49
+
50
+ For each `.less` file referenced by `meta.styles`:
51
+
52
+ **Dead imports (remove):** If the `.less` file only imports antd's Less (`@import '~antd/dist/antd.less'` or similar) with no other CSS, delete it.
53
+
54
+ **Convertible (rename to .css):** If the `.less` file contains plain CSS without Less-specific syntax:
55
+
56
+ 1. Remove any antd `@import` lines
57
+ 2. Wrap the remaining CSS in `@layer components { }`
58
+ 3. Save as `.css` (same name, different extension)
59
+ 4. Delete the old `.less` file
60
+
61
+ **Complex Less (manual):** If the file uses Less variables (`@var`), functions (`darken()`, `lighten()`), mixins, or nested `&` selectors — convert to plain CSS manually first.
62
+
63
+ ### 4. Add direct CSS import
64
+
65
+ In the block component file, add:
66
+
67
+ ```javascript
68
+ import './style.css';
69
+ ```
70
+
71
+ ## Files to Check
72
+
73
+ Glob in plugin directories: `**/*.{js,jsx,ts,tsx}`
74
+ Grep: `meta.styles` or `styles:.*\.less`
75
+
76
+ Also check: `**/types.js` for styles aggregation.
77
+
78
+ ## Examples
79
+
80
+ ### Before — block component
81
+
82
+ ```javascript
83
+ import React from 'react';
84
+ import './MyBlock.less';
85
+
86
+ const MyBlock = ({ properties }) => {
87
+ return <div>{properties.title}</div>;
88
+ };
89
+
90
+ MyBlock.meta = {
91
+ category: 'display',
92
+ styles: ['MyBlock.less'],
93
+ };
94
+
95
+ export default MyBlock;
96
+ ```
97
+
98
+ ### After
99
+
100
+ ```javascript
101
+ import React from 'react';
102
+ import './MyBlock.css';
103
+
104
+ const MyBlock = ({ properties }) => {
105
+ return <div>{properties.title}</div>;
106
+ };
107
+
108
+ MyBlock.meta = {
109
+ category: 'display',
110
+ };
111
+
112
+ export default MyBlock;
113
+ ```
114
+
115
+ ### Before — MyBlock.less
116
+
117
+ ```less
118
+ @import '~antd/dist/antd.less';
119
+
120
+ .my-block {
121
+ padding: 16px;
122
+ border: 1px solid #d9d9d9;
123
+ }
124
+ ```
125
+
126
+ ### After — MyBlock.css
127
+
128
+ ```css
129
+ @layer components {
130
+ .my-block {
131
+ padding: 16px;
132
+ border: 1px solid #d9d9d9;
133
+ }
134
+ }
135
+ ```
136
+
137
+ ## Edge Cases
138
+
139
+ - Some plugins may have `.less` files that import from multiple antd sub-paths — remove all antd imports
140
+ - Check for Less syntax before auto-converting: `@` variables (not `@import` or `@layer`), `darken()`, `lighten()`, `fade()`, `.mixin()`, `& .child` nesting
141
+ - The `import './style.css'` line may already exist if the plugin was partially migrated
142
+ - `meta.styles` may be an empty array — just remove it
143
+
144
+ ## Verification
145
+
146
+ 1. No `.less` files should be imported by block components
147
+ 2. No `meta.styles` assignments should remain
148
+ 3. No `styles` exports in `types.js`
149
+ 4. The plugin should build without Less-related errors
@@ -0,0 +1,39 @@
1
+ # Prompt 01: Rename `xxl` to `2xl`
2
+
3
+ Complexity: **Moderate** (false positive risk — `xxl` may appear as block IDs or custom data)
4
+
5
+ ## Goal
6
+
7
+ Rename all `xxl` breakpoint references to `2xl` in Lowdefy YAML configs. This covers YAML keys, quoted string values, and bare values.
8
+
9
+ ## Scope
10
+
11
+ Search all `.yaml` and `.yml` files in the project (excluding `.` directories and `node_modules`).
12
+
13
+ ## What to Find
14
+
15
+ 1. **YAML keys** -- `xxl:` at the start of a line (after optional whitespace). Examples: ` xxl:`, `xxl: { span: 4 }`
16
+ 2. **Quoted strings** -- `"xxl"` or `'xxl'` as values (typically `_media` comparisons like `_eq: [_media: size, "xxl"]`)
17
+ 3. **Bare values** -- `xxl` as an unquoted value after `- ` or `: ` at end of line
18
+
19
+ ## How to Apply
20
+
21
+ For each file with matches:
22
+
23
+ 1. Show the user every matching line with its line number and file path.
24
+ 2. Ask the user to confirm no matches are false positives (block IDs, custom properties named `xxl`, or string data unrelated to breakpoints).
25
+ 3. After confirmation, apply these replacements in each file:
26
+ - `xxl:` key at start of line (preserving indent) becomes `2xl:`
27
+ - `"xxl"` becomes `"2xl"`
28
+ - `'xxl'` becomes `'2xl'`
29
+ - Bare `xxl` after `- ` or `: ` at end of line becomes `2xl`
30
+
31
+ ## False Positive Checklist
32
+
33
+ - A YAML key `xxl:` that is a block ID or custom property name (not a breakpoint override)
34
+ - A string value `"xxl"` stored as data (not a `_media` comparison)
35
+ - Any `xxl` inside comments (leave comments alone -- they are documentation)
36
+
37
+ ## Completion
38
+
39
+ Report the number of files modified and replacements made.
@@ -0,0 +1,78 @@
1
+ # Migration: Rename `gutter` → `gap`
2
+
3
+ ## Context
4
+
5
+ The `gutter` property on layout slots is renamed to `gap` in the new Tailwind-compatible grid. The `contentGutter` property (parent layout content gap inheritance) is also renamed — directly to `gap` (the `content*` prefix is dropped, see prompt 03).
6
+
7
+ ## What to Do
8
+
9
+ Apply these renames in all YAML files:
10
+
11
+ | Find | Replace |
12
+ | ---------------- | ------- |
13
+ | `gutter:` | `gap:` |
14
+ | `contentGutter:` | `gap:` |
15
+
16
+ Order matters: replace `contentGutter:` before `gutter:` to avoid partial matching.
17
+
18
+ No user confirmation needed — these renames are unambiguous. The `gutter` property name does not exist in any other Lowdefy context.
19
+
20
+ ## Files to Check
21
+
22
+ Glob: `**/*.{yaml,yml}`
23
+ Grep: `gutter:`
24
+
25
+ ## Examples
26
+
27
+ ### Before
28
+
29
+ ```yaml
30
+ - id: my_row
31
+ type: Box
32
+ slots:
33
+ content:
34
+ gutter: 16
35
+ blocks: [...]
36
+ ```
37
+
38
+ ### After
39
+
40
+ ```yaml
41
+ - id: my_row
42
+ type: Box
43
+ slots:
44
+ content:
45
+ gap: 16
46
+ blocks: [...]
47
+ ```
48
+
49
+ ### Before — contentGutter
50
+
51
+ ```yaml
52
+ - id: page_layout
53
+ type: PageHeaderMenu
54
+ layout:
55
+ contentGutter: 24
56
+ ```
57
+
58
+ ### After
59
+
60
+ ```yaml
61
+ - id: page_layout
62
+ type: PageHeaderMenu
63
+ layout:
64
+ gap: 24
65
+ ```
66
+
67
+ ## Edge Cases
68
+
69
+ - Do not rename `gutter` inside string values, operator expressions, or code blocks
70
+ - Only rename YAML keys (at the start of a line after whitespace), not values
71
+
72
+ ## Verification
73
+
74
+ ```
75
+ grep -rn 'gutter:' --include='*.yaml' --include='*.yml' .
76
+ ```
77
+
78
+ Should return zero matches.
@@ -0,0 +1,67 @@
1
+ # Migration: Drop `content*` Prefix from Layout Properties
2
+
3
+ ## Context
4
+
5
+ Layout properties were previously namespaced with a `content` prefix to distinguish content-area config from block-level config. The new grid system removes this distinction — the prefix is dropped.
6
+
7
+ Note: `contentGutter` is already handled by prompt 02 (renamed directly to `gap`).
8
+
9
+ ## What to Do
10
+
11
+ Apply these renames in all YAML files:
12
+
13
+ | Find | Replace |
14
+ | ------------------- | ------------ |
15
+ | `contentAlign:` | `align:` |
16
+ | `contentJustify:` | `justify:` |
17
+ | `contentDirection:` | `direction:` |
18
+ | `contentWrap:` | `wrap:` |
19
+ | `contentOverflow:` | `overflow:` |
20
+
21
+ No user confirmation needed — these renames are unambiguous. The `content*` property names do not exist in any other Lowdefy context.
22
+
23
+ ## Files to Check
24
+
25
+ Glob: `**/*.{yaml,yml}`
26
+ Grep: `contentAlign:|contentJustify:|contentDirection:|contentWrap:|contentOverflow:`
27
+
28
+ ## Examples
29
+
30
+ ### Before
31
+
32
+ ```yaml
33
+ - id: main_layout
34
+ type: PageHeaderMenu
35
+ layout:
36
+ contentAlign: middle
37
+ contentJustify: center
38
+ contentDirection: column
39
+ contentWrap: nowrap
40
+ contentOverflow: auto
41
+ ```
42
+
43
+ ### After
44
+
45
+ ```yaml
46
+ - id: main_layout
47
+ type: PageHeaderMenu
48
+ layout:
49
+ align: middle
50
+ justify: center
51
+ direction: column
52
+ wrap: nowrap
53
+ overflow: auto
54
+ ```
55
+
56
+ ## Edge Cases
57
+
58
+ - Do not rename inside string values, operator expressions, or code blocks
59
+ - Only rename YAML keys (at the start of a line after whitespace), not values
60
+
61
+ ## Verification
62
+
63
+ ```
64
+ grep -rn 'contentAlign:\|contentJustify:\|contentDirection:\|contentWrap:\|contentOverflow:' --include='*.yaml' --include='*.yml' .
65
+ ```
66
+
67
+ Should return zero matches.
@@ -0,0 +1,44 @@
1
+ # Prompt 04: Rename `layout.align` to `layout.selfAlign`
2
+
3
+ Complexity: **Moderate** (ambiguous context -- requires structural analysis)
4
+
5
+ ## Goal
6
+
7
+ Rename `align:` to `selfAlign:` when it appears as a child of a `layout:` block. This property controlled block self-alignment in the parent row; the new grid system renames it to avoid collision with content `align`.
8
+
9
+ ## Prerequisites
10
+
11
+ Run **after** prompt 03. Prompt 03 renames `contentAlign` to `align`. After that step, any `align:` that was _already_ present under `layout:` (before the content-prefix rename) is a self-alignment property and should become `selfAlign:`.
12
+
13
+ ## Scope
14
+
15
+ Search all `.yaml` and `.yml` files in the project (excluding `.` directories and `node_modules`).
16
+
17
+ ## What to Find
18
+
19
+ `align:` as a YAML key that appears indented under a `layout:` parent block. Use indentation tracking:
20
+
21
+ 1. Scan lines. When you encounter `layout:` as a key, record its indent level.
22
+ 2. Subsequent lines indented deeper than the `layout:` key are children of that block.
23
+ 3. When a non-empty, non-comment line appears at the same or lesser indentation, the `layout:` block has ended.
24
+ 4. Within the `layout:` block, any `align:` key (at child indentation) is a candidate.
25
+
26
+ Skip blank lines and comment lines when tracking block boundaries.
27
+
28
+ ## How to Apply
29
+
30
+ For each file with matches:
31
+
32
+ 1. Show the user every matching `align:` line with its line number, file path, and surrounding context (2-3 lines above and below).
33
+ 2. Explain the ambiguity: after prompt 03, both old self-alignment `align:` and newly-renamed content `align:` (from `contentAlign`) exist. Only the old self-alignment instances should become `selfAlign:`.
34
+ 3. Ask the user to confirm each match. Typical heuristic: if `align:` is directly under `layout:` on a block definition (not in a `slots:` or area config section), it is self-alignment.
35
+ 4. After confirmation, replace confirmed `align:` lines with `selfAlign:` (preserving indent).
36
+
37
+ ## False Positive Checklist
38
+
39
+ - `align:` that was just renamed from `contentAlign:` by prompt 03 (content area alignment -- should stay as `align:`)
40
+ - `align:` in area or slot config (not under `layout:` -- should stay as `align:`)
41
+
42
+ ## Completion
43
+
44
+ Report the number of files modified and replacements made.
@@ -0,0 +1,41 @@
1
+ # 01 — Rename `_moment` to `_dayjs`
2
+
3
+ **Complexity:** Simple
4
+
5
+ Rename all `_moment` operator keys to `_dayjs` across the Lowdefy app's YAML config files.
6
+
7
+ ## What to Change
8
+
9
+ The `_moment` operator is replaced by `_dayjs` in Lowdefy v5. Every YAML key that starts with `_moment` followed by `:` or `.` must be renamed to `_dayjs`.
10
+
11
+ Patterns:
12
+
13
+ | Before | After |
14
+ | --------------------------- | -------------------------- |
15
+ | `_moment:` | `_dayjs:` |
16
+ | `_moment.format:` | `_dayjs.format:` |
17
+ | `_moment.humanizeDuration:` | `_dayjs.humanizeDuration:` |
18
+ | `- _moment:` | `- _dayjs:` |
19
+ | `- _moment.diff:` | `- _dayjs.diff:` |
20
+
21
+ The rename applies to all `_moment` + method combinations. The method name itself does not change.
22
+
23
+ ## Instructions
24
+
25
+ 1. Find all `.yaml` and `.yml` files in the project (excluding `node_modules/` and hidden directories).
26
+
27
+ 2. In each file, search for `_moment` used as a YAML key — that is, `_moment` followed by either `:` (standalone operator) or `.` (operator with method). It may appear after whitespace, a list dash, or at the start of a line.
28
+
29
+ 3. Replace each `_moment` with `_dayjs`, preserving the rest of the line exactly.
30
+
31
+ 4. Report:
32
+
33
+ - Each file modified and the number of replacements in it.
34
+ - Total files modified and total replacements.
35
+
36
+ 5. If no matches are found, report: "No `_moment` usage found. Nothing to rename."
37
+
38
+ ## Scope
39
+
40
+ - Only rename YAML keys, not arbitrary string values. The pattern `_moment` inside a quoted string value (e.g., `description: "Uses _moment for dates"`) should **not** be changed.
41
+ - Do not modify files in `node_modules/`, `.git/`, or other hidden directories.
@@ -0,0 +1,37 @@
1
+ # 02 — Report `thresholds` Usage
2
+
3
+ **Complexity:** Moderate
4
+
5
+ Find `thresholds` parameter usage inside `_moment.humanizeDuration` or `_dayjs.humanizeDuration` blocks and advise on removal.
6
+
7
+ ## Background
8
+
9
+ In Lowdefy v5, the `thresholds` parameter on `humanizeDuration` is accepted but **ignored**. Dayjs supports only global thresholds (identical to moment's defaults). Per-call custom thresholds have no effect.
10
+
11
+ ## Instructions
12
+
13
+ 1. Find all `.yaml` and `.yml` files in the project (excluding `node_modules/` and hidden directories).
14
+
15
+ 2. In each file, search for `_moment.humanizeDuration` or `_dayjs.humanizeDuration` blocks that contain a `thresholds:` key. The `thresholds:` key appears as an indented child of the `humanizeDuration` operator block.
16
+
17
+ 3. For each match found:
18
+
19
+ - Record the file path, line number of the `humanizeDuration` call, and line number of the `thresholds` key.
20
+ - Read the surrounding context (the humanizeDuration block plus a few lines of the thresholds config).
21
+
22
+ 4. If matches are found, present a **REVIEW NEEDED** report:
23
+
24
+ > The following configs use `thresholds` with `humanizeDuration`. In Lowdefy v5 (dayjs), `thresholds` is accepted but ignored. Dayjs uses default thresholds (same as moment defaults).
25
+
26
+ For each match, show the file, line number, and the relevant YAML context. Then advise:
27
+
28
+ - If the app does not depend on custom threshold behavior (changing when durations switch units, e.g., "show weeks after 7 days"), no action is needed — the defaults match.
29
+ - If the app does depend on custom thresholds, the `thresholds` property can be removed since it has no effect. The output will use dayjs defaults.
30
+ - Removing the `thresholds` key is recommended for clarity, since it is now a no-op.
31
+
32
+ 5. If no matches are found, report: "No `thresholds` usage found. No review needed."
33
+
34
+ ## Scope
35
+
36
+ - This prompt is **report-only**. Do not automatically remove `thresholds` keys. Present findings and let the user decide.
37
+ - Search for `thresholds` only within humanizeDuration operator blocks, not elsewhere in the config.
@@ -0,0 +1,121 @@
1
+ # Migration: Responsive Style Breakpoints → Tailwind Classes
2
+
3
+ ## Context
4
+
5
+ Lowdefy v4 supported responsive breakpoint keys inside `style:` blocks (e.g., `sm:`, `md:`, `lg:`). This feature is removed in v4.8 — responsive styling now uses Tailwind utility classes via the new `class` property.
6
+
7
+ ## What to Do
8
+
9
+ For each block that uses responsive breakpoint keys inside `style:`, convert the responsive styles to equivalent Tailwind utility classes on the `class` property.
10
+
11
+ ### Common Tailwind Mappings
12
+
13
+ | CSS Property | Tailwind Prefix | Example |
14
+ | ---------------- | --------------- | --------------------------- |
15
+ | `padding` | `p-` | `padding: 16` → `p-4` |
16
+ | `margin` | `m-` | `margin: 8` → `m-2` |
17
+ | `display: none` | `hidden` | |
18
+ | `display: block` | `block` | |
19
+ | `display: flex` | `flex` | |
20
+ | `fontSize` | `text-` | `fontSize: 24` → `text-2xl` |
21
+ | `width` | `w-` | `width: '100%'` → `w-full` |
22
+ | `gap` | `gap-` | `gap: 16` → `gap-4` |
23
+
24
+ Tailwind breakpoint prefixes: `sm:`, `md:`, `lg:`, `xl:`, `2xl:`
25
+
26
+ ### Conversion approach
27
+
28
+ 1. Find `style:` blocks with breakpoint keys (`sm:`, `md:`, `lg:`, `xl:`, `xxl:` / `2xl:`)
29
+ 2. For each breakpoint, map the CSS properties to Tailwind classes
30
+ 3. Add the classes to the block's `class` property (create if it doesn't exist)
31
+ 4. Remove the breakpoint keys from `style:`
32
+ 5. If `style:` becomes empty after removing breakpoints, remove the `style:` block entirely
33
+
34
+ ## Files to Check
35
+
36
+ Glob: `**/*.{yaml,yml}`
37
+ Grep: `sm:|md:|lg:|xl:|2xl:|xxl:` (within `style:` blocks on Lowdefy blocks)
38
+
39
+ **Important:** `layout.sm.span` (antd Col responsive) is NOT affected — only `style.sm.*` breakpoints are removed. Don't touch responsive keys under `layout:`.
40
+
41
+ ## Examples
42
+
43
+ ### Before — responsive padding
44
+
45
+ ```yaml
46
+ - id: container
47
+ type: Box
48
+ style:
49
+ padding: 64
50
+ sm:
51
+ padding: 32
52
+ md:
53
+ padding: 48
54
+ ```
55
+
56
+ ### After
57
+
58
+ ```yaml
59
+ - id: container
60
+ type: Box
61
+ class: p-16 sm:p-8 md:p-12
62
+ ```
63
+
64
+ ### Before — responsive visibility
65
+
66
+ ```yaml
67
+ - id: sidebar
68
+ type: Box
69
+ style:
70
+ display: none
71
+ md:
72
+ display: block
73
+ ```
74
+
75
+ ### After
76
+
77
+ ```yaml
78
+ - id: sidebar
79
+ type: Box
80
+ class: hidden md:block
81
+ ```
82
+
83
+ ### Before — mixed responsive and static styles
84
+
85
+ ```yaml
86
+ - id: card
87
+ type: Card
88
+ style:
89
+ /element:
90
+ borderRadius: 8
91
+ fontSize: 24
92
+ sm:
93
+ fontSize: 16
94
+ ```
95
+
96
+ ### After
97
+
98
+ ```yaml
99
+ - id: card
100
+ type: Card
101
+ style:
102
+ /element:
103
+ borderRadius: 8
104
+ class: text-2xl sm:text-base
105
+ ```
106
+
107
+ ## Edge Cases
108
+
109
+ - Not all CSS properties have direct Tailwind equivalents — for complex properties (transforms, animations, specific pixel values), use arbitrary values: `class: "p-[17px] sm:p-[9px]"`
110
+ - If the block already has a `class` property, append the responsive classes to it
111
+ - Pixel-to-Tailwind-unit conversion: Tailwind uses a 4px scale (1 unit = 4px). `padding: 16` → `p-4`, `margin: 8` → `m-2`
112
+ - Some responsive styles may be better left as custom CSS — use judgment for complex layouts
113
+ - This migration requires understanding the visual intent, not just mechanical replacement
114
+
115
+ ## Verification
116
+
117
+ ```
118
+ grep -rn 'sm:\|md:\|lg:\|xl:\|2xl:' --include='*.yaml' --include='*.yml' . | grep -v layout | grep -v node_modules
119
+ ```
120
+
121
+ Remaining matches should only be inside `layout:` blocks (which are not affected) or non-Lowdefy YAML files.