Skip to main content

Migrating from npm Scripts to Xec

Overview​

This guide helps you migrate from npm scripts in package.json to Xec's more powerful task and script system. Xec provides better cross-platform compatibility, parallel execution, environment management, and TypeScript support while maintaining the simplicity of npm scripts.

Why Migrate to Xec?​

Limitations of npm Scripts​

{
"scripts": {
"build": "webpack --mode production",
"deploy": "npm run build && scp -r dist/ user@server:/app/",
"complex": "node scripts/task1.js && node scripts/task2.js || echo 'Failed'"
}
}

Problems:

  • Platform-specific commands (Windows vs Unix)
  • Limited error handling
  • No built-in parallelization
  • String concatenation for complex tasks
  • No TypeScript support
  • Poor IDE integration

Xec Advantages​

# .xec/config.yaml
tasks:
build:
command: webpack --mode production
timeout: 5m

deploy:
steps:
- name: Build
command: xec build
- name: Upload
targets: production-server
command: xec copy dist/ /app/
parallel: false

Benefits:

  • Cross-platform compatibility
  • Structured error handling
  • Native parallel execution
  • TypeScript scripts with type safety
  • Multi-environment support
  • Better debugging and logging

Migration Strategies​

Strategy 1: Gradual Migration​

Keep npm scripts while gradually moving to Xec:

{
"scripts": {
"build": "xec build",
"test": "xec test",
"deploy": "npm run build && npm run upload",
"upload": "xec deploy production"
}
}

Strategy 2: Full Migration​

Replace all npm scripts with Xec tasks:

# .xec/config.yaml
tasks:
build:
command: webpack --mode production
test:
command: jest --coverage
deploy:
needs: [build, test]
command: xec deploy production

Common Patterns Migration​

1. Simple Commands​

npm Script:

{
"scripts": {
"clean": "rm -rf dist",
"lint": "eslint src/**/*.ts",
"format": "prettier --write src/**/*.ts"
}
}

Xec Equivalent:

# .xec/config.yaml
tasks:
clean:
command: rm -rf dist
# Cross-platform version:
command: xec -e "await fs.rm('dist', { recursive: true, force: true })"

lint:
command: eslint src/**/*.ts

format:
command: prettier --write src/**/*.ts

Or as Xec Script:

// scripts/clean.ts
import { $ } from '@xec-sh/core';
import { rm } from 'fs/promises';

// Cross-platform clean
await rm('dist', { recursive: true, force: true });
console.log('βœ… Cleaned dist directory');

2. Sequential Commands​

npm Script:

{
"scripts": {
"build:all": "npm run clean && npm run compile && npm run bundle"
}
}

Xec Task:

tasks:
build:all:
steps:
- name: Clean
command: xec clean
- name: Compile
command: tsc
- name: Bundle
command: webpack

Xec Script:

// scripts/build-all.ts
import { $ } from '@xec-sh/core';

console.log('🧹 Cleaning...');
await $`rm -rf dist`;

console.log('πŸ“¦ Compiling...');
await $`tsc`;

console.log('🎁 Bundling...');
await $`webpack`;

console.log('βœ… Build complete!');

3. Parallel Commands​

npm Script (Limited):

{
"scripts": {
"dev": "concurrently \"npm run watch:ts\" \"npm run watch:css\" \"npm run serve\""
}
}

Xec Task:

tasks:
dev:
parallel: true
steps:
- name: Watch TypeScript
command: tsc --watch
- name: Watch CSS
command: sass --watch src:dist
- name: Serve
command: vite dev

Xec Script:

// scripts/dev.ts
import { $ } from '@xec-sh/core';

// Run all commands in parallel
await Promise.all([
$`tsc --watch`,
$`sass --watch src:dist`,
$`vite dev`
]);

4. Environment Variables​

npm Script:

{
"scripts": {
"build:prod": "NODE_ENV=production webpack",
"build:dev": "NODE_ENV=development webpack"
}
}

Xec Task:

tasks:
build:
params:
- name: env
default: development
values: [development, production]
env:
NODE_ENV: ${params.env}
command: webpack

Xec Script:

// scripts/build.ts
import { $ } from '@xec-sh/core';

const env = process.argv[2] || 'development';
process.env.NODE_ENV = env;

await $`webpack`;
console.log(`Built for ${env}`);

5. Pre/Post Hooks​

npm Script:

{
"scripts": {
"pretest": "npm run lint",
"test": "jest",
"posttest": "npm run coverage"
}
}

Xec Task:

tasks:
test:
steps:
- name: Lint (pre-test)
command: eslint src/**/*.ts
- name: Run Tests
command: jest
- name: Coverage Report (post-test)
command: jest --coverage

6. Cross-Platform Commands​

npm Script (Platform Issues):

{
"scripts": {
"clean": "rm -rf dist || rmdir /s /q dist",
"copy": "cp -r src/assets dist/ || xcopy src\\assets dist\\ /E"
}
}

Xec (Cross-Platform):

// scripts/clean.ts
import { rm } from 'fs/promises';

await rm('dist', { recursive: true, force: true });
// scripts/copy.ts
import { $ } from '@xec-sh/core';

await $`xec copy src/assets/ dist/assets/`;
// Works on all platforms!

Complex Workflows​

Build and Deploy Pipeline​

npm Scripts:

{
"scripts": {
"prebuild": "npm run clean && npm run lint",
"build": "webpack --mode production",
"postbuild": "npm run optimize",
"predeploy": "npm run test",
"deploy": "npm run build && npm run upload",
"upload": "scp -r dist/* user@server:/var/www/",
"postdeploy": "npm run notify"
}
}

