mirror of
https://github.com/Heretek-Games/drop-plugin-sdk.git
synced 2026-11-07 10:06:00 -10:00
Merge pull request #25 from Heretek-Games/feat/sdk-0.7.0-templates
feat(sdk): v0.7.0 settings contract + complete template release pipeline (#24)
This commit is contained in:
56 files changed
+1580
-41
No files matched your search
@@ -0,0 +1,2 @@
|
||||
sonar.exclusions=templates/**
|
||||
sonar.cpd.exclusions=templates/**
|
||||
@@ -7,7 +7,7 @@
|
||||
## 1. Architecture
|
||||
|
||||
- **`packages/plugin-sdk/`**: Contains `@drop-oss/plugin-sdk`.
|
||||
- `src/types.ts`: Pure TypeScript types matching Drop server's plugin manager contract and desktop client runtime (`PLUGIN_API_VERSION = 2`), including the `RunnerProvider` (`game:runner`), `AuthProvider` (`auth:provider`), `DepotStorageProvider` (`storage:depot`), and `settingsSchema` contracts.
|
||||
- `src/types.ts`: Pure TypeScript types matching Drop server's plugin manager contract and desktop client runtime (`PLUGIN_API_VERSION = 3`), including the `RunnerProvider` (`game:runner`), `AuthProvider` (`auth:provider`), `DepotStorageProvider` (`storage:depot`), `settingsSchema`, and `PluginContext.settings` contracts.
|
||||
- `src/mock.ts`: `MockPluginContext` & `MockPluginStorage` for server plugin testing.
|
||||
- `src/client-mock.ts`: `MockClientPluginContext`, `MockScopedGameFs`, and `MockScopedGameScanner` for desktop client testing.
|
||||
- `src/conformance.ts`: Manifest capability conformance helpers, generic over `PluginCapability`.
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
## 2. Invariants
|
||||
|
||||
- **`PLUGIN_API_VERSION` compatibility**: Always keep `PLUGIN_API_VERSION = 2` in sync with Drop core. Bumping the version requires major semver bump.
|
||||
- **`PLUGIN_API_VERSION` compatibility**: Keep `PLUGIN_API_VERSION = 3` in sync with Drop core (`SUPPORTED_API_VERSIONS = [1, 2, 3]`). Pre-1.0 breaking contract changes land in a minor release.
|
||||
- **Capability Gating**: Ensure `MockPluginContext` and `MockClientPluginContext` enforce capability checks.
|
||||
- **Confinement in Signer**: `signer.ts` must always prevent path traversal (`..` or symlink escapes).
|
||||
|
||||
|
||||
@@ -10,8 +10,8 @@ Maintained by [Heretek Games](https://github.com/Heretek-Games/drop-plugin-sdk).
|
||||
|
||||
| Package | Version | Role |
|
||||
| :------------------------------------------------- | :------ | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`@drop-oss/plugin-sdk`** (`packages/plugin-sdk`) | `0.6.3` | Universal runtime types (`PLUGIN_API_VERSION = 2`), JSON Schema, RPC helper client, and test harnesses (`MockPluginContext`, `MockClientPluginContext`). |
|
||||
| **`@drop-oss/plugin-cli`** (`packages/plugin-cli`) | `0.6.3` | Developer CLI (`drop-plugin`) providing scaffolding (`init`), bundling (`build`, `dev`), validation (`validate`), testing (`test`), signing (`sign`), and packaging (`pack`). Vue SFC compilation included. |
|
||||
| **`@drop-oss/plugin-sdk`** (`packages/plugin-sdk`) | `0.7.0` | Universal runtime types (`PLUGIN_API_VERSION = 3`), JSON Schema, RPC helper client, and test harnesses (`MockPluginContext`, `MockClientPluginContext`). |
|
||||
| **`@drop-oss/plugin-cli`** (`packages/plugin-cli`) | `0.7.0` | Developer CLI (`drop-plugin`) providing scaffolding (`init`), bundling (`build`, `dev`), validation (`validate`), testing (`test`), signing (`sign`), and packaging (`pack`). Vue SFC compilation included. |
|
||||
| **`templates/starter-plugin`** | `1.0.0` | Reference starter template for creating full-stack Drop plugins (server + desktop client). |
|
||||
| **`templates/template-client-ui`** | `1.0.0` | Client UI template with real `.vue` Single File Components and scoped CSS. |
|
||||
| **`templates/template-metadata`** | `1.0.0` | Metadata provider (`metadata:provider`) server template. |
|
||||
@@ -131,7 +131,7 @@ npx drop-plugin pack . ./dist-package
|
||||
|
||||
## Architecture & Invariants
|
||||
|
||||
- **`PLUGIN_API_VERSION = 2`**: Target API version matching Drop's plugin manager contract.
|
||||
- **`PLUGIN_API_VERSION = 3`**: Target API version matching Drop's plugin manager contract. Drop also accepts versions `1` and `2`.
|
||||
- **Strict Confinement**: `signer` and `pack` enforce strict filesystem confinement, rejecting directory traversal (`..`) and symlinks escaping the bundle directory.
|
||||
- **Capability Gating**: All server and client capabilities must be explicitly declared in `drop-plugin.json`. Undeclared API calls throw in runtime and mock harnesses.
|
||||
- **Zero Runtime Dependencies**: `@drop-oss/plugin-sdk` contains zero third-party runtime dependencies.
|
||||
|
||||
@@ -77,7 +77,7 @@ The `drop-plugin.json` manifest declares plugin metadata, execution targets, req
|
||||
|
||||
### Architecture & Security
|
||||
|
||||
- **`apiVersion`** (`integer`, required): Target Drop Plugin API version. Drop core accepts `SUPPORTED_API_VERSIONS` (`[1, 2]`); a manifest that omits it is rejected.
|
||||
- **`apiVersion`** (`integer`, required): Target Drop Plugin API version. Drop core accepts `SUPPORTED_API_VERSIONS` (`[1, 2, 3]`); a manifest that omits it is rejected. Version `3` adds the host-populated `PluginContext.settings` snapshot.
|
||||
- **`trust`** (`"trusted" | "sandboxed"`, default `"trusted"`): Trust tier. Currently, plugins run in-process as `"trusted"`.
|
||||
- **`storageVersion`** (`integer`, optional): Integer schema version for persistent storage migrations.
|
||||
- **`targets`** (`Array<"server" | "client">`, optional): Platforms the plugin provides code for.
|
||||
@@ -110,6 +110,11 @@ The `drop-plugin.json` manifest declares plugin metadata, execution targets, req
|
||||
- `options` (`Array<{ label, value }>`, optional): Choices for `select` fields.
|
||||
- `required` (`boolean`, optional): Whether the field must be set.
|
||||
|
||||
The host persists rendered values in the plugin's namespaced storage and exposes
|
||||
the snapshot as `PluginContext.settings` before `init` (plugin API `3`). Password
|
||||
values are available in-process but are redacted from settings API responses and
|
||||
logs.
|
||||
|
||||
```json
|
||||
{
|
||||
"settingsSchema": {
|
||||
|
||||
@@ -48,7 +48,7 @@ The CLI:
|
||||
|
||||
You can automate building, testing, signing, and releasing `.dropplugin` packages on every Git tag release.
|
||||
|
||||
Use `templates/release-action.yml` as the canonical per-repository release workflow: it runs `npm ci`, builds, tests, validates the manifest, packs the archive, attaches a `.sha256` checksum, and publishes a prerelease automatically for tags like `v1.0.0-rc.1`.
|
||||
Use `templates/release-action.yml` as the canonical per-repository release workflow: it installs dependencies (`npm ci` when a lockfile exists, `npm install` otherwise), builds, tests, validates the manifest, packs the archive, attaches a `.sha256` checksum, and publishes a prerelease automatically for tags like `v1.0.0-rc.1`. Every scaffolded template under `templates/` ships a copy of this workflow, a matching `ci.yml`, a `typecheck` script, and a `.sdk-scope.json` + `scripts/switch-sdk-scope.mjs` pair, so a freshly scaffolded repo can tag `v1.0.0` and release without manual edits.
|
||||
|
||||
|
||||
---
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@drop-oss/plugin-cli",
|
||||
"version": "0.6.3",
|
||||
"version": "0.7.0",
|
||||
"description": "Drop Plugin build, test, signing, and packaging CLI for Drop OSS",
|
||||
"type": "module",
|
||||
"bin": {
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
import path from "node:path";
|
||||
import { cp, mkdir, readFile, writeFile, stat } from "node:fs/promises";
|
||||
import {
|
||||
cp,
|
||||
mkdir,
|
||||
readFile,
|
||||
writeFile,
|
||||
stat,
|
||||
readdir,
|
||||
realpath,
|
||||
} from "node:fs/promises";
|
||||
|
||||
export type TemplateType =
|
||||
| "starter"
|
||||
@@ -16,6 +24,150 @@ export interface InitOptions {
|
||||
template?: string;
|
||||
}
|
||||
|
||||
/** npm scopes the templates may resolve the SDK/CLI from. */
|
||||
const SDK_SCOPES = new Set(["@drop-oss", "@droposs", "@drop"]);
|
||||
const SKIP_DIRS = new Set([
|
||||
"node_modules",
|
||||
".git",
|
||||
"dist",
|
||||
"dist-package",
|
||||
"dist-packages",
|
||||
]);
|
||||
const TEXT_EXT = new Set([
|
||||
".ts",
|
||||
".tsx",
|
||||
".js",
|
||||
".mjs",
|
||||
".cjs",
|
||||
".json",
|
||||
".md",
|
||||
".yml",
|
||||
".yaml",
|
||||
]);
|
||||
|
||||
interface SdkScopeConfig {
|
||||
sdk: string;
|
||||
sdkVersion: string;
|
||||
cliVersion: string;
|
||||
}
|
||||
|
||||
function parseSdkScope(text: string): SdkScopeConfig | null {
|
||||
let parsed: Record<string, unknown>;
|
||||
try {
|
||||
parsed = JSON.parse(text);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
const { sdk, sdkVersion, cliVersion } = parsed;
|
||||
if (
|
||||
typeof sdk !== "string" ||
|
||||
typeof sdkVersion !== "string" ||
|
||||
typeof cliVersion !== "string" ||
|
||||
!SDK_SCOPES.has(sdk)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
return { sdk, sdkVersion, cliVersion };
|
||||
}
|
||||
|
||||
function rewriteSpecifiers(text: string, scope: string): string {
|
||||
let out = text;
|
||||
for (const other of SDK_SCOPES) {
|
||||
if (other === scope) continue;
|
||||
out = out
|
||||
.replaceAll(`${other}/plugin-sdk`, `${scope}/plugin-sdk`)
|
||||
.replaceAll(`${other}/plugin-cli`, `${scope}/plugin-cli`);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function parseDepKind(dep: string): "plugin-sdk" | "plugin-cli" | null {
|
||||
if (dep.endsWith("/plugin-sdk")) return "plugin-sdk";
|
||||
if (dep.endsWith("/plugin-cli")) return "plugin-cli";
|
||||
return null;
|
||||
}
|
||||
|
||||
function rewriteSection(
|
||||
section: Record<string, string>,
|
||||
config: SdkScopeConfig,
|
||||
): void {
|
||||
for (const dep of Object.keys(section)) {
|
||||
const kind = parseDepKind(dep);
|
||||
if (!kind) continue;
|
||||
delete section[dep];
|
||||
section[`${config.sdk}/${kind}`] =
|
||||
kind === "plugin-sdk" ? config.sdkVersion : config.cliVersion;
|
||||
}
|
||||
}
|
||||
|
||||
function rewritePackageDeps(text: string, config: SdkScopeConfig): string {
|
||||
let pkg: Record<string, unknown>;
|
||||
try {
|
||||
pkg = JSON.parse(text);
|
||||
} catch {
|
||||
return text;
|
||||
}
|
||||
const sections = [
|
||||
"dependencies",
|
||||
"devDependencies",
|
||||
"peerDependencies",
|
||||
"optionalDependencies",
|
||||
];
|
||||
for (const sectionName of sections) {
|
||||
const section = pkg[sectionName] as Record<string, string> | undefined;
|
||||
if (section) {
|
||||
rewriteSection(section, config);
|
||||
}
|
||||
}
|
||||
return JSON.stringify(pkg, null, 2) + "\n";
|
||||
}
|
||||
|
||||
async function walkFiles(
|
||||
dir: string,
|
||||
files: string[] = [],
|
||||
visited = new Set<string>(),
|
||||
): Promise<string[]> {
|
||||
const real = await realpath(dir).catch(() => dir);
|
||||
if (visited.has(real)) return files;
|
||||
visited.add(real);
|
||||
for (const entry of await readdir(dir, { withFileTypes: true })) {
|
||||
if (SKIP_DIRS.has(entry.name)) continue;
|
||||
if (entry.isSymbolicLink()) continue;
|
||||
const full = path.join(dir, entry.name);
|
||||
if (entry.isDirectory()) await walkFiles(full, files, visited);
|
||||
else files.push(full);
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewrites a freshly copied template so its `@drop-oss` monorepo specifiers
|
||||
* and `workspace:*` dependencies point at the scope/version declared in the
|
||||
* template's `.sdk-scope.json`. Without this a scaffolded repo keeps
|
||||
* `workspace:*` and cannot install outside the SDK monorepo.
|
||||
*/
|
||||
export async function applySdkScope(targetPath: string): Promise<boolean> {
|
||||
const raw = await readFile(
|
||||
path.join(targetPath, ".sdk-scope.json"),
|
||||
"utf-8",
|
||||
).catch(() => null);
|
||||
if (raw === null) return false;
|
||||
const config = parseSdkScope(raw);
|
||||
if (!config) return false;
|
||||
|
||||
for (const file of await walkFiles(targetPath)) {
|
||||
if (path.basename(file).match(/lock\.(json|yaml|lockb)$/)) continue;
|
||||
if (!TEXT_EXT.has(path.extname(file))) continue;
|
||||
const before = await readFile(file, "utf-8");
|
||||
let after = rewriteSpecifiers(before, config.sdk);
|
||||
if (path.basename(file) === "package.json") {
|
||||
after = rewritePackageDeps(after, config);
|
||||
}
|
||||
if (after !== before) await writeFile(file, after);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export function resolveTemplateFolder(templateName: string = "starter"): string {
|
||||
const norm = templateName.replace(/^template-/, "").toLowerCase();
|
||||
switch (norm) {
|
||||
@@ -90,6 +242,10 @@ export async function initPlugin(
|
||||
},
|
||||
});
|
||||
|
||||
// Point the scaffolded repo at the configured npm scope/version instead of
|
||||
// the monorepo's `workspace:*` links.
|
||||
await applySdkScope(targetPath);
|
||||
|
||||
const pluginId =
|
||||
options.id ||
|
||||
path
|
||||
|
||||
@@ -135,6 +135,43 @@ test("initPlugin scaffolds various templates correctly", async () => {
|
||||
.catch(() => null),
|
||||
);
|
||||
|
||||
// Every template ships the publishing pipeline, scope switch, typecheck
|
||||
// script, and concrete (non-workspace) SDK dependencies.
|
||||
for (const [dir, template] of [
|
||||
[uiDir, "client-ui"],
|
||||
[metaDir, "metadata"],
|
||||
[storeDir, "store"],
|
||||
[runnerDir, "runner"],
|
||||
[fullDir, "fullstack"],
|
||||
] as const) {
|
||||
for (const rel of [
|
||||
".github/workflows/release.yml",
|
||||
".github/workflows/ci.yml",
|
||||
".sdk-scope.json",
|
||||
"scripts/switch-sdk-scope.mjs",
|
||||
]) {
|
||||
assert.ok(
|
||||
await fs.stat(path.join(dir, rel)).catch(() => null),
|
||||
`${template} is missing ${rel}`,
|
||||
);
|
||||
}
|
||||
const pkg = JSON.parse(
|
||||
await fs.readFile(path.join(dir, "package.json"), "utf-8"),
|
||||
);
|
||||
assert.ok(pkg.scripts.typecheck, `${template} is missing a typecheck script`);
|
||||
assert.equal(pkg.dependencies["@droposs/plugin-sdk"], "^0.7.0");
|
||||
assert.equal(pkg.devDependencies["@droposs/plugin-cli"], "^0.7.0");
|
||||
assert.equal(pkg.dependencies["@drop-oss/plugin-sdk"], undefined);
|
||||
assert.equal(pkg.devDependencies["@drop-oss/plugin-cli"], undefined);
|
||||
}
|
||||
|
||||
const runnerSrc = await fs.readFile(
|
||||
path.join(runnerDir, "src/client.ts"),
|
||||
"utf-8",
|
||||
);
|
||||
assert.match(runnerSrc, /@droposs\/plugin-sdk/);
|
||||
assert.doesNotMatch(runnerSrc, /@drop-oss\/plugin-sdk/);
|
||||
|
||||
await fs.rm(tmp, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
|
||||
@@ -227,9 +227,20 @@ test("initPlugin scaffolds a new plugin repository", async () => {
|
||||
await fs.readFile(path.join(targetDir, "package.json"), "utf-8"),
|
||||
);
|
||||
assert.equal(pkg.name, "drop-my-custom-plugin");
|
||||
assert.equal(pkg.dependencies["@droposs/plugin-sdk"], "^0.7.0");
|
||||
assert.equal(pkg.devDependencies["@droposs/plugin-cli"], "^0.7.0");
|
||||
assert.equal(pkg.scripts.typecheck, "tsc --noEmit");
|
||||
|
||||
const srcExists = await fs.stat(path.join(targetDir, "src", "index.ts"));
|
||||
assert.ok(srcExists.isFile());
|
||||
const src = await fs.readFile(path.join(targetDir, "src", "index.ts"), "utf-8");
|
||||
assert.match(src, /@droposs\/plugin-sdk/);
|
||||
assert.doesNotMatch(src, /@drop-oss\/plugin-sdk/);
|
||||
assert.ok(
|
||||
await fs
|
||||
.stat(path.join(targetDir, ".github", "workflows", "release.yml"))
|
||||
.catch(() => null),
|
||||
);
|
||||
} finally {
|
||||
await fs.rm(tmpDir, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@drop-oss/plugin-sdk",
|
||||
"version": "0.6.3",
|
||||
"version": "0.7.0",
|
||||
"description": "Drop Plugin SDK contract, types, and mock testing harness for Drop OSS",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
|
||||
@@ -47,8 +47,8 @@
|
||||
},
|
||||
"apiVersion": {
|
||||
"type": "integer",
|
||||
"enum": [1, 2],
|
||||
"description": "Target Drop Plugin API version. Required; supported values are 1 and 2"
|
||||
"enum": [1, 2, 3],
|
||||
"description": "Target Drop Plugin API version. Required; supported values are 1, 2, and 3"
|
||||
},
|
||||
"trust": {
|
||||
"type": "string",
|
||||
|
||||
@@ -70,6 +70,7 @@ export class MockPluginContext implements PluginContext {
|
||||
public id: string;
|
||||
public logger: PluginLogger;
|
||||
public storage: PluginStorage;
|
||||
public settings: Record<string, unknown>;
|
||||
public capabilities: Set<PluginCapability>;
|
||||
|
||||
public routes = new Map<
|
||||
@@ -92,10 +93,12 @@ export class MockPluginContext implements PluginContext {
|
||||
"events",
|
||||
"network",
|
||||
],
|
||||
settings: Record<string, unknown> = {},
|
||||
) {
|
||||
this.id = id;
|
||||
this.logger = new MockPluginLogger();
|
||||
this.capabilities = new Set(capabilities);
|
||||
this.settings = settings;
|
||||
this.storage = this.capabilities.has("storage")
|
||||
? new MockPluginStorage()
|
||||
: guardStorage(id);
|
||||
|
||||
@@ -3,14 +3,15 @@
|
||||
* contract changes incompatibly. Plugins declare the version they were built
|
||||
* against in `metadata.apiVersion`; mismatches are rejected at registration.
|
||||
*/
|
||||
export const PLUGIN_API_VERSION = 2;
|
||||
export const PLUGIN_API_VERSION = 3;
|
||||
|
||||
/**
|
||||
* Plugin API versions Drop accepts at registration. `1` covers legacy
|
||||
* single-target bundles, `2` the universal server/client manifest. Declaring
|
||||
* an unsupported version (or omitting `apiVersion` entirely) fails closed.
|
||||
* single-target bundles, `2` the universal server/client manifest, and `3`
|
||||
* adds host-populated `PluginContext.settings`. Declaring an unsupported
|
||||
* version (or omitting `apiVersion` entirely) fails closed.
|
||||
*/
|
||||
export const SUPPORTED_API_VERSIONS = [1, 2] as const;
|
||||
export const SUPPORTED_API_VERSIONS = [1, 2, 3] as const;
|
||||
|
||||
/**
|
||||
* Current bundle signature scheme. `2` means `signature` covers the file
|
||||
@@ -105,7 +106,7 @@ export interface PluginMetadata {
|
||||
/**
|
||||
* Plugin API version the plugin was built against. Required: Drop rejects
|
||||
* plugins that omit it rather than defaulting to the current version.
|
||||
* Supported values are `SUPPORTED_API_VERSIONS` (`1` and `2`).
|
||||
* Supported values are `SUPPORTED_API_VERSIONS` (`1`, `2`, and `3`).
|
||||
*/
|
||||
apiVersion: number;
|
||||
/** Trust tier. Defaults to "trusted". */
|
||||
@@ -298,6 +299,14 @@ export interface PluginContext {
|
||||
id: string;
|
||||
logger: PluginLogger;
|
||||
storage: PluginStorage;
|
||||
/**
|
||||
* Read-only snapshot of the values the host persisted for this plugin's
|
||||
* `metadata.settingsSchema`, loaded before `init`. Includes `password`
|
||||
* fields because plugins run trusted in-process; the host still redacts
|
||||
* those values from settings API responses and logs. Empty or absent when
|
||||
* the plugin declares no `settingsSchema`.
|
||||
*/
|
||||
settings?: Readonly<Record<string, unknown>>;
|
||||
registerRoute(
|
||||
method: HttpMethod,
|
||||
pattern: string,
|
||||
|
||||
@@ -12,12 +12,23 @@ import {
|
||||
type ServerCapability,
|
||||
} from "../dist/index.js";
|
||||
|
||||
test("PLUGIN_API_VERSION is 2", () => {
|
||||
assert.equal(PLUGIN_API_VERSION, 2);
|
||||
assert.deepEqual([...SUPPORTED_API_VERSIONS], [1, 2]);
|
||||
test("PLUGIN_API_VERSION is 3", () => {
|
||||
assert.equal(PLUGIN_API_VERSION, 3);
|
||||
assert.deepEqual([...SUPPORTED_API_VERSIONS], [1, 2, 3]);
|
||||
assert.equal(SIGNATURE_VERSION, 2);
|
||||
});
|
||||
|
||||
test("MockPluginContext exposes host-provided settings", () => {
|
||||
const ctx = new MockPluginContext("server-test", ["routes"], {
|
||||
apiKey: "secret",
|
||||
retries: 3,
|
||||
});
|
||||
assert.deepEqual(ctx.settings, { apiKey: "secret", retries: 3 });
|
||||
|
||||
const empty = new MockPluginContext("server-test-empty", ["routes"]);
|
||||
assert.deepEqual(empty.settings, {});
|
||||
});
|
||||
|
||||
test("MockPluginContext enforces capability gating", () => {
|
||||
const ctx = new MockPluginContext("server-test", ["routes"]);
|
||||
ctx.registerRoute("GET", "/test", () => ({ ok: true }));
|
||||
|
||||
@@ -27,7 +27,12 @@ jobs:
|
||||
cache: npm
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci --ignore-scripts
|
||||
run: |
|
||||
if [ -f package-lock.json ]; then
|
||||
npm ci --ignore-scripts
|
||||
else
|
||||
npm install --no-audit --no-fund --ignore-scripts
|
||||
fi
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Build, typecheck, and test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # actions/[email protected]
|
||||
|
||||
- name: Set up Node.js 22
|
||||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # actions/[email protected]
|
||||
with:
|
||||
node-version: "22"
|
||||
cache: npm
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
if [ -f package-lock.json ]; then
|
||||
npm ci --ignore-scripts
|
||||
else
|
||||
npm install --no-audit --no-fund --ignore-scripts
|
||||
fi
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
|
||||
- name: Typecheck
|
||||
run: npm run typecheck
|
||||
|
||||
- name: Test
|
||||
run: npm test
|
||||
|
||||
- name: Validate manifest
|
||||
run: npx --no-install drop-plugin validate .
|
||||
+6
-1
@@ -27,7 +27,12 @@ jobs:
|
||||
cache: npm
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci --ignore-scripts
|
||||
run: |
|
||||
if [ -f package-lock.json ]; then
|
||||
npm ci --ignore-scripts
|
||||
else
|
||||
npm install --no-audit --no-fund --ignore-scripts
|
||||
fi
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
node_modules/
|
||||
dist/
|
||||
dist-package/
|
||||
*.dropplugin
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"sdk": "@droposs",
|
||||
"sdkVersion": "^0.6.2",
|
||||
"cliVersion": "^0.6.2"
|
||||
"sdkVersion": "^0.7.0",
|
||||
"cliVersion": "^0.7.0"
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
import { readdirSync, readFileSync, writeFileSync, existsSync, realpathSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import { execSync } from "node:child_process";
|
||||
import { execFileSync } from "node:child_process";
|
||||
|
||||
const LEGACY_SCOPE = "@droposs";
|
||||
const NEW_SCOPE = "@drop-oss";
|
||||
@@ -122,12 +122,12 @@ const installDir = process.cwd();
|
||||
|
||||
if (existsSync(path.join(installDir, "package-lock.json"))) {
|
||||
try {
|
||||
execSync("npm install --no-audit --no-fund", { cwd: installDir, stdio: "inherit" });
|
||||
execFileSync("npm", ["install", "--no-audit", "--no-fund"], { cwd: installDir, stdio: "inherit" });
|
||||
} catch {
|
||||
execSync("npm install --no-audit --no-fund --legacy-peer-deps", { cwd: installDir, stdio: "inherit" });
|
||||
execFileSync("npm", ["install", "--no-audit", "--no-fund", "--legacy-peer-deps"], { cwd: installDir, stdio: "inherit" });
|
||||
}
|
||||
} else if (existsSync(path.join(installDir, "pnpm-lock.yaml"))) {
|
||||
execSync("pnpm install", { cwd: installDir, stdio: "inherit" });
|
||||
execFileSync("pnpm", ["install"], { cwd: installDir, stdio: "inherit" });
|
||||
} else {
|
||||
console.warn("no lockfile found; skipping dependency install");
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Build, typecheck, and test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # actions/[email protected]
|
||||
|
||||
- name: Set up Node.js 22
|
||||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # actions/[email protected]
|
||||
with:
|
||||
node-version: "22"
|
||||
cache: npm
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
if [ -f package-lock.json ]; then
|
||||
npm ci --ignore-scripts
|
||||
else
|
||||
npm install --no-audit --no-fund --ignore-scripts
|
||||
fi
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
|
||||
- name: Typecheck
|
||||
run: npm run typecheck
|
||||
|
||||
- name: Test
|
||||
run: npm test
|
||||
|
||||
- name: Validate manifest
|
||||
run: npx --no-install drop-plugin validate .
|
||||
@@ -0,0 +1,65 @@
|
||||
name: Release Drop Plugin
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
concurrency:
|
||||
group: release-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: Build, validate, and publish .dropplugin
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # actions/[email protected]
|
||||
|
||||
- name: Set up Node.js 22
|
||||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # actions/[email protected]
|
||||
with:
|
||||
node-version: "22"
|
||||
cache: npm
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
if [ -f package-lock.json ]; then
|
||||
npm ci --ignore-scripts
|
||||
else
|
||||
npm install --no-audit --no-fund --ignore-scripts
|
||||
fi
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
|
||||
- name: Test
|
||||
run: npm test
|
||||
|
||||
- name: Validate manifest
|
||||
run: npx --no-install drop-plugin validate .
|
||||
|
||||
- name: Pack plugin archive
|
||||
run: npx --no-install drop-plugin pack . ./dist-package
|
||||
|
||||
- name: Generate checksums
|
||||
id: pkg
|
||||
run: |
|
||||
FILE=$(ls dist-package/*.dropplugin | head -n 1)
|
||||
echo "path=$FILE" >> "$GITHUB_OUTPUT"
|
||||
echo "name=$(basename "$FILE")" >> "$GITHUB_OUTPUT"
|
||||
sha256sum "$FILE" > "$FILE.sha256"
|
||||
|
||||
- name: Create GitHub release
|
||||
uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2.6.2
|
||||
with:
|
||||
files: |
|
||||
${{ steps.pkg.outputs.path }}
|
||||
${{ steps.pkg.outputs.path }}.sha256
|
||||
name: ${{ steps.pkg.outputs.name }}
|
||||
prerelease: ${{ contains(github.ref_name, '-') }}
|
||||
generate_release_notes: true
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"sdk": "@droposs",
|
||||
"sdkVersion": "^0.7.0",
|
||||
"cliVersion": "^0.7.0"
|
||||
}
|
||||
@@ -27,16 +27,20 @@
|
||||
},
|
||||
"checksum": "639af61b6c28f29ede8d001f5b1545c6c78b3360af1dc68d56fcf502fadaefc7",
|
||||
"files": {
|
||||
".github/workflows/ci.yml": "4741172bed309e809eb7d7a1ca02ce241fc8edeb2430060bbd680a08a9c20f10",
|
||||
".github/workflows/release.yml": "999abcf55a425c3c0fb4dcb8442aca189a252a4967a5a7d82c709223ef46c871",
|
||||
".gitignore": "c7ecb5dd064aa6e25e6d7afb4258dc3132295a306a5c1d343676ef541763d0bd",
|
||||
".sdk-scope.json": "f13936572aca04f0f47da440ed27eb69ea7d5c5b1d0901b69ebd4aa55ce455b6",
|
||||
"dist/src/client.css": "a7e80bda51554013cce91e4a153dd4598f63e8334da9a1d69b6f492d0f848629",
|
||||
"dist/src/client.css.map": "58159ed3c29742d2d044de6e304a03806cf1c6ef0405136cce3e2613e94ffc4f",
|
||||
"dist/src/client.js": "639af61b6c28f29ede8d001f5b1545c6c78b3360af1dc68d56fcf502fadaefc7",
|
||||
"dist/src/client.js.map": "0a5a2907dea7b026cccbf4469520b5a4b4a97185a2a474b6b1d25ce38ac6b640",
|
||||
"dist/test/client.test.js": "7234144f610006dd7b56e689580a68e6bc090c5c4ead137c90313d0e32260708",
|
||||
"package.json": "45f7f8d41de9a5a4c6c246faee3359a3de9a6e89cc2bcc38caeeaff92dabccb4",
|
||||
"package.json": "4cdb8a519181b4e51355d034ff00e4159bde5fc4676e843fc62258c31655afc3",
|
||||
"scripts/switch-sdk-scope.mjs": "ab740ee477a7f5eb07b5344bd12f8c311d51f49279ad4778668e7e2ea4fef798",
|
||||
"src/client.ts": "d6584c900faf826bcc5f45a900853110ff586591a295414c7386ef4c7d76e4c3",
|
||||
"src/components/MyPanel.vue": "f92d837fe157e4cf48ceef4c77d2b1f46e52d1b34e6548163a928ee61df8fe55",
|
||||
"test/client.test.ts": "b357cbe8049dc5f58d98fc3e7f2168e2d94c3c4c4353056794e757cf5b89b2a9",
|
||||
"tsconfig.json": "a31a559ca71e6b0d196fffa84189fbaa4e117b6def7c4f45bd13645fbbfe30b9"
|
||||
"tsconfig.json": "ffd407ef4014fe75cbdc6ad897632157a2d8b8f66a03a4d711c6ac1374f4cde2"
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@
|
||||
"build": "drop-plugin build",
|
||||
"dev": "drop-plugin dev",
|
||||
"test": "node --test test/client.test.ts",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"validate": "drop-plugin validate"
|
||||
},
|
||||
"license": "MIT",
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
#!/usr/bin/env node
|
||||
/* global process, console */
|
||||
|
||||
import { readdirSync, readFileSync, writeFileSync, existsSync, realpathSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import { execFileSync } from "node:child_process";
|
||||
|
||||
const LEGACY_SCOPE = "@droposs";
|
||||
const NEW_SCOPE = "@drop-oss";
|
||||
const SKIP_DIRS = new Set(["node_modules", ".git", "dist", "dist-package", "dist-packages"]);
|
||||
const LOCKFILES = new Set(["package-lock.json", "pnpm-lock.yaml", "yarn.lock", "bun.lockb", "deno.lock"]);
|
||||
const TEXT_EXT = new Set([".ts", ".tsx", ".js", ".mjs", ".cjs", ".json", ".md", ".yml", ".yaml"]);
|
||||
|
||||
const configPath = path.resolve(process.cwd(), ".sdk-scope.json");
|
||||
if (!existsSync(configPath)) {
|
||||
console.error("Missing .sdk-scope.json in repo root");
|
||||
process.exit(1);
|
||||
}
|
||||
const config = JSON.parse(readFileSync(configPath, "utf-8"));
|
||||
const scope = config.sdk;
|
||||
if (scope !== NEW_SCOPE && scope !== LEGACY_SCOPE) {
|
||||
console.error(`Unknown scope '${scope}'; .sdk-scope.json sdk must be '${NEW_SCOPE}' or '${LEGACY_SCOPE}'`);
|
||||
process.exit(1);
|
||||
}
|
||||
const sdkVersion = config["sdkVersion"];
|
||||
const cliVersion = config["cliVersion"];
|
||||
if (typeof sdkVersion !== "string" || typeof cliVersion !== "string") {
|
||||
console.error(".sdk-scope.json must define sdkVersion and cliVersion");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const OTHER_SCOPES = scope === NEW_SCOPE ? [LEGACY_SCOPE] : [NEW_SCOPE];
|
||||
|
||||
function walk(dir, files = [], visited = new Set()) {
|
||||
const real = realpathSync(dir);
|
||||
if (visited.has(real)) return files;
|
||||
visited.add(real);
|
||||
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
||||
const full = path.join(dir, entry.name);
|
||||
if (SKIP_DIRS.has(entry.name)) continue;
|
||||
if (entry.isSymbolicLink()) continue;
|
||||
if (entry.isDirectory()) walk(full, files, visited);
|
||||
else files.push(full);
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
function normalizeSpecifiers(text) {
|
||||
let out = text;
|
||||
for (const other of OTHER_SCOPES) {
|
||||
out = out
|
||||
.replaceAll(`${other}/plugin-sdk`, `${scope}/plugin-sdk`)
|
||||
.replaceAll(`${other}/plugin-cli`, `${scope}/plugin-cli`);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function rewritePackageJson(file, text) {
|
||||
const indent = detectIndent(text);
|
||||
let pkg;
|
||||
try {
|
||||
pkg = JSON.parse(text);
|
||||
} catch {
|
||||
return text;
|
||||
}
|
||||
for (const sectionName of ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"]) {
|
||||
const section = pkg[sectionName];
|
||||
if (!section) continue;
|
||||
const rebuilt = {};
|
||||
for (const dep of Object.keys(section)) {
|
||||
const kind = parseSdkDep(dep);
|
||||
if (!kind) {
|
||||
rebuilt[dep] = section[dep];
|
||||
continue;
|
||||
}
|
||||
const target = `${scope}/${kind}`;
|
||||
rebuilt[target] = kind === "plugin-sdk" ? sdkVersion : cliVersion;
|
||||
}
|
||||
pkg[sectionName] = rebuilt;
|
||||
}
|
||||
return JSON.stringify(pkg, null, indent) + (text.endsWith("\n") ? "\n" : "");
|
||||
}
|
||||
|
||||
function parseSdkDep(dep) {
|
||||
for (const scopeName of [LEGACY_SCOPE, NEW_SCOPE, "@drop"]) {
|
||||
if (dep === `${scopeName}/plugin-sdk`) return "plugin-sdk";
|
||||
if (dep === `${scopeName}/plugin-cli`) return "plugin-cli";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function detectIndent(text) {
|
||||
const match = text.match(/\n([ \t]+)"/);
|
||||
return match ? match[1] : "\t";
|
||||
}
|
||||
|
||||
const edited = [];
|
||||
|
||||
const files = walk(process.cwd());
|
||||
for (const file of files) {
|
||||
if (LOCKFILES.has(path.basename(file))) continue;
|
||||
if (!TEXT_EXT.has(path.extname(file))) continue;
|
||||
const before = readFileSync(file, "utf-8");
|
||||
let after = normalizeSpecifiers(before);
|
||||
if (path.basename(file) === "package.json") {
|
||||
after = rewritePackageJson(file, after);
|
||||
}
|
||||
if (after !== before) {
|
||||
writeFileSync(file, after);
|
||||
edited.push(path.relative(process.cwd(), file));
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`Scope set to '${scope}' (sdk ${sdkVersion}, cli ${cliVersion})`);
|
||||
if (edited.length) {
|
||||
for (const f of edited) console.log(` rewrote ${f}`);
|
||||
} else {
|
||||
console.log(" no specifier changes needed");
|
||||
}
|
||||
|
||||
const installDir = process.cwd();
|
||||
|
||||
if (existsSync(path.join(installDir, "package-lock.json"))) {
|
||||
try {
|
||||
execFileSync("npm", ["install", "--no-audit", "--no-fund"], { cwd: installDir, stdio: "inherit" });
|
||||
} catch {
|
||||
execFileSync("npm", ["install", "--no-audit", "--no-fund", "--legacy-peer-deps"], { cwd: installDir, stdio: "inherit" });
|
||||
}
|
||||
} else if (existsSync(path.join(installDir, "pnpm-lock.yaml"))) {
|
||||
execFileSync("pnpm", ["install"], { cwd: installDir, stdio: "inherit" });
|
||||
} else {
|
||||
console.warn("no lockfile found; skipping dependency install");
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
"esModuleInterop": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"allowJs": true,
|
||||
"outDir": "dist"
|
||||
},
|
||||
"include": ["src/**/*", "test/**/*"]
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Build, typecheck, and test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # actions/[email protected]
|
||||
|
||||
- name: Set up Node.js 22
|
||||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # actions/[email protected]
|
||||
with:
|
||||
node-version: "22"
|
||||
cache: npm
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
if [ -f package-lock.json ]; then
|
||||
npm ci --ignore-scripts
|
||||
else
|
||||
npm install --no-audit --no-fund --ignore-scripts
|
||||
fi
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
|
||||
- name: Typecheck
|
||||
run: npm run typecheck
|
||||
|
||||
- name: Test
|
||||
run: npm test
|
||||
|
||||
- name: Validate manifest
|
||||
run: npx --no-install drop-plugin validate .
|
||||
@@ -0,0 +1,65 @@
|
||||
name: Release Drop Plugin
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
concurrency:
|
||||
group: release-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: Build, validate, and publish .dropplugin
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # actions/[email protected]
|
||||
|
||||
- name: Set up Node.js 22
|
||||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # actions/[email protected]
|
||||
with:
|
||||
node-version: "22"
|
||||
cache: npm
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
if [ -f package-lock.json ]; then
|
||||
npm ci --ignore-scripts
|
||||
else
|
||||
npm install --no-audit --no-fund --ignore-scripts
|
||||
fi
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
|
||||
- name: Test
|
||||
run: npm test
|
||||
|
||||
- name: Validate manifest
|
||||
run: npx --no-install drop-plugin validate .
|
||||
|
||||
- name: Pack plugin archive
|
||||
run: npx --no-install drop-plugin pack . ./dist-package
|
||||
|
||||
- name: Generate checksums
|
||||
id: pkg
|
||||
run: |
|
||||
FILE=$(ls dist-package/*.dropplugin | head -n 1)
|
||||
echo "path=$FILE" >> "$GITHUB_OUTPUT"
|
||||
echo "name=$(basename "$FILE")" >> "$GITHUB_OUTPUT"
|
||||
sha256sum "$FILE" > "$FILE.sha256"
|
||||
|
||||
- name: Create GitHub release
|
||||
uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2.6.2
|
||||
with:
|
||||
files: |
|
||||
${{ steps.pkg.outputs.path }}
|
||||
${{ steps.pkg.outputs.path }}.sha256
|
||||
name: ${{ steps.pkg.outputs.name }}
|
||||
prerelease: ${{ contains(github.ref_name, '-') }}
|
||||
generate_release_notes: true
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"sdk": "@droposs",
|
||||
"sdkVersion": "^0.7.0",
|
||||
"cliVersion": "^0.7.0"
|
||||
}
|
||||
@@ -79,19 +79,23 @@
|
||||
},
|
||||
"checksum": "4be6e9eb395333d01656762da93debc7f50c101ae3e222230e2b75217770580b",
|
||||
"files": {
|
||||
".github/workflows/ci.yml": "4741172bed309e809eb7d7a1ca02ce241fc8edeb2430060bbd680a08a9c20f10",
|
||||
".github/workflows/release.yml": "999abcf55a425c3c0fb4dcb8442aca189a252a4967a5a7d82c709223ef46c871",
|
||||
".gitignore": "c7ecb5dd064aa6e25e6d7afb4258dc3132295a306a5c1d343676ef541763d0bd",
|
||||
".sdk-scope.json": "f13936572aca04f0f47da440ed27eb69ea7d5c5b1d0901b69ebd4aa55ce455b6",
|
||||
"dist/src/client.css": "16fe4969e12025eba710f5d71939827c8e3db98344fb4cb2f6622ff199f49ad3",
|
||||
"dist/src/client.css.map": "aef5ae0e46e30bafcd54d1c0d8a712ca40ac7df0b10ec5254b112345980ef2e0",
|
||||
"dist/src/client.js": "ba9114a7de9420db06682108ae4e4952bf60b570041a79d1efee536bf01f220e",
|
||||
"dist/src/client.js.map": "e83e8e665da64f3cb85692f8529a32f0e10336cafc430a97a34179f4685dc2e5",
|
||||
"dist/src/index.js": "4be6e9eb395333d01656762da93debc7f50c101ae3e222230e2b75217770580b",
|
||||
"dist/src/index.js.map": "0c6c706d98afc1913a45cf484c12d6d79b5ef5186c32c4ef35af7b4ff1c1e18f",
|
||||
"package.json": "e1a2e1aaf319cca92c451a11bebe22a65dcf8e51d6794f7ae90c35355f0e64b1",
|
||||
"package.json": "5cf0bd19d8e597dac4cb1c60c67e8ef0255a5a47c5507720a481599ac7623bfb",
|
||||
"scripts/switch-sdk-scope.mjs": "ab740ee477a7f5eb07b5344bd12f8c311d51f49279ad4778668e7e2ea4fef798",
|
||||
"src/client.ts": "a8f767ad5ded4ae6ef767a46fcaa0c945fd9098e4cd923473053c9e437e21815",
|
||||
"src/components/StatusPanel.vue": "2b8374cfad18715c235b7909ddcd2c45332d664d0f4e89b0e3e6418e5ab02fb6",
|
||||
"src/index.ts": "cff4a6b75e96bae971da4272d6ededde5cd88358b466ba861db54d9051d84d60",
|
||||
"test/client.test.ts": "8fe9659122b3e331688866813195b101a1ca9c543b5f79787bde8c8538211a29",
|
||||
"test/index.test.ts": "a0cc24ae8d19ff423bb717a3edb4c8e029144ed639ff02200d5d5fa4b028528b",
|
||||
"tsconfig.json": "a31a559ca71e6b0d196fffa84189fbaa4e117b6def7c4f45bd13645fbbfe30b9"
|
||||
"test/index.test.ts": "2ca92c3a328f623e82da8d2b38e155dc24ddd91cbfedd69619b56393137bd777",
|
||||
"tsconfig.json": "ffd407ef4014fe75cbdc6ad897632157a2d8b8f66a03a4d711c6ac1374f4cde2"
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@
|
||||
"build": "drop-plugin build",
|
||||
"dev": "drop-plugin dev",
|
||||
"test": "node --test test/index.test.ts test/client.test.ts",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"validate": "drop-plugin validate"
|
||||
},
|
||||
"license": "MIT",
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
#!/usr/bin/env node
|
||||
/* global process, console */
|
||||
|
||||
import { readdirSync, readFileSync, writeFileSync, existsSync, realpathSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import { execFileSync } from "node:child_process";
|
||||
|
||||
const LEGACY_SCOPE = "@droposs";
|
||||
const NEW_SCOPE = "@drop-oss";
|
||||
const SKIP_DIRS = new Set(["node_modules", ".git", "dist", "dist-package", "dist-packages"]);
|
||||
const LOCKFILES = new Set(["package-lock.json", "pnpm-lock.yaml", "yarn.lock", "bun.lockb", "deno.lock"]);
|
||||
const TEXT_EXT = new Set([".ts", ".tsx", ".js", ".mjs", ".cjs", ".json", ".md", ".yml", ".yaml"]);
|
||||
|
||||
const configPath = path.resolve(process.cwd(), ".sdk-scope.json");
|
||||
if (!existsSync(configPath)) {
|
||||
console.error("Missing .sdk-scope.json in repo root");
|
||||
process.exit(1);
|
||||
}
|
||||
const config = JSON.parse(readFileSync(configPath, "utf-8"));
|
||||
const scope = config.sdk;
|
||||
if (scope !== NEW_SCOPE && scope !== LEGACY_SCOPE) {
|
||||
console.error(`Unknown scope '${scope}'; .sdk-scope.json sdk must be '${NEW_SCOPE}' or '${LEGACY_SCOPE}'`);
|
||||
process.exit(1);
|
||||
}
|
||||
const sdkVersion = config["sdkVersion"];
|
||||
const cliVersion = config["cliVersion"];
|
||||
if (typeof sdkVersion !== "string" || typeof cliVersion !== "string") {
|
||||
console.error(".sdk-scope.json must define sdkVersion and cliVersion");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const OTHER_SCOPES = scope === NEW_SCOPE ? [LEGACY_SCOPE] : [NEW_SCOPE];
|
||||
|
||||
function walk(dir, files = [], visited = new Set()) {
|
||||
const real = realpathSync(dir);
|
||||
if (visited.has(real)) return files;
|
||||
visited.add(real);
|
||||
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
||||
const full = path.join(dir, entry.name);
|
||||
if (SKIP_DIRS.has(entry.name)) continue;
|
||||
if (entry.isSymbolicLink()) continue;
|
||||
if (entry.isDirectory()) walk(full, files, visited);
|
||||
else files.push(full);
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
function normalizeSpecifiers(text) {
|
||||
let out = text;
|
||||
for (const other of OTHER_SCOPES) {
|
||||
out = out
|
||||
.replaceAll(`${other}/plugin-sdk`, `${scope}/plugin-sdk`)
|
||||
.replaceAll(`${other}/plugin-cli`, `${scope}/plugin-cli`);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function rewritePackageJson(file, text) {
|
||||
const indent = detectIndent(text);
|
||||
let pkg;
|
||||
try {
|
||||
pkg = JSON.parse(text);
|
||||
} catch {
|
||||
return text;
|
||||
}
|
||||
for (const sectionName of ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"]) {
|
||||
const section = pkg[sectionName];
|
||||
if (!section) continue;
|
||||
const rebuilt = {};
|
||||
for (const dep of Object.keys(section)) {
|
||||
const kind = parseSdkDep(dep);
|
||||
if (!kind) {
|
||||
rebuilt[dep] = section[dep];
|
||||
continue;
|
||||
}
|
||||
const target = `${scope}/${kind}`;
|
||||
rebuilt[target] = kind === "plugin-sdk" ? sdkVersion : cliVersion;
|
||||
}
|
||||
pkg[sectionName] = rebuilt;
|
||||
}
|
||||
return JSON.stringify(pkg, null, indent) + (text.endsWith("\n") ? "\n" : "");
|
||||
}
|
||||
|
||||
function parseSdkDep(dep) {
|
||||
for (const scopeName of [LEGACY_SCOPE, NEW_SCOPE, "@drop"]) {
|
||||
if (dep === `${scopeName}/plugin-sdk`) return "plugin-sdk";
|
||||
if (dep === `${scopeName}/plugin-cli`) return "plugin-cli";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function detectIndent(text) {
|
||||
const match = text.match(/\n([ \t]+)"/);
|
||||
return match ? match[1] : "\t";
|
||||
}
|
||||
|
||||
const edited = [];
|
||||
|
||||
const files = walk(process.cwd());
|
||||
for (const file of files) {
|
||||
if (LOCKFILES.has(path.basename(file))) continue;
|
||||
if (!TEXT_EXT.has(path.extname(file))) continue;
|
||||
const before = readFileSync(file, "utf-8");
|
||||
let after = normalizeSpecifiers(before);
|
||||
if (path.basename(file) === "package.json") {
|
||||
after = rewritePackageJson(file, after);
|
||||
}
|
||||
if (after !== before) {
|
||||
writeFileSync(file, after);
|
||||
edited.push(path.relative(process.cwd(), file));
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`Scope set to '${scope}' (sdk ${sdkVersion}, cli ${cliVersion})`);
|
||||
if (edited.length) {
|
||||
for (const f of edited) console.log(` rewrote ${f}`);
|
||||
} else {
|
||||
console.log(" no specifier changes needed");
|
||||
}
|
||||
|
||||
const installDir = process.cwd();
|
||||
|
||||
if (existsSync(path.join(installDir, "package-lock.json"))) {
|
||||
try {
|
||||
execFileSync("npm", ["install", "--no-audit", "--no-fund"], { cwd: installDir, stdio: "inherit" });
|
||||
} catch {
|
||||
execFileSync("npm", ["install", "--no-audit", "--no-fund", "--legacy-peer-deps"], { cwd: installDir, stdio: "inherit" });
|
||||
}
|
||||
} else if (existsSync(path.join(installDir, "pnpm-lock.yaml"))) {
|
||||
execFileSync("pnpm", ["install"], { cwd: installDir, stdio: "inherit" });
|
||||
} else {
|
||||
console.warn("no lockfile found; skipping dependency install");
|
||||
}
|
||||
@@ -19,7 +19,7 @@ test("MyFullstackPlugin registers status and config routes", async () => {
|
||||
const statusRes = (await status.handler(null, {
|
||||
params: {},
|
||||
query: {},
|
||||
readJson: async () => ({}),
|
||||
readJson: async <T = unknown>() => ({}) as T,
|
||||
})) as { ok: boolean };
|
||||
assert.equal(statusRes.ok, true);
|
||||
|
||||
@@ -28,7 +28,7 @@ test("MyFullstackPlugin registers status and config routes", async () => {
|
||||
const configRes = (await config.handler(null, {
|
||||
params: {},
|
||||
query: {},
|
||||
readJson: async () => ({ mode: "full" }),
|
||||
readJson: async <T = unknown>() => ({ mode: "full" }) as T,
|
||||
})) as { saved: boolean; synced: number };
|
||||
assert.equal(configRes.saved, true);
|
||||
assert.equal(configRes.synced, 1);
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
"esModuleInterop": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"allowJs": true,
|
||||
"outDir": "dist"
|
||||
},
|
||||
"include": ["src/**/*", "test/**/*"]
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Build, typecheck, and test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # actions/[email protected]
|
||||
|
||||
- name: Set up Node.js 22
|
||||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # actions/[email protected]
|
||||
with:
|
||||
node-version: "22"
|
||||
cache: npm
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
if [ -f package-lock.json ]; then
|
||||
npm ci --ignore-scripts
|
||||
else
|
||||
npm install --no-audit --no-fund --ignore-scripts
|
||||
fi
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
|
||||
- name: Typecheck
|
||||
run: npm run typecheck
|
||||
|
||||
- name: Test
|
||||
run: npm test
|
||||
|
||||
- name: Validate manifest
|
||||
run: npx --no-install drop-plugin validate .
|
||||
@@ -0,0 +1,65 @@
|
||||
name: Release Drop Plugin
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
concurrency:
|
||||
group: release-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: Build, validate, and publish .dropplugin
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # actions/[email protected]
|
||||
|
||||
- name: Set up Node.js 22
|
||||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # actions/[email protected]
|
||||
with:
|
||||
node-version: "22"
|
||||
cache: npm
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
if [ -f package-lock.json ]; then
|
||||
npm ci --ignore-scripts
|
||||
else
|
||||
npm install --no-audit --no-fund --ignore-scripts
|
||||
fi
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
|
||||
- name: Test
|
||||
run: npm test
|
||||
|
||||
- name: Validate manifest
|
||||
run: npx --no-install drop-plugin validate .
|
||||
|
||||
- name: Pack plugin archive
|
||||
run: npx --no-install drop-plugin pack . ./dist-package
|
||||
|
||||
- name: Generate checksums
|
||||
id: pkg
|
||||
run: |
|
||||
FILE=$(ls dist-package/*.dropplugin | head -n 1)
|
||||
echo "path=$FILE" >> "$GITHUB_OUTPUT"
|
||||
echo "name=$(basename "$FILE")" >> "$GITHUB_OUTPUT"
|
||||
sha256sum "$FILE" > "$FILE.sha256"
|
||||
|
||||
- name: Create GitHub release
|
||||
uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2.6.2
|
||||
with:
|
||||
files: |
|
||||
${{ steps.pkg.outputs.path }}
|
||||
${{ steps.pkg.outputs.path }}.sha256
|
||||
name: ${{ steps.pkg.outputs.name }}
|
||||
prerelease: ${{ contains(github.ref_name, '-') }}
|
||||
generate_release_notes: true
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"sdk": "@droposs",
|
||||
"sdkVersion": "^0.7.0",
|
||||
"cliVersion": "^0.7.0"
|
||||
}
|
||||
@@ -28,13 +28,17 @@
|
||||
},
|
||||
"checksum": "4b4d8aa64f747b9611e00609a3036b493ba030a3e5aadf4d68fcf7752fef88a9",
|
||||
"files": {
|
||||
".github/workflows/ci.yml": "4741172bed309e809eb7d7a1ca02ce241fc8edeb2430060bbd680a08a9c20f10",
|
||||
".github/workflows/release.yml": "999abcf55a425c3c0fb4dcb8442aca189a252a4967a5a7d82c709223ef46c871",
|
||||
".gitignore": "c7ecb5dd064aa6e25e6d7afb4258dc3132295a306a5c1d343676ef541763d0bd",
|
||||
".sdk-scope.json": "f13936572aca04f0f47da440ed27eb69ea7d5c5b1d0901b69ebd4aa55ce455b6",
|
||||
"dist/src/index.js": "4b4d8aa64f747b9611e00609a3036b493ba030a3e5aadf4d68fcf7752fef88a9",
|
||||
"dist/src/index.js.map": "904c14711dda7e966d9d534e53f277e895c87411b486d76424eee15a1963bafb",
|
||||
"dist/test/index.test.js": "640511eaf5d061c82d6cdf609bd518cd8033e05a59f7642418231ba69e9ed59a",
|
||||
"package.json": "86dbe2c6a0412914c40ee2692b730262602437b19e5c06c15e0f654b27feb427",
|
||||
"package.json": "d6635b8ea742bc70120ae47bfdacecc434833f0913f1d1db9c4903cce76d84c5",
|
||||
"scripts/switch-sdk-scope.mjs": "ab740ee477a7f5eb07b5344bd12f8c311d51f49279ad4778668e7e2ea4fef798",
|
||||
"src/index.ts": "546e73733177bd21ec77bcef1c77b11453ad15dbf3204d5aef8671e75ba19d48",
|
||||
"test/index.test.ts": "efd5b6120feaee7b74fb7e774d3ff243d1bf6d9195d28181c2ab615d45908b0f",
|
||||
"tsconfig.json": "a31a559ca71e6b0d196fffa84189fbaa4e117b6def7c4f45bd13645fbbfe30b9"
|
||||
"tsconfig.json": "ffd407ef4014fe75cbdc6ad897632157a2d8b8f66a03a4d711c6ac1374f4cde2"
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
"scripts": {
|
||||
"build": "drop-plugin build",
|
||||
"test": "node --test test/index.test.ts",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"validate": "drop-plugin validate"
|
||||
},
|
||||
"license": "MIT",
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
#!/usr/bin/env node
|
||||
/* global process, console */
|
||||
|
||||
import { readdirSync, readFileSync, writeFileSync, existsSync, realpathSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import { execFileSync } from "node:child_process";
|
||||
|
||||
const LEGACY_SCOPE = "@droposs";
|
||||
const NEW_SCOPE = "@drop-oss";
|
||||
const SKIP_DIRS = new Set(["node_modules", ".git", "dist", "dist-package", "dist-packages"]);
|
||||
const LOCKFILES = new Set(["package-lock.json", "pnpm-lock.yaml", "yarn.lock", "bun.lockb", "deno.lock"]);
|
||||
const TEXT_EXT = new Set([".ts", ".tsx", ".js", ".mjs", ".cjs", ".json", ".md", ".yml", ".yaml"]);
|
||||
|
||||
const configPath = path.resolve(process.cwd(), ".sdk-scope.json");
|
||||
if (!existsSync(configPath)) {
|
||||
console.error("Missing .sdk-scope.json in repo root");
|
||||
process.exit(1);
|
||||
}
|
||||
const config = JSON.parse(readFileSync(configPath, "utf-8"));
|
||||
const scope = config.sdk;
|
||||
if (scope !== NEW_SCOPE && scope !== LEGACY_SCOPE) {
|
||||
console.error(`Unknown scope '${scope}'; .sdk-scope.json sdk must be '${NEW_SCOPE}' or '${LEGACY_SCOPE}'`);
|
||||
process.exit(1);
|
||||
}
|
||||
const sdkVersion = config["sdkVersion"];
|
||||
const cliVersion = config["cliVersion"];
|
||||
if (typeof sdkVersion !== "string" || typeof cliVersion !== "string") {
|
||||
console.error(".sdk-scope.json must define sdkVersion and cliVersion");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const OTHER_SCOPES = scope === NEW_SCOPE ? [LEGACY_SCOPE] : [NEW_SCOPE];
|
||||
|
||||
function walk(dir, files = [], visited = new Set()) {
|
||||
const real = realpathSync(dir);
|
||||
if (visited.has(real)) return files;
|
||||
visited.add(real);
|
||||
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
||||
const full = path.join(dir, entry.name);
|
||||
if (SKIP_DIRS.has(entry.name)) continue;
|
||||
if (entry.isSymbolicLink()) continue;
|
||||
if (entry.isDirectory()) walk(full, files, visited);
|
||||
else files.push(full);
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
function normalizeSpecifiers(text) {
|
||||
let out = text;
|
||||
for (const other of OTHER_SCOPES) {
|
||||
out = out
|
||||
.replaceAll(`${other}/plugin-sdk`, `${scope}/plugin-sdk`)
|
||||
.replaceAll(`${other}/plugin-cli`, `${scope}/plugin-cli`);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function rewritePackageJson(file, text) {
|
||||
const indent = detectIndent(text);
|
||||
let pkg;
|
||||
try {
|
||||
pkg = JSON.parse(text);
|
||||
} catch {
|
||||
return text;
|
||||
}
|
||||
for (const sectionName of ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"]) {
|
||||
const section = pkg[sectionName];
|
||||
if (!section) continue;
|
||||
const rebuilt = {};
|
||||
for (const dep of Object.keys(section)) {
|
||||
const kind = parseSdkDep(dep);
|
||||
if (!kind) {
|
||||
rebuilt[dep] = section[dep];
|
||||
continue;
|
||||
}
|
||||
const target = `${scope}/${kind}`;
|
||||
rebuilt[target] = kind === "plugin-sdk" ? sdkVersion : cliVersion;
|
||||
}
|
||||
pkg[sectionName] = rebuilt;
|
||||
}
|
||||
return JSON.stringify(pkg, null, indent) + (text.endsWith("\n") ? "\n" : "");
|
||||
}
|
||||
|
||||
function parseSdkDep(dep) {
|
||||
for (const scopeName of [LEGACY_SCOPE, NEW_SCOPE, "@drop"]) {
|
||||
if (dep === `${scopeName}/plugin-sdk`) return "plugin-sdk";
|
||||
if (dep === `${scopeName}/plugin-cli`) return "plugin-cli";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function detectIndent(text) {
|
||||
const match = text.match(/\n([ \t]+)"/);
|
||||
return match ? match[1] : "\t";
|
||||
}
|
||||
|
||||
const edited = [];
|
||||
|
||||
const files = walk(process.cwd());
|
||||
for (const file of files) {
|
||||
if (LOCKFILES.has(path.basename(file))) continue;
|
||||
if (!TEXT_EXT.has(path.extname(file))) continue;
|
||||
const before = readFileSync(file, "utf-8");
|
||||
let after = normalizeSpecifiers(before);
|
||||
if (path.basename(file) === "package.json") {
|
||||
after = rewritePackageJson(file, after);
|
||||
}
|
||||
if (after !== before) {
|
||||
writeFileSync(file, after);
|
||||
edited.push(path.relative(process.cwd(), file));
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`Scope set to '${scope}' (sdk ${sdkVersion}, cli ${cliVersion})`);
|
||||
if (edited.length) {
|
||||
for (const f of edited) console.log(` rewrote ${f}`);
|
||||
} else {
|
||||
console.log(" no specifier changes needed");
|
||||
}
|
||||
|
||||
const installDir = process.cwd();
|
||||
|
||||
if (existsSync(path.join(installDir, "package-lock.json"))) {
|
||||
try {
|
||||
execFileSync("npm", ["install", "--no-audit", "--no-fund"], { cwd: installDir, stdio: "inherit" });
|
||||
} catch {
|
||||
execFileSync("npm", ["install", "--no-audit", "--no-fund", "--legacy-peer-deps"], { cwd: installDir, stdio: "inherit" });
|
||||
}
|
||||
} else if (existsSync(path.join(installDir, "pnpm-lock.yaml"))) {
|
||||
execFileSync("pnpm", ["install"], { cwd: installDir, stdio: "inherit" });
|
||||
} else {
|
||||
console.warn("no lockfile found; skipping dependency install");
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
"esModuleInterop": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"allowJs": true,
|
||||
"outDir": "dist"
|
||||
},
|
||||
"include": ["src/**/*", "test/**/*"]
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Build, typecheck, and test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # actions/[email protected]
|
||||
|
||||
- name: Set up Node.js 22
|
||||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # actions/[email protected]
|
||||
with:
|
||||
node-version: "22"
|
||||
cache: npm
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
if [ -f package-lock.json ]; then
|
||||
npm ci --ignore-scripts
|
||||
else
|
||||
npm install --no-audit --no-fund --ignore-scripts
|
||||
fi
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
|
||||
- name: Typecheck
|
||||
run: npm run typecheck
|
||||
|
||||
- name: Test
|
||||
run: npm test
|
||||
|
||||
- name: Validate manifest
|
||||
run: npx --no-install drop-plugin validate .
|
||||
@@ -0,0 +1,65 @@
|
||||
name: Release Drop Plugin
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
concurrency:
|
||||
group: release-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: Build, validate, and publish .dropplugin
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # actions/[email protected]
|
||||
|
||||
- name: Set up Node.js 22
|
||||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # actions/[email protected]
|
||||
with:
|
||||
node-version: "22"
|
||||
cache: npm
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
if [ -f package-lock.json ]; then
|
||||
npm ci --ignore-scripts
|
||||
else
|
||||
npm install --no-audit --no-fund --ignore-scripts
|
||||
fi
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
|
||||
- name: Test
|
||||
run: npm test
|
||||
|
||||
- name: Validate manifest
|
||||
run: npx --no-install drop-plugin validate .
|
||||
|
||||
- name: Pack plugin archive
|
||||
run: npx --no-install drop-plugin pack . ./dist-package
|
||||
|
||||
- name: Generate checksums
|
||||
id: pkg
|
||||
run: |
|
||||
FILE=$(ls dist-package/*.dropplugin | head -n 1)
|
||||
echo "path=$FILE" >> "$GITHUB_OUTPUT"
|
||||
echo "name=$(basename "$FILE")" >> "$GITHUB_OUTPUT"
|
||||
sha256sum "$FILE" > "$FILE.sha256"
|
||||
|
||||
- name: Create GitHub release
|
||||
uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2.6.2
|
||||
with:
|
||||
files: |
|
||||
${{ steps.pkg.outputs.path }}
|
||||
${{ steps.pkg.outputs.path }}.sha256
|
||||
name: ${{ steps.pkg.outputs.name }}
|
||||
prerelease: ${{ contains(github.ref_name, '-') }}
|
||||
generate_release_notes: true
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"sdk": "@droposs",
|
||||
"sdkVersion": "^0.7.0",
|
||||
"cliVersion": "^0.7.0"
|
||||
}
|
||||
@@ -28,13 +28,17 @@
|
||||
},
|
||||
"checksum": "1aed97c00c0d65e276bbe6cc4fb856bcc311eba57dd02aced1050739c25b656e",
|
||||
"files": {
|
||||
".github/workflows/ci.yml": "4741172bed309e809eb7d7a1ca02ce241fc8edeb2430060bbd680a08a9c20f10",
|
||||
".github/workflows/release.yml": "999abcf55a425c3c0fb4dcb8442aca189a252a4967a5a7d82c709223ef46c871",
|
||||
".gitignore": "c7ecb5dd064aa6e25e6d7afb4258dc3132295a306a5c1d343676ef541763d0bd",
|
||||
".sdk-scope.json": "f13936572aca04f0f47da440ed27eb69ea7d5c5b1d0901b69ebd4aa55ce455b6",
|
||||
"dist/src/client.js": "1aed97c00c0d65e276bbe6cc4fb856bcc311eba57dd02aced1050739c25b656e",
|
||||
"dist/src/client.js.map": "a33fa4205bdff524362e0ae3d9fa43160dfa494635b1119be712c91a47d5b7cf",
|
||||
"dist/test/client.test.js": "30a5d956c06e06aa230bacb27537a2e02b0865c99215dcb5175733caa99769d1",
|
||||
"package.json": "2f3b4ffaa8a135c11f07818c690841259402200f371bf64c832cf18731d7190c",
|
||||
"package.json": "b129d1090d19b77e37c162af3a91e613f6f5955f3f8b052f0da3798e2028e899",
|
||||
"scripts/switch-sdk-scope.mjs": "ab740ee477a7f5eb07b5344bd12f8c311d51f49279ad4778668e7e2ea4fef798",
|
||||
"src/client.ts": "458ac509935463f9b2e2bd6c56144fb12b3434819155432f08403d7b67b03a6e",
|
||||
"test/client.test.ts": "de8e38567558d71a95c36ea414f555bda0cb8b46c5e85c73b19d5cbab6bf814c",
|
||||
"tsconfig.json": "a31a559ca71e6b0d196fffa84189fbaa4e117b6def7c4f45bd13645fbbfe30b9"
|
||||
"tsconfig.json": "ffd407ef4014fe75cbdc6ad897632157a2d8b8f66a03a4d711c6ac1374f4cde2"
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
"scripts": {
|
||||
"build": "drop-plugin build",
|
||||
"test": "node --test test/client.test.ts",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"validate": "drop-plugin validate"
|
||||
},
|
||||
"license": "MIT",
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
#!/usr/bin/env node
|
||||
/* global process, console */
|
||||
|
||||
import { readdirSync, readFileSync, writeFileSync, existsSync, realpathSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import { execFileSync } from "node:child_process";
|
||||
|
||||
const LEGACY_SCOPE = "@droposs";
|
||||
const NEW_SCOPE = "@drop-oss";
|
||||
const SKIP_DIRS = new Set(["node_modules", ".git", "dist", "dist-package", "dist-packages"]);
|
||||
const LOCKFILES = new Set(["package-lock.json", "pnpm-lock.yaml", "yarn.lock", "bun.lockb", "deno.lock"]);
|
||||
const TEXT_EXT = new Set([".ts", ".tsx", ".js", ".mjs", ".cjs", ".json", ".md", ".yml", ".yaml"]);
|
||||
|
||||
const configPath = path.resolve(process.cwd(), ".sdk-scope.json");
|
||||
if (!existsSync(configPath)) {
|
||||
console.error("Missing .sdk-scope.json in repo root");
|
||||
process.exit(1);
|
||||
}
|
||||
const config = JSON.parse(readFileSync(configPath, "utf-8"));
|
||||
const scope = config.sdk;
|
||||
if (scope !== NEW_SCOPE && scope !== LEGACY_SCOPE) {
|
||||
console.error(`Unknown scope '${scope}'; .sdk-scope.json sdk must be '${NEW_SCOPE}' or '${LEGACY_SCOPE}'`);
|
||||
process.exit(1);
|
||||
}
|
||||
const sdkVersion = config["sdkVersion"];
|
||||
const cliVersion = config["cliVersion"];
|
||||
if (typeof sdkVersion !== "string" || typeof cliVersion !== "string") {
|
||||
console.error(".sdk-scope.json must define sdkVersion and cliVersion");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const OTHER_SCOPES = scope === NEW_SCOPE ? [LEGACY_SCOPE] : [NEW_SCOPE];
|
||||
|
||||
function walk(dir, files = [], visited = new Set()) {
|
||||
const real = realpathSync(dir);
|
||||
if (visited.has(real)) return files;
|
||||
visited.add(real);
|
||||
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
||||
const full = path.join(dir, entry.name);
|
||||
if (SKIP_DIRS.has(entry.name)) continue;
|
||||
if (entry.isSymbolicLink()) continue;
|
||||
if (entry.isDirectory()) walk(full, files, visited);
|
||||
else files.push(full);
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
function normalizeSpecifiers(text) {
|
||||
let out = text;
|
||||
for (const other of OTHER_SCOPES) {
|
||||
out = out
|
||||
.replaceAll(`${other}/plugin-sdk`, `${scope}/plugin-sdk`)
|
||||
.replaceAll(`${other}/plugin-cli`, `${scope}/plugin-cli`);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function rewritePackageJson(file, text) {
|
||||
const indent = detectIndent(text);
|
||||
let pkg;
|
||||
try {
|
||||
pkg = JSON.parse(text);
|
||||
} catch {
|
||||
return text;
|
||||
}
|
||||
for (const sectionName of ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"]) {
|
||||
const section = pkg[sectionName];
|
||||
if (!section) continue;
|
||||
const rebuilt = {};
|
||||
for (const dep of Object.keys(section)) {
|
||||
const kind = parseSdkDep(dep);
|
||||
if (!kind) {
|
||||
rebuilt[dep] = section[dep];
|
||||
continue;
|
||||
}
|
||||
const target = `${scope}/${kind}`;
|
||||
rebuilt[target] = kind === "plugin-sdk" ? sdkVersion : cliVersion;
|
||||
}
|
||||
pkg[sectionName] = rebuilt;
|
||||
}
|
||||
return JSON.stringify(pkg, null, indent) + (text.endsWith("\n") ? "\n" : "");
|
||||
}
|
||||
|
||||
function parseSdkDep(dep) {
|
||||
for (const scopeName of [LEGACY_SCOPE, NEW_SCOPE, "@drop"]) {
|
||||
if (dep === `${scopeName}/plugin-sdk`) return "plugin-sdk";
|
||||
if (dep === `${scopeName}/plugin-cli`) return "plugin-cli";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function detectIndent(text) {
|
||||
const match = text.match(/\n([ \t]+)"/);
|
||||
return match ? match[1] : "\t";
|
||||
}
|
||||
|
||||
const edited = [];
|
||||
|
||||
const files = walk(process.cwd());
|
||||
for (const file of files) {
|
||||
if (LOCKFILES.has(path.basename(file))) continue;
|
||||
if (!TEXT_EXT.has(path.extname(file))) continue;
|
||||
const before = readFileSync(file, "utf-8");
|
||||
let after = normalizeSpecifiers(before);
|
||||
if (path.basename(file) === "package.json") {
|
||||
after = rewritePackageJson(file, after);
|
||||
}
|
||||
if (after !== before) {
|
||||
writeFileSync(file, after);
|
||||
edited.push(path.relative(process.cwd(), file));
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`Scope set to '${scope}' (sdk ${sdkVersion}, cli ${cliVersion})`);
|
||||
if (edited.length) {
|
||||
for (const f of edited) console.log(` rewrote ${f}`);
|
||||
} else {
|
||||
console.log(" no specifier changes needed");
|
||||
}
|
||||
|
||||
const installDir = process.cwd();
|
||||
|
||||
if (existsSync(path.join(installDir, "package-lock.json"))) {
|
||||
try {
|
||||
execFileSync("npm", ["install", "--no-audit", "--no-fund"], { cwd: installDir, stdio: "inherit" });
|
||||
} catch {
|
||||
execFileSync("npm", ["install", "--no-audit", "--no-fund", "--legacy-peer-deps"], { cwd: installDir, stdio: "inherit" });
|
||||
}
|
||||
} else if (existsSync(path.join(installDir, "pnpm-lock.yaml"))) {
|
||||
execFileSync("pnpm", ["install"], { cwd: installDir, stdio: "inherit" });
|
||||
} else {
|
||||
console.warn("no lockfile found; skipping dependency install");
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
"esModuleInterop": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"allowJs": true,
|
||||
"outDir": "dist"
|
||||
},
|
||||
"include": ["src/**/*", "test/**/*"]
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Build, typecheck, and test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # actions/[email protected]
|
||||
|
||||
- name: Set up Node.js 22
|
||||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # actions/[email protected]
|
||||
with:
|
||||
node-version: "22"
|
||||
cache: npm
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
if [ -f package-lock.json ]; then
|
||||
npm ci --ignore-scripts
|
||||
else
|
||||
npm install --no-audit --no-fund --ignore-scripts
|
||||
fi
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
|
||||
- name: Typecheck
|
||||
run: npm run typecheck
|
||||
|
||||
- name: Test
|
||||
run: npm test
|
||||
|
||||
- name: Validate manifest
|
||||
run: npx --no-install drop-plugin validate .
|
||||
@@ -0,0 +1,65 @@
|
||||
name: Release Drop Plugin
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
concurrency:
|
||||
group: release-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: Build, validate, and publish .dropplugin
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # actions/[email protected]
|
||||
|
||||
- name: Set up Node.js 22
|
||||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # actions/[email protected]
|
||||
with:
|
||||
node-version: "22"
|
||||
cache: npm
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
if [ -f package-lock.json ]; then
|
||||
npm ci --ignore-scripts
|
||||
else
|
||||
npm install --no-audit --no-fund --ignore-scripts
|
||||
fi
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
|
||||
- name: Test
|
||||
run: npm test
|
||||
|
||||
- name: Validate manifest
|
||||
run: npx --no-install drop-plugin validate .
|
||||
|
||||
- name: Pack plugin archive
|
||||
run: npx --no-install drop-plugin pack . ./dist-package
|
||||
|
||||
- name: Generate checksums
|
||||
id: pkg
|
||||
run: |
|
||||
FILE=$(ls dist-package/*.dropplugin | head -n 1)
|
||||
echo "path=$FILE" >> "$GITHUB_OUTPUT"
|
||||
echo "name=$(basename "$FILE")" >> "$GITHUB_OUTPUT"
|
||||
sha256sum "$FILE" > "$FILE.sha256"
|
||||
|
||||
- name: Create GitHub release
|
||||
uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2.6.2
|
||||
with:
|
||||
files: |
|
||||
${{ steps.pkg.outputs.path }}
|
||||
${{ steps.pkg.outputs.path }}.sha256
|
||||
name: ${{ steps.pkg.outputs.name }}
|
||||
prerelease: ${{ contains(github.ref_name, '-') }}
|
||||
generate_release_notes: true
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"sdk": "@droposs",
|
||||
"sdkVersion": "^0.7.0",
|
||||
"cliVersion": "^0.7.0"
|
||||
}
|
||||
@@ -28,13 +28,17 @@
|
||||
},
|
||||
"checksum": "d6dd6f7ea2fce5f4d36b5e1c5d67acb4437a00dfbb908593dd6ac3191168ed2b",
|
||||
"files": {
|
||||
".github/workflows/ci.yml": "4741172bed309e809eb7d7a1ca02ce241fc8edeb2430060bbd680a08a9c20f10",
|
||||
".github/workflows/release.yml": "999abcf55a425c3c0fb4dcb8442aca189a252a4967a5a7d82c709223ef46c871",
|
||||
".gitignore": "c7ecb5dd064aa6e25e6d7afb4258dc3132295a306a5c1d343676ef541763d0bd",
|
||||
".sdk-scope.json": "f13936572aca04f0f47da440ed27eb69ea7d5c5b1d0901b69ebd4aa55ce455b6",
|
||||
"dist/src/client.js": "d6dd6f7ea2fce5f4d36b5e1c5d67acb4437a00dfbb908593dd6ac3191168ed2b",
|
||||
"dist/src/client.js.map": "d9c5c3e51398e7987190d3d2ee0f78d0151060fd662e44a94bcbff7ee78b4cff",
|
||||
"dist/test/client.test.js": "3f0b0d88df89542ad652022d50a1fa245fadc1743c9373ef37f82b1c8c15eefe",
|
||||
"package.json": "9bd246d0eeba2becc4d50e63869683cb66d2aba81a780dfabd61699da91a9dfb",
|
||||
"package.json": "82b8b701d226bbf45dbf9e605588f0b249a780c4dd457601202ba71d56b64a10",
|
||||
"scripts/switch-sdk-scope.mjs": "ab740ee477a7f5eb07b5344bd12f8c311d51f49279ad4778668e7e2ea4fef798",
|
||||
"src/client.ts": "982c9dd2afd1afd77d9d9071c7cc97ede6143648ea8a4e71a5e80e664cb449d9",
|
||||
"test/client.test.ts": "b0cd225d32c199605e35532d1cab89fc5513cfffeb91f10c76e0cb4435ed9611",
|
||||
"tsconfig.json": "a31a559ca71e6b0d196fffa84189fbaa4e117b6def7c4f45bd13645fbbfe30b9"
|
||||
"tsconfig.json": "ffd407ef4014fe75cbdc6ad897632157a2d8b8f66a03a4d711c6ac1374f4cde2"
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
"scripts": {
|
||||
"build": "drop-plugin build",
|
||||
"test": "node --test test/client.test.ts",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"validate": "drop-plugin validate"
|
||||
},
|
||||
"license": "MIT",
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
#!/usr/bin/env node
|
||||
/* global process, console */
|
||||
|
||||
import { readdirSync, readFileSync, writeFileSync, existsSync, realpathSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import { execFileSync } from "node:child_process";
|
||||
|
||||
const LEGACY_SCOPE = "@droposs";
|
||||
const NEW_SCOPE = "@drop-oss";
|
||||
const SKIP_DIRS = new Set(["node_modules", ".git", "dist", "dist-package", "dist-packages"]);
|
||||
const LOCKFILES = new Set(["package-lock.json", "pnpm-lock.yaml", "yarn.lock", "bun.lockb", "deno.lock"]);
|
||||
const TEXT_EXT = new Set([".ts", ".tsx", ".js", ".mjs", ".cjs", ".json", ".md", ".yml", ".yaml"]);
|
||||
|
||||
const configPath = path.resolve(process.cwd(), ".sdk-scope.json");
|
||||
if (!existsSync(configPath)) {
|
||||
console.error("Missing .sdk-scope.json in repo root");
|
||||
process.exit(1);
|
||||
}
|
||||
const config = JSON.parse(readFileSync(configPath, "utf-8"));
|
||||
const scope = config.sdk;
|
||||
if (scope !== NEW_SCOPE && scope !== LEGACY_SCOPE) {
|
||||
console.error(`Unknown scope '${scope}'; .sdk-scope.json sdk must be '${NEW_SCOPE}' or '${LEGACY_SCOPE}'`);
|
||||
process.exit(1);
|
||||
}
|
||||
const sdkVersion = config["sdkVersion"];
|
||||
const cliVersion = config["cliVersion"];
|
||||
if (typeof sdkVersion !== "string" || typeof cliVersion !== "string") {
|
||||
console.error(".sdk-scope.json must define sdkVersion and cliVersion");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const OTHER_SCOPES = scope === NEW_SCOPE ? [LEGACY_SCOPE] : [NEW_SCOPE];
|
||||
|
||||
function walk(dir, files = [], visited = new Set()) {
|
||||
const real = realpathSync(dir);
|
||||
if (visited.has(real)) return files;
|
||||
visited.add(real);
|
||||
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
||||
const full = path.join(dir, entry.name);
|
||||
if (SKIP_DIRS.has(entry.name)) continue;
|
||||
if (entry.isSymbolicLink()) continue;
|
||||
if (entry.isDirectory()) walk(full, files, visited);
|
||||
else files.push(full);
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
function normalizeSpecifiers(text) {
|
||||
let out = text;
|
||||
for (const other of OTHER_SCOPES) {
|
||||
out = out
|
||||
.replaceAll(`${other}/plugin-sdk`, `${scope}/plugin-sdk`)
|
||||
.replaceAll(`${other}/plugin-cli`, `${scope}/plugin-cli`);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function rewritePackageJson(file, text) {
|
||||
const indent = detectIndent(text);
|
||||
let pkg;
|
||||
try {
|
||||
pkg = JSON.parse(text);
|
||||
} catch {
|
||||
return text;
|
||||
}
|
||||
for (const sectionName of ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"]) {
|
||||
const section = pkg[sectionName];
|
||||
if (!section) continue;
|
||||
const rebuilt = {};
|
||||
for (const dep of Object.keys(section)) {
|
||||
const kind = parseSdkDep(dep);
|
||||
if (!kind) {
|
||||
rebuilt[dep] = section[dep];
|
||||
continue;
|
||||
}
|
||||
const target = `${scope}/${kind}`;
|
||||
rebuilt[target] = kind === "plugin-sdk" ? sdkVersion : cliVersion;
|
||||
}
|
||||
pkg[sectionName] = rebuilt;
|
||||
}
|
||||
return JSON.stringify(pkg, null, indent) + (text.endsWith("\n") ? "\n" : "");
|
||||
}
|
||||
|
||||
function parseSdkDep(dep) {
|
||||
for (const scopeName of [LEGACY_SCOPE, NEW_SCOPE, "@drop"]) {
|
||||
if (dep === `${scopeName}/plugin-sdk`) return "plugin-sdk";
|
||||
if (dep === `${scopeName}/plugin-cli`) return "plugin-cli";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function detectIndent(text) {
|
||||
const match = text.match(/\n([ \t]+)"/);
|
||||
return match ? match[1] : "\t";
|
||||
}
|
||||
|
||||
const edited = [];
|
||||
|
||||
const files = walk(process.cwd());
|
||||
for (const file of files) {
|
||||
if (LOCKFILES.has(path.basename(file))) continue;
|
||||
if (!TEXT_EXT.has(path.extname(file))) continue;
|
||||
const before = readFileSync(file, "utf-8");
|
||||
let after = normalizeSpecifiers(before);
|
||||
if (path.basename(file) === "package.json") {
|
||||
after = rewritePackageJson(file, after);
|
||||
}
|
||||
if (after !== before) {
|
||||
writeFileSync(file, after);
|
||||
edited.push(path.relative(process.cwd(), file));
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`Scope set to '${scope}' (sdk ${sdkVersion}, cli ${cliVersion})`);
|
||||
if (edited.length) {
|
||||
for (const f of edited) console.log(` rewrote ${f}`);
|
||||
} else {
|
||||
console.log(" no specifier changes needed");
|
||||
}
|
||||
|
||||
const installDir = process.cwd();
|
||||
|
||||
if (existsSync(path.join(installDir, "package-lock.json"))) {
|
||||
try {
|
||||
execFileSync("npm", ["install", "--no-audit", "--no-fund"], { cwd: installDir, stdio: "inherit" });
|
||||
} catch {
|
||||
execFileSync("npm", ["install", "--no-audit", "--no-fund", "--legacy-peer-deps"], { cwd: installDir, stdio: "inherit" });
|
||||
}
|
||||
} else if (existsSync(path.join(installDir, "pnpm-lock.yaml"))) {
|
||||
execFileSync("pnpm", ["install"], { cwd: installDir, stdio: "inherit" });
|
||||
} else {
|
||||
console.warn("no lockfile found; skipping dependency install");
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
"esModuleInterop": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"allowJs": true,
|
||||
"outDir": "dist"
|
||||
},
|
||||
"include": ["src/**/*", "test/**/*"]
|
||||
|
||||
Reference in new issue
Block a user