sfcc-metadata-cli 0.0.1 → 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 ADDED
@@ -0,0 +1,249 @@
1
+ # sfcc-metadata-cli
2
+
3
+ A companion CLI tool to [b2c-tools](https://github.com/SalesforceCommerceCloud/b2c-tools) for creating SFCC B2C migrations.
4
+
5
+ ## Features
6
+
7
+ - **Create migrations** with timestamp-based naming for unique, sortable names
8
+ - **Generate custom object definitions** with interactive prompts
9
+ - **Add site preferences** with automatic group detection
10
+ - **Extend system objects** with custom attributes
11
+
12
+ ## Installation
13
+
14
+ ```bash
15
+ npm install sfcc-metadata-cli --save-dev
16
+ ```
17
+
18
+ ## Usage
19
+
20
+ ```bash
21
+ # Using npx
22
+ npx sfcc-metadata <command>
23
+
24
+ # Or add scripts to your package.json
25
+ ```
26
+
27
+ ### package.json Scripts (Recommended)
28
+
29
+ Add these scripts to your project's `package.json`:
30
+
31
+ ```json
32
+ {
33
+ "scripts": {
34
+ "migration:create": "sfcc-metadata create",
35
+ "migration:custom-object": "sfcc-metadata custom-object",
36
+ "migration:site-preference": "sfcc-metadata site-preference",
37
+ "migration:system-object": "sfcc-metadata system-object"
38
+ }
39
+ }
40
+ ```
41
+
42
+ Then run:
43
+
44
+ ```bash
45
+ npm run migration:create
46
+ npm run migration:custom-object
47
+ npm run migration:site-preference
48
+ npm run migration:system-object
49
+ ```
50
+
51
+ ### Commands
52
+
53
+ #### Create Migration
54
+
55
+ Creates a new migration folder with timestamp-based naming: `YYYYMMDD_HHMMSS_description`
56
+
57
+ ```bash
58
+ # Interactive mode
59
+ npx sfcc-metadata create
60
+
61
+ # With description
62
+ npx sfcc-metadata create "add order status field"
63
+ # Creates: 20251231_143052_add_order_status_field
64
+
65
+ # Short format (date + sequence)
66
+ npx sfcc-metadata create --short "fix shipping"
67
+ # Creates: 20251231_01_fix_shipping
68
+ ```
69
+
70
+ #### Custom Object Definition
71
+
72
+ Creates a custom object type definition in the `meta/custom-objecttype-definitions.xml` file.
73
+
74
+ ```bash
75
+ # Interactive mode
76
+ npx sfcc-metadata custom-object
77
+
78
+ # Example: Create a custom object for caching
79
+ npx sfcc-metadata custom-object \
80
+ --type-id CacheConfig \
81
+ --display-name "Cache Configuration" \
82
+ --key-id cacheKey \
83
+ --storage-scope site
84
+ ```
85
+
86
+ #### Site Preference
87
+
88
+ Creates a site preference attribute in the `meta/system-objecttype-extensions.xml` file.
89
+
90
+ ```bash
91
+ # Interactive mode
92
+ npx sfcc-metadata site-preference
93
+
94
+ # Example: Add a boolean preference
95
+ npx sfcc-metadata site-preference \
96
+ --attribute-id enableFeatureX \
97
+ --type boolean \
98
+ --default-value false \
99
+ --group "Custom Configs"
100
+ ```
101
+
102
+ #### System Object Extension
103
+
104
+ Extends system objects (Order, Product, Customer, etc.) with custom attributes.
105
+
106
+ ```bash
107
+ # Interactive mode
108
+ npx sfcc-metadata system-object
109
+
110
+ # Example: Add a custom attribute to Order
111
+ npx sfcc-metadata system-object \
112
+ --object-type Order \
113
+ --attribute-id customField \
114
+ --type string \
115
+ --group Order_Custom
116
+ ```
117
+
118
+ ### Common Options
119
+
120
+ | Option | Alias | Description |
121
+ | ------------------ | ----- | ------------------------------------------------------ |
122
+ | `--migrations-dir` | `-m` | Path to migrations directory (default: `./migrations`) |
123
+ | `--interactive` | `-i` | Enable/disable interactive mode |
124
+ | `--help` | `-h` | Show help |
125
+
126
+ ## Migration Naming Convention
127
+
128
+ ### New Format (Recommended)
129
+
130
+ Migrations use **timestamp-based naming** for guaranteed uniqueness and natural sorting:
131
+
132
+ | Format | Example | Description |
133
+ | ---------------------- | ---------------------------------- | --------------------------- |
134
+ | `YYYYMMDD_HHMMSS` | `20251231_143052` | Full timestamp |
135
+ | `YYYYMMDD_HHMMSS_desc` | `20251231_143052_add_order_fields` | With description |
136
+ | `YYYYMMDD_NN_desc` | `20251231_01_fix_shipping` | Short format (--short flag) |
137
+
138
+ **Benefits:**
139
+ - ✅ No conflicts - timestamp ensures uniqueness
140
+ - ✅ Self-documenting - description in the name
141
+ - ✅ Natural sorting - alphabetical order = chronological order
142
+ - ✅ Unlimited migrations per day
143
+
144
+ ### Legacy Format
145
+
146
+ Old migrations using `YY.MM.N` format are still supported and will sort before new migrations.
147
+
148
+ ## Supported Attribute Types
149
+
150
+ | Type | Description |
151
+ | ---------------- | ---------------------------- |
152
+ | `string` | Single-line text |
153
+ | `text` | Multi-line text |
154
+ | `html` | HTML content |
155
+ | `int` | Integer number |
156
+ | `double` | Decimal number |
157
+ | `boolean` | True/False |
158
+ | `date` | Date only |
159
+ | `datetime` | Date and time |
160
+ | `enum-of-string` | Single/multi-select dropdown |
161
+ | `enum-of-int` | Integer enumeration |
162
+ | `set-of-string` | Set of strings |
163
+ | `image` | Image reference |
164
+ | `password` | Encrypted password |
165
+
166
+ ## Examples
167
+
168
+ ### Create a Complete Migration
169
+
170
+ ```bash
171
+ # 1. Create the migration folder
172
+ node tools/migration-helper/index.js create
173
+
174
+ # 2. Add a custom object
175
+ node tools/migration-helper/index.js custom-object
176
+
177
+ # 3. Add a site preference
178
+ node tools/migration-helper/index.js site-preference
179
+
180
+ # 4. Preview the migration
181
+ npm run migrate
182
+
183
+ # 5. Apply the migration
184
+ npm run migrate:apply
185
+ ```
186
+
187
+ ### Generated XML Examples
188
+
189
+ #### Custom Object
190
+ ```xml
191
+ <?xml version="1.0" encoding="UTF-8"?>
192
+ <metadata xmlns="http://www.demandware.com/xml/impex/metadata/2006-10-31">
193
+ <custom-type type-id="MyCustomObject">
194
+ <display-name xml:lang="x-default">My Custom Object</display-name>
195
+ <staging-mode>source-to-target</staging-mode>
196
+ <storage-scope>site</storage-scope>
197
+ <key-definition attribute-id="key">
198
+ <type>string</type>
199
+ <min-length>0</min-length>
200
+ </key-definition>
201
+ <attribute-definitions>
202
+ <!-- attributes -->
203
+ </attribute-definitions>
204
+ <group-definitions>
205
+ <!-- groups -->
206
+ </group-definitions>
207
+ </custom-type>
208
+ </metadata>
209
+ ```
210
+
211
+ #### Site Preference
212
+ ```xml
213
+ <?xml version="1.0" encoding="UTF-8"?>
214
+ <metadata xmlns="http://www.demandware.com/xml/impex/metadata/2006-10-31">
215
+ <type-extension type-id="SitePreferences">
216
+ <custom-attribute-definitions>
217
+ <attribute-definition attribute-id="myPreference">
218
+ <display-name xml:lang="x-default">My Preference</display-name>
219
+ <type>boolean</type>
220
+ <mandatory-flag>false</mandatory-flag>
221
+ <externally-managed-flag>false</externally-managed-flag>
222
+ <default-value>false</default-value>
223
+ </attribute-definition>
224
+ </custom-attribute-definitions>
225
+ <group-definitions>
226
+ <attribute-group group-id="Custom Configs">
227
+ <!-- existing attributes -->
228
+ <attribute attribute-id="myPreference"/>
229
+ </attribute-group>
230
+ </group-definitions>
231
+ </type-extension>
232
+ </metadata>
233
+ ```
234
+
235
+ ## Integration with b2c-tools
236
+
237
+ After creating migrations with this tool, use b2c-tools to apply them:
238
+
239
+ ```bash
240
+ # Preview migrations
241
+ node build/b2c-tools.js import migrate --dry-run
242
+
243
+ # Apply migrations
244
+ node build/b2c-tools.js import migrate
245
+ ```
246
+
247
+ ## License
248
+
249
+ AGPL-3.0-or-later
package/biome.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
3
+ "organizeImports": {
4
+ "enabled": true
5
+ },
6
+ "linter": {
7
+ "enabled": true,
8
+ "rules": {
9
+ "recommended": true,
10
+ "complexity": {
11
+ "noForEach": "off"
12
+ },
13
+ "style": {
14
+ "noNonNullAssertion": "off"
15
+ }
16
+ }
17
+ },
18
+ "formatter": {
19
+ "enabled": true,
20
+ "indentStyle": "space",
21
+ "indentWidth": 4
22
+ },
23
+ "javascript": {
24
+ "formatter": {
25
+ "quoteStyle": "single",
26
+ "semicolons": "always"
27
+ }
28
+ },
29
+ "files": {
30
+ "ignore": ["node_modules", "package-lock.json"]
31
+ }
32
+ }
@@ -0,0 +1,157 @@
1
+ /**
2
+ * Create Migration Command
3
+ * Creates a new migration folder with timestamp-based naming convention
4
+ * Format: YYYYMMDD_HHMMSS or YYYYMMDD_HHMMSS_description
5
+ */
6
+
7
+ const path = require('node:path');
8
+ const inquirer = require('inquirer');
9
+ const chalk = require('chalk');
10
+ const {
11
+ generateMigrationName,
12
+ generateShortMigrationName,
13
+ listMigrations,
14
+ ensureDir,
15
+ getLatestMigration,
16
+ } = require('../lib/utils');
17
+
18
+ module.exports = {
19
+ command: 'create [description]',
20
+ aliases: ['new', 'init'],
21
+ desc: 'Create a new migration folder (format: YYYYMMDD_HHMMSS_description)',
22
+ builder: (yargs) => {
23
+ return yargs
24
+ .positional('description', {
25
+ describe:
26
+ 'Short description for the migration (e.g., "add_order_fields")',
27
+ type: 'string',
28
+ })
29
+ .option('short', {
30
+ alias: 's',
31
+ type: 'boolean',
32
+ default: false,
33
+ description:
34
+ 'Use short format: YYYYMMDD_NN instead of timestamp',
35
+ })
36
+ .option('with-meta', {
37
+ type: 'boolean',
38
+ default: false,
39
+ description: 'Create meta folder structure',
40
+ })
41
+ .option('interactive', {
42
+ alias: 'i',
43
+ type: 'boolean',
44
+ default: false,
45
+ description: 'Interactive mode with prompts',
46
+ });
47
+ },
48
+ handler: async (argv) => {
49
+ const migrationsDir = path.resolve(argv.migrationsDir);
50
+
51
+ try {
52
+ let description = argv.description;
53
+ const withMeta = argv.withMeta;
54
+ const useShortFormat = argv.short;
55
+
56
+ // List existing migrations
57
+ const existingMigrations = listMigrations(migrationsDir);
58
+ const latestMigration = getLatestMigration(migrationsDir);
59
+
60
+ if (existingMigrations.length > 0) {
61
+ console.log(chalk.cyan('\nRecent migrations:'));
62
+ existingMigrations.slice(-5).forEach((m) => {
63
+ console.log(chalk.gray(` - ${m}`));
64
+ });
65
+ if (existingMigrations.length > 5) {
66
+ console.log(
67
+ chalk.gray(
68
+ ` ... and ${existingMigrations.length - 5} more`,
69
+ ),
70
+ );
71
+ }
72
+ console.log();
73
+ }
74
+
75
+ if (argv.interactive || (!description && process.stdin.isTTY)) {
76
+ const answers = await inquirer.prompt([
77
+ {
78
+ type: 'input',
79
+ name: 'description',
80
+ message:
81
+ 'Migration description (e.g., "add_order_status_field"):',
82
+ filter: (input) => input.trim(),
83
+ transformer: (input) => {
84
+ // Show preview of the sanitized name
85
+ const sanitized = input
86
+ .toLowerCase()
87
+ .replace(/\s+/g, '_')
88
+ .replace(/[^a-z0-9_]/g, '');
89
+ return (
90
+ input +
91
+ (sanitized !== input
92
+ ? chalk.gray(` → ${sanitized}`)
93
+ : '')
94
+ );
95
+ },
96
+ },
97
+ ]);
98
+
99
+ description = answers.description || description;
100
+ }
101
+
102
+ // Generate migration name
103
+ const migrationName = useShortFormat
104
+ ? generateShortMigrationName(migrationsDir, description)
105
+ : generateMigrationName(migrationsDir, description);
106
+
107
+ // Check if migration already exists
108
+ const migrationPath = path.join(migrationsDir, migrationName);
109
+ if (existingMigrations.includes(migrationName)) {
110
+ console.error(
111
+ chalk.red(
112
+ `\nError: Migration "${migrationName}" already exists`,
113
+ ),
114
+ );
115
+ process.exit(1);
116
+ }
117
+
118
+ // Create migration folder
119
+ ensureDir(migrationPath);
120
+ console.log(chalk.green(`\n✓ Created migration: ${migrationName}`));
121
+
122
+ // Create meta folder if requested
123
+ if (withMeta) {
124
+ const metaPath = path.join(migrationPath, 'meta');
125
+ ensureDir(metaPath);
126
+ console.log(chalk.green('✓ Created meta folder'));
127
+ }
128
+
129
+ console.log(chalk.cyan(`\nMigration path: ${migrationPath}`));
130
+ console.log(chalk.gray('\nNext steps:'));
131
+ console.log(
132
+ chalk.gray(' - Add XML files to the migration folder'),
133
+ );
134
+ console.log(
135
+ chalk.gray(
136
+ ' - Use "migration-helper custom-object" to add custom object definitions',
137
+ ),
138
+ );
139
+ console.log(
140
+ chalk.gray(
141
+ ' - Use "migration-helper site-preference" to add site preferences',
142
+ ),
143
+ );
144
+ console.log(
145
+ chalk.gray(' - Run "npm run migrate" to preview migrations'),
146
+ );
147
+ console.log(
148
+ chalk.gray(
149
+ ' - Run "npm run migrate:apply" to apply migrations',
150
+ ),
151
+ );
152
+ } catch (error) {
153
+ console.error(chalk.red(`\nError: ${error.message}`));
154
+ process.exit(1);
155
+ }
156
+ },
157
+ };