An ESBuild plugin for developing Scriptable iOS app scripts with modern JavaScript tooling.
- Node.js 14 or later
- esbuild 0.24 or later
- Scriptable iOS app with iCloud Drive sync enabled
- Automatically adds Scriptable metadata banner to output files
- Supports manifest files for script configuration
- Uses manifest name as output filename (if specified)
- Handles deployment to iCloud Drive with auto-detection
- Full TypeScript support with type definitions
- File filtering with glob pattern matching
- Custom manifest path mapping
- Configurable warning behaviors
- Continues building even when deployment fails (configurable)
- Detailed deployment logging options
npm install esbuild-plugin-scriptable --save-dev
pnpm add esbuild-plugin-scriptable --save-dev
yarn add esbuild-plugin-scriptable --dev
import {build} from 'esbuild';
import {scriptableBanner, scriptableDeploy} from 'esbuild-plugin-scriptable';
await build({
entryPoints: ['src/widget.ts'],
outfile: 'dist/widget.js',
bundle: true,
plugins: [
scriptableBanner({
warnOnMissingManifest: true,
patterns: '**/*.widget.ts',
}),
scriptableDeploy({
logLevel: 'verbose',
patterns: '**/*.widget.ts',
}),
],
});
-
manifestExtensions
: Custom manifest file extensions (default: ['.manifest.json', '.manifest', '.json']) -
warnOnMissingManifest
: Show warning when manifest file is missing (default: true) -
warnOnInvalidManifest
: Show warning when manifest file is invalid (default: true) -
warnOnMissingEntry
: Show warning when no matching entry point is found (default: true) -
getManifestPath
: Custom manifest file path mapping function -
patterns
: Source file patterns to match entry points (default: ['**/*.{ts,tsx,js,jsx}'])- Can be a string or array of strings
- Supports glob patterns with negation (! prefix)
- Examples:
-
'**/*.widget.ts'
- only process widget source files -
['**/*.ts', '!**/*.test.ts']
- exclude test files
-
-
logLevel
: Control logging detail (default: 'auto')- 'auto': Use esbuild's logLevel
- 'silent': No logs
- 'error': Only errors
- 'warn': Errors and warnings
- 'info': Normal output
- 'verbose': Detailed output
-
logLevel
: Control deployment logging detail (default: 'normal')- 'silent': No logs
- 'normal': Basic deployment info
- 'verbose': Detailed deployment info
- 'debug': All deployment details
-
continueOnError
: Continue building when deployment fails (default: true) -
scriptableDir
: Custom Scriptable directory path (default: auto-detect in iCloud Drive) -
patterns
: Source file patterns to match entry points (default: ['**/*.{ts,tsx,js,jsx}'])- Can be a string or array of strings
- Supports glob patterns with negation (! prefix)
- Examples:
-
'**/*.widget.ts'
- only deploy widget source files -
['**/*.ts', '!**/*.test.ts']
- exclude test files
-
-
addBanner
: Whether to add Scriptable banner from manifest (default: true) -
manifestExtensions
: Custom manifest file extensions (default: ['.manifest.json', '.manifest', '.json'])
The manifest file can be used to configure both the script metadata and the output filename:
{
"name": "weather-widget",
"alwaysRunInApp": true,
"shareSheetInputs": ["file-url", "url"],
"iconColor": "blue",
"iconGlyph": "cloud",
"version": "1.0.0",
"description": "A weather widget for Scriptable"
}
When a manifest includes a name
property:
- The deploy plugin will use this name for the output file (e.g.,
weather-widget.js
) - If no name is specified, the original filename will be used
- The banner plugin will still use all manifest properties to generate the script metadata
import {build} from 'esbuild';
import {scriptableBanner, scriptableDeploy} from 'esbuild-plugin-scriptable';
await build({
entryPoints: ['src/widget.ts'],
outfile: 'dist/widget.js',
bundle: true,
plugins: [
scriptableBanner({
warnOnMissingManifest: true,
patterns: ['**/*.ts', '!**/*.test.ts'],
getManifestPath: entryPath => {
return entryPath.replace(/\.ts$/, '.manifest.json');
},
}),
scriptableDeploy({
logLevel: 'verbose',
addBanner: true,
patterns: ['**/*.ts', '!**/*.test.ts'],
scriptableDir: '/custom/path/to/Scriptable',
}),
],
});
The banner can be added in two stages:
-
Build stage (via
scriptableBanner
plugin):- Adds banner to the build output files
- Banner is part of the compiled result
-
Deploy stage (via
scriptableDeploy
plugin'saddBanner
option):- Does not affect build output files
- Only adds banner to the deployed files in Scriptable directory
- Will update/override any existing banner in the target file
When using both plugins:
-
scriptableBanner
will add banner to build output -
scriptableDeploy
withaddBanner: true
will update/override the banner in deployed files -
scriptableDeploy
withaddBanner: false
will keep the existing banner (if any)
// Example 1: Banner in both build output and deployed files
plugins: [
scriptableBanner({...}), // Adds banner to build output
scriptableDeploy({
addBanner: true // Updates banner in deployed files
})
]
// Example 2: Banner only in deployed files
plugins: [
scriptableDeploy({
addBanner: true // Adds banner only to deployed files
})
]
// Example 3: Banner only in build output
plugins: [
scriptableBanner({...}), // Adds banner to build output
scriptableDeploy({
addBanner: false // Keeps existing banner in deployed files
})
]
The addBanner
option in scriptableDeploy
:
- Only affects the files in Scriptable directory
- Does not modify the build output files
- When
true
, will ensure the latest banner from manifest is used in deployed files - When
false
, will preserve any existing banner in the files
Apache-2.0