mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-29 06:35:24 +00:00
chore: lock the plugin boundary and remove 2.9.0 leftovers
This commit is contained in:
1 parent
6489b62ece
commit
2961297989
399 files changed
+3964
-5627
No files matched your search
@@ -2,7 +2,6 @@ import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import * as esbuild from "esbuild";
|
||||
import { BACKEND_EXTERNALS, FRONTEND_EXTERNALS } from "../lib/externals.mjs";
|
||||
import { legacyCoreImports } from "../lib/legacy-core-imports.mjs";
|
||||
import { staticUrlImports } from "../lib/static-url-imports.mjs";
|
||||
import { readManifest, resolveEntry, copyDir } from "../lib/plugin-dir.mjs";
|
||||
|
||||
@@ -18,17 +17,6 @@ const FRONTEND_ENTRIES = [
|
||||
"src/frontend/index.js",
|
||||
];
|
||||
|
||||
/** Whether the plugin sits in a Termix checkout, i.e. is a bundled plugin. */
|
||||
function insideTermixCheckout(cwd) {
|
||||
let dir = path.resolve(cwd);
|
||||
for (;;) {
|
||||
if (fs.existsSync(path.join(dir, "src", "ui", "plugin-host"))) return true;
|
||||
const parent = path.dirname(dir);
|
||||
if (parent === dir) return false;
|
||||
dir = parent;
|
||||
}
|
||||
}
|
||||
|
||||
export async function build({ cwd }) {
|
||||
const manifest = readManifest(cwd);
|
||||
const pluginId = manifest.id ?? path.basename(cwd);
|
||||
@@ -59,7 +47,6 @@ export async function build({ cwd }) {
|
||||
// node_modules import instead, resolved at runtime like a host-provided
|
||||
// package.
|
||||
external: [...BACKEND_EXTERNALS, ...(manifest.nativeDependencies ?? [])],
|
||||
plugins: [legacyCoreImports({ pluginId, platform: "node" })],
|
||||
});
|
||||
|
||||
const frontendEntry = resolveEntry(cwd, FRONTEND_ENTRIES);
|
||||
@@ -75,14 +62,7 @@ export async function build({ cwd }) {
|
||||
sourcemap: true,
|
||||
logLevel: "warning",
|
||||
external: FRONTEND_EXTERNALS,
|
||||
plugins: [
|
||||
staticUrlImports({ outDir }),
|
||||
legacyCoreImports({
|
||||
pluginId,
|
||||
platform: "browser",
|
||||
insideTermix: insideTermixCheckout(cwd),
|
||||
}),
|
||||
],
|
||||
plugins: [staticUrlImports({ outDir })],
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,4 @@ export const FRONTEND_EXTERNALS = [
|
||||
"sonner",
|
||||
"@termix/plugin-sdk/frontend",
|
||||
"@termix/plugin-sdk/ui",
|
||||
// Legacy: the shell's own modules, see legacy-core-imports.mjs. D1 removes.
|
||||
"@termix/legacy-core/*",
|
||||
];
|
||||
@@ -1,68 +0,0 @@
|
||||
/**
|
||||
* Legacy core imports: the debt D1 removes.
|
||||
*
|
||||
* Plugin code predates the SDK and still reaches core, and in one case a
|
||||
* sibling plugin, by relative path. Those specifiers must stay out of the
|
||||
* bundle and be rewritten to where the built server puts the target.
|
||||
*
|
||||
* The rewrite is output-relative, not source-relative. Every plugin bundles
|
||||
* to plugins/<id>/dist/backend.js, which sits at the same depth the old
|
||||
* per-file output did, so one prefix covers a source file at any nesting.
|
||||
*
|
||||
* Delete this file in D1, once every plugin reaches core through the SDK.
|
||||
*/
|
||||
|
||||
import { legacyCoreSpecifier } from "./legacy-core-specifier.mjs";
|
||||
|
||||
// "../../../src/backend/" or "../../../../src/backend/", any depth.
|
||||
const CORE_BACKEND = /^(?:\.\.\/)+src\/backend\//;
|
||||
// Shared types. Mostly erased at compile, but automations imports a real
|
||||
// value (AUTOMATION_DEFINITION_VERSION), so this has to resolve at runtime.
|
||||
const CORE_TYPES = /^(?:\.\.\/)+src\/types\//;
|
||||
const CORE_UI = /^(?:\.\.\/)+src\/ui\//;
|
||||
/** dist/plugins/<id>/dist/backend.js -> dist/backend/... */
|
||||
const COMPILED_CORE = "../../../backend/backend/";
|
||||
const COMPILED_TYPES = "../../../backend/types/";
|
||||
|
||||
const CORE_ALIAS = /^@\//;
|
||||
|
||||
export function legacyCoreImports({ pluginId, platform, insideTermix = true }) {
|
||||
return {
|
||||
name: "legacy-core-imports",
|
||||
setup(build) {
|
||||
// The shell's own modules, reached as "@/..." or by relative path.
|
||||
// Bundling them would give the plugin a second copy of the shell's
|
||||
// contexts and API client, so each becomes a bare specifier the page's
|
||||
// import map resolves to the shell's own module.
|
||||
if (platform === "browser") {
|
||||
const toLegacy = (args) => {
|
||||
const specifier = legacyCoreSpecifier(args.path);
|
||||
if (!specifier) return null;
|
||||
if (!insideTermix) {
|
||||
return {
|
||||
errors: [
|
||||
{
|
||||
text: `${pluginId}: "${args.path}" reaches Termix core. Only plugins built inside a Termix checkout may; use @termix/plugin-sdk instead.`,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
return { path: specifier, external: true };
|
||||
};
|
||||
build.onResolve({ filter: CORE_ALIAS }, toLegacy);
|
||||
build.onResolve({ filter: CORE_UI }, toLegacy);
|
||||
build.onResolve({ filter: CORE_TYPES }, toLegacy);
|
||||
}
|
||||
|
||||
build.onResolve({ filter: CORE_BACKEND }, (args) => ({
|
||||
path: args.path.replace(CORE_BACKEND, COMPILED_CORE),
|
||||
external: true,
|
||||
}));
|
||||
|
||||
build.onResolve({ filter: CORE_TYPES }, (args) => ({
|
||||
path: args.path.replace(CORE_TYPES, COMPILED_TYPES),
|
||||
external: true,
|
||||
}));
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
/**
|
||||
* Legacy core modules a bundled plugin frontend still imports.
|
||||
*
|
||||
* Bundled plugin frontends predate the SDK and import the shell's own modules
|
||||
* as "@/..." (or, in a couple of places, by relative path into src/). Bundling
|
||||
* those would give the plugin a second copy of the shell's contexts and API
|
||||
* client, so the CLI rewrites each one to a bare specifier under
|
||||
* "@termix/legacy-core/" and core's build exposes exactly that module through
|
||||
* the import map. Both sides use this one normalizer so they cannot disagree.
|
||||
*
|
||||
* Only plugins built inside a Termix checkout may do this. D1 deletes it.
|
||||
*/
|
||||
|
||||
export const LEGACY_CORE_PREFIX = "@termix/legacy-core/";
|
||||
|
||||
const EXTENSION = /\.(tsx|ts|jsx|js|mjs)$/;
|
||||
|
||||
function clean(rest) {
|
||||
return rest.replace(EXTENSION, "").replace(/\/+$/, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* "@/components/button.tsx" -> "@termix/legacy-core/ui/components/button"
|
||||
* "@/types" -> "@termix/legacy-core/types"
|
||||
* "@/types/connection-log" -> "@termix/legacy-core/types/connection-log"
|
||||
* "../../../src/ui/x.tsx" -> "@termix/legacy-core/ui/x"
|
||||
* Anything else -> null
|
||||
*/
|
||||
export function legacyCoreSpecifier(specifier) {
|
||||
if (specifier.startsWith("@/")) {
|
||||
const rest = clean(specifier.slice(2));
|
||||
if (rest === "types" || rest.startsWith("types/")) {
|
||||
return LEGACY_CORE_PREFIX + rest;
|
||||
}
|
||||
return `${LEGACY_CORE_PREFIX}ui/${rest}`;
|
||||
}
|
||||
const relative = /^(?:\.\.\/)+src\/(ui|types)(\/.*)?$/.exec(specifier);
|
||||
if (relative) {
|
||||
return LEGACY_CORE_PREFIX + clean(`${relative[1]}${relative[2] ?? ""}`);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Every value import in a source file that names a legacy core module.
|
||||
* Type-only imports are skipped: they are erased and never reach the map.
|
||||
*/
|
||||
export function findLegacyCoreImports(source) {
|
||||
const found = new Set();
|
||||
const patterns = [
|
||||
/^\s*(?:import|export)\s+(?!type\s)[^'";]*?\sfrom\s+["']([^"']+)["']/gm,
|
||||
/^\s*import\s+["']([^"']+)["']/gm,
|
||||
/import\(\s*["']([^"']+)["']\s*\)/g,
|
||||
];
|
||||
for (const pattern of patterns) {
|
||||
for (const match of source.matchAll(pattern)) {
|
||||
const normalized = legacyCoreSpecifier(match[1]);
|
||||
if (normalized) found.add(normalized);
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
Reference in new issue
Block a user