Xec Configuration:

# .xec/config.yaml
targets:
production:
type: ssh
host: server.example.com
user: deploy

tasks:
build:
description: Build application for production
steps:
- name: Clean
command: rm -rf dist
- name: Lint
command: eslint src/**/*.ts
- name: Compile
command: webpack --mode production
- name: Optimize
command: terser dist/bundle.js -o dist/bundle.min.js

test:
command: jest --coverage
timeout: 5m

deploy:
description: Deploy to production
needs: [test, build]
steps:
- name: Upload Files
command: xec copy dist/ production:/var/www/html/
- name: Restart Service
targets: production
command: systemctl restart nginx
- name: Notify
command: xec -e "await notify('Deployment complete!')"

Xec Script Version:

// scripts/deploy.ts
import { $, on } from '@xec-sh/core';
import { readConfig } from './utils';

const config = await readConfig();
const target = process.argv[2] || 'production';

// Pre-deployment checks
console.log('πŸ” Running tests...');
const testResult = await $`jest --coverage`.nothrow();
if (!testResult.ok) {
console.error('❌ Tests failed!');
process.exit(1);
}

// Build
console.log('πŸ—οΈ Building application...');
await $`rm -rf dist`;
await $`eslint src/**/*.ts`;
await $`webpack --mode production`;
await $`terser dist/bundle.js -o dist/bundle.min.js`;

// Deploy
console.log(`πŸš€ Deploying to ${target}...`);
await $`xec copy dist/ ${target}:/var/www/html/`;
await on(target, 'systemctl restart nginx');

// Notify
console.log('βœ… Deployment complete!');
await $`curl -X POST https://hooks.slack.com/services/... -d '{"text":"Deployed to ${target}"}'`;

Working with Monorepos​

npm Scripts with Lerna/Workspaces:

{
"scripts": {
"build": "lerna run build",
"test": "lerna run test --parallel",
"deploy": "lerna run deploy --scope=@myapp/api"
}
}

Xec Monorepo Support:

# .xec/config.yaml
tasks:
build:
description: Build all packages
parallel: true
steps:
- name: Build Core
cwd: packages/core
command: npm run build
- name: Build API
cwd: packages/api
command: npm run build
- name: Build UI
cwd: packages/ui
command: npm run build

test:
pattern: packages/*/test
command: npm test
parallel: true

deploy:
params:
- name: package
required: true
cwd: packages/${params.package}
command: npm run deploy

Migration Checklist​

Phase 1: Setup​

  • Install Xec: npm install -g @xec-sh/cli
  • Initialize Xec: xec new config
  • Create .xec/config.yaml

Phase 2: Migrate Simple Scripts​

  • Identify simple, standalone scripts
  • Create equivalent Xec tasks
  • Test each task individually
  • Update npm scripts to call Xec

Phase 3: Migrate Complex Workflows​

  • Map sequential command chains
  • Convert to Xec step-based tasks
  • Add proper error handling
  • Implement parallel execution where beneficial

Phase 4: Migrate Environment-Specific Scripts​

  • Define targets for different environments
  • Use Xec's environment variable management
  • Create environment-specific configurations

Phase 5: Complete Migration​

  • Replace all npm scripts with Xec commands
  • Update CI/CD pipelines
  • Update documentation
  • Remove unused dependencies

Compatibility Bridge​

Keep npm scripts as aliases during transition:

{
"scripts": {
"build": "xec build",
"test": "xec test",
"deploy": "xec deploy production",
"dev": "xec dev",
"clean": "xec clean"
}
}

This allows team members to use familiar npm run commands while leveraging Xec's power underneath.

Advanced Features After Migration​

1. Multi-Target Deployment​

// Deploy to multiple servers simultaneously
await Promise.all([
on('server1', 'systemctl restart app'),
on('server2', 'systemctl restart app'),
on('server3', 'systemctl restart app')
]);

2. Conditional Execution​

const branch = await $`git branch --show-current`.text();
if (branch === 'main') {
await $`xec deploy production`;
} else {
await $`xec deploy staging`;
}

3. Interactive Prompts​

import { question } from '@xec-sh/core';

const proceed = await question({
message: 'Deploy to production?',
type: 'confirm',
default: false
});

if (proceed) {
await $`xec deploy production`;
}

4. Progress Tracking​

import { spinner } from '@xec-sh/core';

const spin = spinner('Building application...');
spin.start();

await $`webpack --mode production`;

spin.succeed('Build complete!');

Common Gotchas​

1. Path Differences​

  • npm scripts run from package.json directory
  • Xec runs from project root by default
  • Use cwd option to change working directory

2. Shell Differences​

  • npm uses system shell
  • Xec uses consistent shell across platforms
  • Some shell-specific features may need adjustment

3. Environment Variables​

  • npm scripts inherit all env vars
  • Xec provides controlled environment
  • Explicitly pass needed variables

Getting Help​

  • Run xec new task for task templates
  • Check xec run --help for execution options
  • Visit Xec Documentation

Summary​

Migrating from npm scripts to Xec provides:

  • βœ… Better cross-platform support
  • βœ… TypeScript with full type safety
  • βœ… Parallel execution control
  • βœ… Multi-environment deployment
  • βœ… Advanced error handling
  • βœ… Interactive prompts and progress
  • βœ… Structured task management

Start with simple scripts and gradually migrate complex workflows to experience the full power of Xec!