@omicronenergy/oscd-tooling
v0.0.6
Published
Centralized tooling and CLI for OMICRON Energy OSS plugins (prototype).
Readme
@omicronenergy/oscd-tooling
Centralized development tooling for OpenSCD plugins.
The oscd CLI owns the shared build, lint, test, bundle, deploy, and browser-test tooling so individual plugin repositories can keep their dev dependencies and configuration small.
Goals
- Make the maintenance of multiple OpenSCD plugins more manageable by centralizing shared development tooling.
- Keep tool versions consistent across plugin repositories.
- Move repeated scripts and configuration into one package.
- Let plugin repositories depend on
@omicronenergy/oscd-toolinginstead of declaring every tool directly.
Consumer Setup
Install the tooling package as a dev dependency:
{
"devDependencies": {
"@omicronenergy/oscd-tooling": "^0.0.1"
}
}Recommended consumer scripts:
{
"scripts": {
"lint": "oscd lint",
"format": "oscd lint --format",
"analyze": "oscd analyze",
"build": "oscd build",
"bundle": "oscd bundle",
"test": "oscd test",
"test:watch": "oscd test --watch",
"test:visual": "oscd test --visual",
"test:update": "oscd test --update",
"start": "oscd start",
"start:bundle": "oscd start-bundle",
"updates": "oscd updates",
"updates:write": "oscd updates --write",
"deploy": "oscd deploy",
"prepare": "oscd install-hooks"
}
}Projects can still add package-specific scripts around these commands when they need extra generated assets.
For example, if a project must generate an asset into dist between cleaning and bundling, it can use oscd clean && <generate-assets> && oscd bundle --no-clean.
Migrating a Plugin
When moving an existing plugin to @omicronenergy/oscd-tooling, keep the plugin-specific runtime dependencies, but remove toolchain dependencies that are now owned by this package.
Dev dependencies usually no longer needed in the plugin:
- ESLint packages:
eslint,@eslint/js,@typescript-eslint/*,@stylistic/eslint-plugin,eslint-plugin-* - Build and bundle tools:
typescript,rollup,@rollup/plugin-*,rollup-plugin-*,rimraf,concurrently - Test runner tools:
@web/test-runner,@web/test-runner-*,@web/dev-server* - Analysis and docs tools:
@custom-elements-manifest/analyzer,typedoc - Release and hook tools:
gh-pages,husky,commitlint,@commitlint/*,lint-staged - Browser test tooling:
playwrightwhen it is only needed by Web Test Runner
Files usually removable from the plugin:
rollup.config.jsweb-test-runner.config.jsweb-dev-server.config.jscommitlint.config.js.husky/
Files usually kept, but simplified:
tsconfig.json: extend the shared base config, but keep project-relative paths locally.package.json: keep plugin metadata, runtime dependencies, exports, files, project-specific scripts, and the@omicronenergy/oscd-toolingdev dependency..githooks/: generated byoscd install-hooks; keep the generatedpre-commitandcommit-msghooks if they are committed..github/workflows/*: keep CI/release workflows unless those are moved to shared GitHub workflow templates separately..editorconfig,.gitignore, release config, README, and plugin-specific docs.
Minimal tsconfig.json for most plugins:
{
"extends": "@omicronenergy/oscd-tooling/configs/base.tsconfig.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": "src"
},
"include": ["src/**/*.ts"]
}Do not move rootDir, outDir, or include into the shared base config. TypeScript resolves relative paths from the config file that declares them, so these must stay in each consuming project.
Optional local files:
eslint.config.js: not required byoscd lint, but useful for editor integrations. If kept, make it a thin wrapper:import oscdEsLintConfig from '@omicronenergy/oscd-tooling/configs/eslint.config.js'; export default [...oscdEsLintConfig];Project-specific generated assets: keep local scripts around
oscdcommands. For example:{ "scripts": { "marked": "marked -i src/about.md -o src/about.html && cp src/about.html dist/about.html", "build": "oscd build && npm run marked", "bundle": "oscd clean && npm run marked && oscd bundle --no-clean" } }
Direct test imports still matter. If test source imports a package directly, for example import { spy } from 'sinon', that package is part of the plugin's test source dependency graph and should remain declared in the plugin unless it is re-exported through a shared testing API.
The same applies to @open-wc/testing if tests import fixture, expect, or other helpers from it directly.
Commands
oscd lint
Runs ESLint using the shared ESLint config from this package.
Useful options:
oscd lint --format--format runs ESLint with automatic fixes enabled.
oscd analyze
Runs Custom Elements Manifest analysis for the consuming plugin.
oscd build
Runs clean, then the TypeScript build in the consuming repository.
Useful options:
oscd build --analyze
oscd build --no-clean--analyze runs Custom Elements Manifest analysis before the TypeScript build. Analysis is not run by default.
oscd clean
Removes generated build output from dist, while preserving snapshot output.
oscd bundle
Runs clean, then Rollup using the shared Rollup config.
Useful options:
oscd bundle --analyze
oscd bundle --no-clean--analyze runs Custom Elements Manifest analysis before bundling. Analysis is not run by default.
oscd test
Builds the consuming project and runs Web Test Runner using the shared WTR config.
Useful options:
oscd test --watch
oscd test --visual
oscd test --updateoscd start
Runs the development build/watch server. Pass --analyze to run Custom Elements Manifest analysis before the initial build.
oscd start-bundle
Runs the bundled development server. Pass --analyze to run Custom Elements Manifest analysis before the initial bundle.
oscd versions
Prints the versions of the tooling packages resolved from @omicronenergy/oscd-tooling.
oscd updates
Runs npm-check-updates (ncu) from the centralized tooling dependency, using the
shared ncurc.config.js policy (see Dependency Update Policy
below).
Without options, it runs ncu --interactive, so upgrades allowed by the policy can
still be picked and choosen from manually.
Useful options:
oscd updates --write--write applies every upgrade the policy allows directly to package.json and runs
npm install, non-interactively. Intended for a scripted/CI-friendly "safe update"
flow, e.g. a postinstall-free periodic job or a local npm run update script.
oscd install-hooks
Installs the Git hooks expected by the tooling package.
oscd install-playwright
Runs playwright install --with-deps from the centralized Playwright dependency.
oscd commitlint
Runs commitlint. This is primarily intended for Git hooks.
oscd deploy
Deploys the bundled plugin output with gh-pages.
Shared Configs
The package exposes shared configs under configs/:
base.tsconfig.jsoneslint.config.jsrollup.config.jscommitlint.config.jsweb-test-runner.config.jslint-staged.config.jsncurc.config.js
These are defaults, not requirements. oscd resolves each tool's config from
the consuming repo first and only falls back to the shared config when no
local file is present:
Override — drop a file with the same name (e.g.
rollup.config.js,web-test-runner.config.js,eslint.config.js,commitlint.config.js,lint-staged.config.js,ncurc.config.js) at the repo root.oscdpicks it up automatically and uses it in place of the shared default, no CLI flags needed. Useful when a project's needs (multi-entry bundling, custom test harness HTML, etc.) genuinely diverge from the shared default.Extend — import the shared config from
@omicronenergy/oscd-tooling/configs/<name>inside your own local config file and compose with it, instead of replacing it wholesale:// rollup.config.js import base from '@omicronenergy/oscd-tooling/configs/rollup.config.js'; export default [...base, /* extra entry */];This keeps you aligned with tooling's defaults (and their future updates) while adding project-specific pieces on top. All files under
configs/are published for this purpose.
Consumer TypeScript configs should extend the shared base config:
{
"extends": "@omicronenergy/oscd-tooling/configs/base.tsconfig.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": "src"
},
"include": ["src/**/*.ts"]
}The base config also centralizes test type visibility, such as Mocha globals, through the tooling package.
Dependency Update Policy
oscd updates (and this package's own npm run update / npm run updates scripts)
resolve their target versions from configs/ncurc.config.js, a shared
npm-check-updates config.
By default, ncu proposes the latest version of every dependency, major bumps
included. latest is not always safe: a dependency's newest major release can be
incompatible with the rest of the toolchain even though ncu has no way to know that.
For example, typescript-eslint declares a peer dependency of
"typescript": ">=4.8.4 <6.1.0" (the upper bound is exclusive), but typescript@latest
on npm is already 7.x (the new native/Go compiler) - blindly taking that upgrade
breaks linting outright. TypeScript is pinned to ~6.0.3, the newest release that still
satisfies typescript-eslint's peer range, with a ~ (patch-only) range so it can't
silently drift to the unsupported 6.1.x.
configs/ncurc.config.js encodes this as a small, explicit, per-package target
function: most packages default to latest, but a short, curated list is held back
because either:
- a major (or, for
typescript, even a minor) bump is known to break the toolchain today, or - the package belongs to a family that must be upgraded together, deliberately, and
tested as a group rather than piecemeal (e.g. the
@web/test-runner*/@web/dev-server*family, and@open-wc/testing).
The list is intentionally conservative and will go stale as upstream packages resolve these constraints; remove an entry once the underlying incompatibility is fixed and the upgrade has been verified.
This is only a default, not a hard requirement. Consumers can drop their own
ncurc.config.js at their repo root, or extend the shared one:
// ncurc.config.js
import base from '@omicronenergy/oscd-tooling/configs/ncurc.config.js';
export default { ...base, reject: ['some-pinned-package'] };Dependency Model
Tools executed by oscd are dependencies of this package. The CLI resolves those tools from @omicronenergy/oscd-tooling and executes them with the consuming repository as process.cwd().
This means plugin repositories generally do not need direct dev dependencies for tools such as ESLint, Rollup, TypeScript, Web Test Runner, Playwright, or Custom Elements Manifest Analyzer.
Code imported directly by plugin source or test files is different. If a plugin test imports a package by name, for example import { spy } from 'sinon', then that package must either be a direct dependency of the plugin repository or be re-exported through an explicit shared testing API.
Notes
oscd install-playwrightmay require elevated system permissions on Linux because Playwright's--with-depscan install OS packages.oscd updatesis interactive by default and intended to be run manually; useoscd updates --writefor a non-interactive, scriptable/CI-friendly upgrade that still respects the sharedncurc.config.jspolicy.- Commands are designed to run from the consuming repository, not from the tooling package directory.
