Native Shell
Native Shell은 모든 플랫폼의 앱 안에서 만들어지는 AI UI입니다
CLI to rule the world. (and handle stuff related to Storyblok CMS)
저장소 README에서 가져옴
README 전체를 불러오는 중입니다.
README 전문 보기 ↗| Node | 22.x.x or >=24.x.x |
sb-mig migrate content now supports --publicationMode preserve-layers, --publicationMode collapse-draft, and --publicationMode save-only.--publicationLanguages and reusable language publish-state maps.sb-mig language-publish-statesb-mig published-layer-exportsb-mig story-versions--publish is now --publicationMode--publishLanguages is now --publicationLanguages--preservePublishedLayer is now --publicationMode preserve-layersThe sections below are retained for historical context. For current usage, prefer the docs site and sb-mig --help.
Do not hesitate to get in touch if you encounter any issues or require further clarification on any points.
nrwl to maintain (https://github.com/lerna/lerna/issues/3121) will see what will happen in future with it :)sb-mig --help command.backup command, had some very weird syntax before, now it works like sync)npm install --global sb-mig
In your working directory (usually root of your repo where u have your app/webpage using Storyblok), you have to create a .env file with your variables:
STORYBLOK_OAUTH_TOKEN=1234567890qwertyuiop
STORYBLOK_SPACE_ID=12345
# For nextjs
# NEXT_PUBLIC_STORYBLOK_ACCESS_TOKEN=zxcvbnmasdfghjkl
# For gatsby
# GATSBY_STORYBLOK_ACCESS_TOKEN=zxcvbnmasdfghjkl
# any other (you have to handle '.env' access in frontend yourself (for example with dotenv package)
STORYBLOK_ACCESS_TOKEN=zxcvbnmasdfghjkl
You can also provide your custom config. To do that u have to create storyblok.config.js file in your root catalog with following structure:
// storyblok.config.js
module.exports = {
storyblokComponentsLocalDirectory: "src/@storyblok-components",
sbmigWorkingDirectory: "sbmig",
componentsDirectories: ["src", "storyblok"],
schemaFileExt: "sb.js",
datasourceExt: "sb.datasource.js",
rolesExt: "sb.roles.js",
storyblokApiUrl: "https://api.storyblok.com/v1",
oauthToken: process.env.STORYBLOK_OAUTH_TOKEN,
spaceId: process.env.STORYBLOK_SPACE_ID,
accessToken: process.env.STORYBLOK_ACCESS_TOKEN,
};
You don't need to pass everything to the config file, just add what you need and it will be merged with the original config. If you just need to set the componentDirectory, for example, add the following:
// storyblok.config.js
module.exports = {
componentsDirectories: ["src", "storyblok", "@storyblok-components"],
};
The public management API exposes asset helpers for uploading a local file and updating existing asset metadata:
import { managementApi } from "sb-mig/dist/api/managementApi.js";
await managementApi.assets.createAsset(
{
spaceId: "12345",
pathToFile: "./public/image.jpg",
payload: {
asset_folder_id: 67890,
validate_upload: 1,
},
},
config,
);
await managementApi.assets.updateAsset(
{
spaceId: "12345",
assetId: 98765,
payload: {
meta_data: {
alt: "Image alt text",
title: "Image title",
},
},
},
config,
);
This is what a basic storyblok .sb.js schema file which maps to a component looks like:
export default {
name: "text-block",
display_name: "Text block",
is_root: false,
is_nestable: true,
component_group_name: "Some group",
schema: {
title: {
type: "text",
},
}
};
Important notice: name inside .sb.js schema need to match .sb.js filename.
You can add anything mentioned here: https://www.storyblok.com/docs/api/management#core-resources/components/components to your component. (with the exception of component_group_uuid: insert component_group_name and sb-mig will resolve uuid automagically).
You can also add tabs to your component schema (which is not documented in above storyblok documentation):
...
schema: {
title: {
type: "text",
},
Settings: {
type: "tab",
display_name: "Settings",
"keys": [
"title"
]
},
}
...
There is also support for sections inside components:
...
schema: {
title: {
type: "text",
},
somesection: {
type: "section",
"keys": [
"title"
]
},
}
...
The main purpose of sb-mig is to sync your .sb.js component schema files with your Storyblok space.
In v4.x.x There is 1 way to sync your schemas, which is to name all your schemas with .sb.js extension. You can have them in any place of your repositories, as long as this place is pointed in storyblok.config.js componentDirectories field, which is set to componentsDirectories: ["src", "storyblok"], by default:
sb-mig sync components row column
This command will look for row.sb.js and column.sb.js files inside a directories mentioned in componentDirectories field. (You can change directories name mapping by modifying componentDirectories inside storyblok.config.js). You can also change the extension searched by changing schemaFileExt. How to install and configure)
sync components and sync datasources are safe to run from a coding agent or a script:
sb-mig sync components --all --dry-run --json
sb-mig sync components hero card --presets --dry-run --json
sb-mig sync datasources --all --dry-run --json
--dry-run reads the space and writes nothing. It reports every component as create, update or unchanged, with field-level changes and component_whitelist changes, computed after your resolvers. Datasources are reported the same way, entry by entry. A sync run right after a sync plans nothing: server-side ids, dates and defaults don't count, and preview_tmpl is compared the way Storyblok stores it: only the tags its docs allow (div, span, strong, ul, li, p) with their class, and with entities decoded, since a bare > is stored as >. A changed class, or one of those tags added or removed, still shows.--presets, each component also lists its presets as create, update or unchanged. A preset that exists in the space is compared by content (its _uids and the space's own keys aside), and an update lists in changedKeys where the JSON differs, such as preset.title.0.text. Under --ssot, or when the space's presets can't be read, an existing preset is planned as update.--json prints exactly one JSON document on stdout. Every log line goes to stderr. The document looks like { ok, command, dryRun, components, datasources, destructive, errors }. After a real sync, each component and datasource has status: "written"; "recreated" for a component --ssot deleted and created again; or "failed" when its own write or one of its presets or entries failed. A preset or entry error names its component or datasource in parent.destructive lists the changes that can lose content: a removed field (field-removed), a field removed while one of the same type is added (likely-rename), a field whose type changes (type-changed), a narrowed whitelist on a blocks or link field (whitelist-narrowed), and, with --ssot, the components and groups that exist only in the space and are deleted (component-removed, group-removed). Under --ssot the plan compares against the space as it is before anything is removed. A dry-run with destructive changes still exits 0, so read destructive before you run the sync for real.sb-mig copy space --from <sourceSpaceId> --to <targetSpaceId> copies a space's schema into another, already existing space: languages, component groups (nesting included), components, presets, and datasources with their entries and dimension values. It also copies the space settings: the internationalization switches (individual translation publishing and alternative versions are turned on, never off), the language flag display, and the Visual Editor location and preview URLs, merged by name so preview URLs that exist only in the target are kept. It copies no stories and no assets — run copy stories and copy assets into the prepared space afterwards. Resources are matched by name and overwritten with the source version; anything that exists only in the target is left untouched, so a rerun updates instead of duplicating. Group uuids, component_group_whitelist entries and preset component ids are rewritten to the target's own. The command prints a PLAN block with per-resource create/update/skip counts and asks before its first write; --dry-run stops after the plan, --only groups,components narrows the resources, and --outputPath writes the plan as JSON.
sb-mig copy space --from 12345 --to 67890 --dry-run
sb-mig copy space --from 12345 --to 67890 --yes
Use sb-mig migrate content to run one or more migration configs against
stories from a space or from a file.
sb-mig migrate content --all --from 12345 --to 12345 --migration itemsToContent
You can run multiple migration configs in order:
sb-mig migrate content --all \
--from 12345 \
--to 12345 \
--migration migration-a \
--migration migration-b \
--migration migration-c
Storyblok stories can have separate draft/current and published layers. In 6.x, sb-mig makes this explicit with --publicationMode.
preserve-layers is the default. Use it when you want migrated stories to keep the source publication shape: draft-only stories stay draft-only, clean published stories stay published, and dirty published stories keep separate migrated draft/current and published layers. This mode currently requires migrating from a Storyblok space to the same Storyblok space.
sb-mig migrate content --all \
--from 12345 \
--to 12345 \
--migration itemsToContent \
--publicationMode preserve-layers \
--yes
collapse-draft publishes changed stories and can collapse draft/current changes into the published result. Use this when the target should end up with the migrated content published.
sb-mig migrate content --all \
--from 12345 \
--to 12345 \
--migration itemsToContent \
--publicationMode collapse-draft \
--publicationLanguages all \
--yes
save-only updates draft/current JSON without publishing any languages.
sb-mig migrate content --all \
--from 12345 \
--to 12345 \
--migration itemsToContent \
--publicationMode save-only \
--yes
Use --publicationLanguages with default, all, or a comma-separated list of Storyblok language codes:
sb-mig migrate content --all \
--from 12345 \
--to 12345 \
--migration itemsToContent \
--publicationMode collapse-draft \
--publicationLanguages default,fr,de \
--yes
When --publicationMode publishes languages, sb-mig can build the language publish-state map automatically. For bigger or more sensitive runs, generate the map first and pass it back with --languagePublishStatePath:
sb-mig language-publish-state \
--from 12345 \
--startsWith blog/ \
--languages all \
--fileName blog-prod
sb-mig migrate content --all \
--from 12345 \
--to 12345 \
--migration itemsToContent \
--publicationMode preserve-layers \
--publicationLanguages all \
--languagePublishStatePath sbmig/language-publish-state/blog-prod.json \
--yes
Migrations write JSON artifacts under sbmig/migrations by default. Use --dry-run to preview the selected stories, transformed output, publication plan, and write summary without making Storyblok API writes.
sb-mig migrate content --all \
--from 12345 \
--to 12345 \
--migration itemsToContent \
--publicationMode preserve-layers \
--publicationLanguages all \
--dry-run \
--fileName blog-v6-check
6.x also writes JSONL migration run logs for easier auditing and post-run debugging. When published layers are involved, additional published-layer input/output and summary files are written.
Migration files export a mapper keyed by component name. By default, sb-mig
runs a migration only for the component keys exported by that migration file.
Use these flags when a consuming app has wrapper components that should reuse an existing migration function:
--migrationComponentAlias
Format: <migration>:<source>=<alias1>,<alias2>--migrationComponents
Format: <migration>:<component1>,<component2>--migrationComponentAlias extends the migration mapper at runtime by reusing
the source component's migration function for extra component names.
--migrationComponents overrides the exact component scope for that migration.
Example:
sb-mig migrate content --all \
--from 12345 \
--to 12345 \
--migration colorPickerModeValues \
--migrationComponentAlias colorPickerModeValues:sb-button=sb-open-drift-button \
--migrationComponentAlias colorPickerModeValues:sb-section=sb-tour-page-section \
--migrationComponents colorPickerModeValues:sb-button,sb-open-drift-button,sb-section,sb-tour-page-section
This runs colorPickerModeValues for the normal Backpack keys and also for the
two wrapper component names by aliasing them onto the existing sb-button and
sb-section migration handlers.
These commands are read-only against Storyblok and are useful before a migration run.
Build a language publish-state map:
sb-mig language-publish-state \
--from 12345 \
--startsWith about/ \
--languages all \
--fileName about-prod
Export draft/current and latest published story layers:
sb-mig published-layer-export \
--from 12345 \
--startsWith about/ \
--fileName about-layer-check
Inspect raw Storyblok story versions for a single story:
sb-mig story-versions --from 12345 --withSlug about/contact --raw
You can also sync your datasources.
Add datasourceExt: "your-own-extension", to your storyblok.config.js. If u will not add it, will be used default one (sb.datasource.js)
// storyblok.config.js
export default {
...
datasourcesDirectory: "mydatasource.ext.js"
...
};
Create file with .sb.datasource.js (or your defined one) extension inside it. Basic schema for datasources file:
export default {
name: "icons",
slug: "icons",
datasource_entries: [
{
componentName: "icon1",
importPath: "icon 2"
},
{
componentName: "icon2",
importPath: "icon 2"
},
{
componentName: "icon3",
importPath: "icon 3"
},
{
componentName: "icon4",
importPath: "icon 4"
},
]
};
Above snippet will create datasource with icons name and icons slug. datasource_entries will be your name <-> value array.
Single datasource entry consist of precisely 2 fields. But they can be named however you like (advise to name it: name and value, it will be anyway translated to that, due to how storyblok stores them)
Command for syncing datasources:
sb-mig sync datasources icons
Example output from above command
Synciong priovided datasources icons...
Trying to sync provided datasources: icons
Trying to get all Datasources.
Trying to get 'icons' datasource entries.
Trying to get 'icons' datasource.
✓ Datasource entries for 15558 datasource id has been successfully synced
Like with syncing component, you can also use syncing multiple datasources at once:
sb-mig sync datasources icons logos
✓ Datasource entries for 15558 datasource id has been successfully synced.
✓ Datasource entries for 15559 datasource id has been successfully synced.
You can also sync all datasources, and that's the command we strongly recommend. It will sync all datasources also the one from node_modules, so potentially from storyblok-components. But will prefer syncing local ones, if there will be clash of datasources filenames (for example, you can have datasource from node_modules, but wanted to overwrite some fields, you will be able to do that.
sb-mig sync datasources --all
Writing your own predefined data (presets) for components can be a pain, so with sb-mig you can create presets for your components in the storyblok gui, and then export them to a schema based .sb.js file to be picked up while syncing.
To do so, first create a preset for your component in storyblok:
then run
sb-mig backup component-presets --one text-block // component you've created preset for
The tool will now download all presets related to the text-block component.
Now you can go to your folder structure (by default: ./sbmig/component-presets/), and rename the generated file to (for example): text-block-preset.
You should remove the id field from the preset (it will be looked up by name)
Finally, add the all_presets field to your text-block component schema.
import allPresets from './presets/_text-block-preset.json'
export default {
...
schema: {
title: {
type: "text",
pos: 1
},
},
all_presets: allPresets,
...
};
Now, sync your component
sb-mig sync components text-block --presets
output:
Checking preset for 'text-block-2' component
Trying to get all 'text-block-2' presets.
Trying to get all components.
Trying to get preset by id: 437086
Preset: 'My Preset' with '437086' id has been updated.
This feature is still quite experimental, that's why it's not completely straightforward to do. Workin on it :)
To develop and make changes to the library:
git clone [email protected]:sb-mig/sb-mig.git
Install packages
npm install
Run development command
npm run build:dev
It will watch a file change, and on every change, will rebuild typescript and build the whole lib/cli.
The you can use
node dist/index.js debug
to access sb-mig
For your conveniece, you can also, link it to proper sb-mi name:
npm link
And then you can use it like that:
sb-mig debug
sb-mig debug shows the resolved config, config file paths, sb-mig and Node
versions, dependency versions, and package module type without calling Storyblok.
Use sb-mig debug --json for a single JSON report. Both formats mask values under
keys matching /token|secret|key|password|auth(?!or(?!iz))/i (including oauth,
proxyauth, and authorization, but excluding author). Values
appear as ••••abcd (only the last four characters; values with four characters
or fewer are fully masked). Known secret strings longer than four characters,
their URL-encoded and JSON-escaped copies, URL userinfo, and sensitive URL query
and fragment values are also masked, including nested config and embedded copies.
Ordinary booleans and numbers are never masked by value.
The report shows the loaded config file (or null when using defaults), searched
config paths, and .env and project package.json paths with envExists and
packageJsonExists flags. configError reports only the error name and config
line:column when available, or null when none occurred. Error messages and source
code are omitted to protect credentials in invalid config. Config resolution uses
defaults when package.json is absent; this applies to normal commands too.
git pull origin beta).git pull origin master), Push directly, resolve conflicts in favor of master.The general purpose of this package is to manage creation and maintenance of components from code/command line, to be able to create a whole space and project structure without using GUI.
errorssync components and sync datasources never wait for input without a terminal, or under --json. When they would ask (--ssot), they fail with a message that names the flag to pass: --yes to confirm, or --dry-run to preview. --ssot also refuses to run when no schema files were found, or when none of the space's components is among them, since that usually means a wrong directory or schemaFileExt. Other commands still read a piped answer, such as echo y | sb-mig ....