@kubohiroya/sb3-toolchain
v0.14.0
Published
Git-friendly source management and deterministic builds for Scratch 3 and TurboWarp SB3 projects
Maintainers
Readme
sb3-toolchain
A Node.js toolchain for managing Scratch 3 and TurboWarp .sb3 projects as Git-diffable
expanded sources and rebuilding bit-for-bit identical SB3 files from the same input.
Features
- Safely expand an SB3 into formatted
project.source.json, assets, and embedded extensions - Validate asset references, MD5 hashes, ZIP entries, and embedded extension mappings
- Manage embedded extensions from pinned GitHub commits or exact installed npm package versions and verify their SHA-256 hashes offline
- Optionally compare versioned extension API manifests before replacing embedded JavaScript
- Statically bundle multiple extensions into one permission unit without deleting their original JavaScript, then restore them from either the expanded source or the bundled SB3
- Produce deterministic builds with fixed ZIP entry order, timestamps, and compression settings
- Add sprites, backdrops, costumes, and sounds from an optional JSON or YAML build manifest without modifying the expanded base source
- Optionally lay out every target's scripts in a deterministic TurboWarp-style cleaned arrangement
- Protect uncommitted Git changes when importing
- Protect existing output through transactional replacement and rollback
- Provide both a CLI and a JavaScript API
Requirements
- Node.js 22.12.0 or later
- pnpm 11
Installation
Pin the verified npm version for reproducible installation.
pnpm add --save-dev --save-exact @kubohiroya/[email protected]Quick start
Expand an SB3 saved by TurboWarp, validate it, and rebuild it.
sb3-toolchain import tmp/project.sb3 --output app
sb3-toolchain check app
sb3-toolchain build app --output dist/project.sb3See docs/workflows.md for the recommended source-of-truth, re-import,
replacement protection, extension update, and CI workflows for a project repository.
JavaScript API
import {readFile} from 'node:fs/promises';
import {
buildSb3,
bundleExtensions,
createDeterministicSb3,
extensionIntegrity,
extensionStatus,
importSb3,
migrateExtensionId,
planExtensionIdMigration,
syncExtensions,
unbundleSb3,
unbundleExtensions,
updateExtensions,
validateSb3Source,
} from '@kubohiroya/sb3-toolchain';
await importSb3({
inputPath: 'tmp/project.sb3',
outputDirectory: 'app',
});
await validateSb3Source('app');
await buildSb3({
sourceDirectory: 'app',
outputPath: 'dist/project.sb3',
// Opt in to cleaned block coordinates in the generated SB3 only:
cleanUpBlocks: true,
});
const {archive} = await createDeterministicSb3('app', {cleanUpBlocks: true});
const integrity = extensionIntegrity(await readFile('app/extensions/example.js'));
const statuses = await extensionStatus('app');
const migration = await planExtensionIdMigration({
sourceDirectory: 'app',
fromId: 'oldId',
toId: 'newid',
});
await migrateExtensionId({
sourceDirectory: 'app',
fromId: 'oldId',
toId: 'newid',
yes: true,
});
await syncExtensions({sourceDirectory: 'app', yes: true});
await updateExtensions({
sourceDirectory: 'app',
extensionId: 'oldId',
migrateToId: 'newid',
sourceArtifact: 'dist/newid.js',
apiManifestArtifact: 'dist/newid.manifest.json',
yes: true,
});
// After reviewing a reported breaking API change, opt in explicitly:
await updateExtensions({sourceDirectory: 'app', allowBreakingApi: true, yes: true});
await bundleExtensions({
sourceDirectory: 'app',
bundleId: 'projectbundle',
bundleName: 'Project Extension Bundle',
extensionIds: ['extensionone', 'extensiontwo'],
yes: true,
});
await unbundleExtensions({
sourceDirectory: 'app',
bundleId: 'projectbundle',
yes: true,
});
await unbundleSb3({
inputPath: 'dist/project.sb3',
outputPath: 'dist/project.unbundled.sb3',
bundleId: 'projectbundle',
yes: true,
});Documentation
docs/workflows.md: SB3 source and extension management workflowsdocs/source-format-v1.md: expanded source format and deterministic outputdocs/project-asset-additions.md: JSON/YAML--project-assets, backdrops, and editable or strict locksdocs/extension-id-migration.md: migration of extension IDs already used by a projectdocs/extension-api-compatibility.md(日本語): opt-in static API compatibility checks for extension updatesdocs/extension-bundles.md: static bundling into one permission unit and reversible unbundling
Development
The source is TypeScript, built with Vite in library mode and tested with Vitest. pnpm run check
runs lint, format, typecheck, tests, build, repository policy, and pack checks in that order.
corepack enable
pnpm install --frozen-lockfile
pnpm run checkIndividual steps:
pnpm run typecheck
pnpm run test
pnpm run test:watch
pnpm run buildpnpm run build emits dist/ (ESM plus .d.ts declarations and source maps). The library entry
and the sb3-toolchain executable are both built from src/, so run a build before using the CLI
from a checkout.
License
SPDX-License-Identifier: MPL-2.0
This implementation extracts the general SB3 source-management mechanism developed for
kubohiroya/tm-kamishibai from the
TurboWarp TM application layer.
