deck-plugin-sdk 1.10.3 → 1.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +97 -97
- package/bin/deck-plugin.mjs +158 -158
- package/build-plugin.mjs +172 -172
- package/package.json +60 -60
- package/packages.mjs +107 -107
- package/permissions.json +252 -241
- package/publish.mjs +116 -116
- package/shim/bridge.js +253 -242
- package/template/AGENTS.md +81 -81
- package/template/CLAUDE.md +1 -1
- package/template/LICENSE +21 -21
- package/template/README.md +20 -20
- package/template/gitignore +4 -4
- package/template/plugin.json +10 -10
- package/template/src/assets.d.ts +16 -16
- package/template/src/plugin.tsx +65 -65
- package/types/core/bridge.d.ts +52 -0
- package/validate.mjs +112 -112
package/packages.mjs
CHANGED
|
@@ -1,107 +1,107 @@
|
|
|
1
|
-
// What a plugin's npm packages need from the build. Shared by Deck's own plugin build
|
|
2
|
-
// (sdk/shim-builder.mjs, which makes the shim) and the SDK's (sdk/build-plugin.mjs), so the two cannot disagree.
|
|
3
|
-
//
|
|
4
|
-
// A plugin is installed as plugin.json and one plugin.js, so everything a package brings has to
|
|
5
|
-
// end up inside that one file: a sidecar image, font or stylesheet would never be installed.
|
|
6
|
-
//
|
|
7
|
-
// "The plugin's own code" is what is under its src/. Anything else is a package, whether it sits
|
|
8
|
-
// in node_modules or is linked in from another folder (`npm install ../my-lib`).
|
|
9
|
-
import { build } from "esbuild";
|
|
10
|
-
import { readFileSync } from "node:fs";
|
|
11
|
-
import { dirname, join, relative, sep } from "node:path";
|
|
12
|
-
|
|
13
|
-
/** Images and fonts the plugin or a package imports, inlined as data: URLs. */
|
|
14
|
-
export const assetLoaders = Object.fromEntries(
|
|
15
|
-
[".png", ".jpg", ".jpeg", ".gif", ".webp", ".avif", ".bmp", ".ico", ".svg", ".woff", ".woff2", ".ttf", ".otf", ".eot"]
|
|
16
|
-
.map((ext) => [ext, "dataurl"]),
|
|
17
|
-
);
|
|
18
|
-
|
|
19
|
-
const under = (path, dir) => !!path && (path + sep).toLowerCase().startsWith((dir + sep).toLowerCase());
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Whether an import of Deck's shared icons or animation library comes from a package rather than
|
|
23
|
-
* the plugin's own code. The plugin's own imports go to Deck's copies, through a shim that names
|
|
24
|
-
* only the exports its `src/` uses; a package's would find its names missing there, so a package
|
|
25
|
-
* gets the real library, bundled and tree-shaken like any other dependency.
|
|
26
|
-
*/
|
|
27
|
-
export const fromPackage = (args, dir) =>
|
|
28
|
-
(args.path === "lucide-react" || args.path === "framer-motion") && !!args.importer && !under(args.importer, join(dir, "src"));
|
|
29
|
-
|
|
30
|
-
/** `import "x.css"` with nothing bound: imported for its effect, so it is applied. */
|
|
31
|
-
function importedBare(importer, spec) {
|
|
32
|
-
let text;
|
|
33
|
-
try { text = readFileSync(importer, "utf8"); } catch { return false; }
|
|
34
|
-
const q = spec.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
35
|
-
return new RegExp(`(^|[;\\n])\\s*import\\s*["']${q}["']`).test(text);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* CSS imported for its effect is applied: CSS a package imports, and CSS any code imports with
|
|
40
|
-
* nothing bound (`import "some-lib/styles.css"`, `import "@fontsource/inter"`). It is bundled
|
|
41
|
-
* with its `@import`s and the images and fonts its `url()`s name, and added to the page as one
|
|
42
|
-
* <style> per file, replaced rather than duplicated when the plugin reloads.
|
|
43
|
-
*
|
|
44
|
-
* `import css from "./x.css"` in the plugin's own code still gives it the text, to inject where and
|
|
45
|
-
* when it wants (the canvas plugin does that with Excalidraw's stylesheet).
|
|
46
|
-
*/
|
|
47
|
-
export function packageCss(pluginId, dir) {
|
|
48
|
-
const src = join(dir, "src");
|
|
49
|
-
return {
|
|
50
|
-
name: "deck-package-css",
|
|
51
|
-
setup(b) {
|
|
52
|
-
b.onResolve({ filter: /.*/ }, async (args) => {
|
|
53
|
-
if (args.pluginData?.css || (args.kind !== "import-statement" && args.kind !== "require-call")) return undefined;
|
|
54
|
-
// The plugin's own code: a bare import, which may name a package whose entry is a
|
|
55
|
-
// stylesheet (@fontsource/inter). A package's: any import of a .css file.
|
|
56
|
-
const own = under(args.importer, src);
|
|
57
|
-
if (!(own ? importedBare(args.importer, args.path) : /\.css$/.test(args.path))) return undefined;
|
|
58
|
-
const r = await b.resolve(args.path, { kind: args.kind, resolveDir: args.resolveDir, importer: args.importer, pluginData: { css: true } });
|
|
59
|
-
if (r.errors.length) return { errors: r.errors };
|
|
60
|
-
if (!/\.css$/.test(r.path)) return undefined;
|
|
61
|
-
return { path: r.path, namespace: "deck-package-css" };
|
|
62
|
-
});
|
|
63
|
-
b.onLoad({ filter: /.*/, namespace: "deck-package-css" }, async (args) => {
|
|
64
|
-
const out = await build({ entryPoints: [args.path], outfile: "package.css", bundle: true, write: false, minify: true, loader: assetLoaders, logLevel: "silent" });
|
|
65
|
-
const css = out.outputFiles.find((f) => f.path.endsWith(".css"))?.text ?? "";
|
|
66
|
-
// Named by where it is inside the project, not on this machine: the bundle is shared.
|
|
67
|
-
const where = /[\\/]node_modules[\\/]/.test(args.path) ? args.path.replace(/^.*[\\/]node_modules[\\/]/, "") : relative(dir, args.path);
|
|
68
|
-
const key = `${pluginId}:${where.replace(/\\/g, "/")}`;
|
|
69
|
-
return {
|
|
70
|
-
resolveDir: dirname(args.path),
|
|
71
|
-
loader: "js",
|
|
72
|
-
contents: `const css = ${JSON.stringify(css)};
|
|
73
|
-
if (typeof document !== "undefined") {
|
|
74
|
-
const key = ${JSON.stringify(key)};
|
|
75
|
-
let s = [...document.head.querySelectorAll("style[data-deck-css]")].find((e) => e.dataset.deckCss === key);
|
|
76
|
-
if (!s) { s = document.createElement("style"); s.dataset.deckCss = key; document.head.appendChild(s); }
|
|
77
|
-
s.textContent = css;
|
|
78
|
-
}
|
|
79
|
-
export default css;
|
|
80
|
-
`,
|
|
81
|
-
};
|
|
82
|
-
});
|
|
83
|
-
},
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* `import text from "some/file?raw"` gives a file's contents as a string, the convention Vite
|
|
89
|
-
* uses. What a library needs as text rather than as code: PDF.js's worker, which a plugin starts
|
|
90
|
-
* from a Blob URL because only plugin.js is installed (see the Requests plugin's script worker).
|
|
91
|
-
*
|
|
92
|
-
* `?base64` gives any file's bytes as a base64 string instead: data a library asks for only
|
|
93
|
-
* sometimes (PDF.js's font files and image decoders), decoded when it does rather than every time
|
|
94
|
-
* Deck starts, as a `.wasm` import would be.
|
|
95
|
-
*/
|
|
96
|
-
export const rawImports = {
|
|
97
|
-
name: "deck-raw",
|
|
98
|
-
setup(b) {
|
|
99
|
-
b.onResolve({ filter: /\?(raw|base64)$/ }, async (args) => {
|
|
100
|
-
const as = args.path.endsWith("?raw") ? "text" : "base64";
|
|
101
|
-
const r = await b.resolve(args.path.replace(/\?(raw|base64)$/, ""), { kind: args.kind, resolveDir: args.resolveDir, importer: args.importer });
|
|
102
|
-
if (r.errors.length) return { errors: r.errors };
|
|
103
|
-
return { path: r.path, namespace: "deck-raw", pluginData: { as } };
|
|
104
|
-
});
|
|
105
|
-
b.onLoad({ filter: /.*/, namespace: "deck-raw" }, (args) => ({ contents: readFileSync(args.path), loader: args.pluginData?.as === "base64" ? "base64" : "text" }));
|
|
106
|
-
},
|
|
107
|
-
};
|
|
1
|
+
// What a plugin's npm packages need from the build. Shared by Deck's own plugin build
|
|
2
|
+
// (sdk/shim-builder.mjs, which makes the shim) and the SDK's (sdk/build-plugin.mjs), so the two cannot disagree.
|
|
3
|
+
//
|
|
4
|
+
// A plugin is installed as plugin.json and one plugin.js, so everything a package brings has to
|
|
5
|
+
// end up inside that one file: a sidecar image, font or stylesheet would never be installed.
|
|
6
|
+
//
|
|
7
|
+
// "The plugin's own code" is what is under its src/. Anything else is a package, whether it sits
|
|
8
|
+
// in node_modules or is linked in from another folder (`npm install ../my-lib`).
|
|
9
|
+
import { build } from "esbuild";
|
|
10
|
+
import { readFileSync } from "node:fs";
|
|
11
|
+
import { dirname, join, relative, sep } from "node:path";
|
|
12
|
+
|
|
13
|
+
/** Images and fonts the plugin or a package imports, inlined as data: URLs. */
|
|
14
|
+
export const assetLoaders = Object.fromEntries(
|
|
15
|
+
[".png", ".jpg", ".jpeg", ".gif", ".webp", ".avif", ".bmp", ".ico", ".svg", ".woff", ".woff2", ".ttf", ".otf", ".eot"]
|
|
16
|
+
.map((ext) => [ext, "dataurl"]),
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
const under = (path, dir) => !!path && (path + sep).toLowerCase().startsWith((dir + sep).toLowerCase());
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Whether an import of Deck's shared icons or animation library comes from a package rather than
|
|
23
|
+
* the plugin's own code. The plugin's own imports go to Deck's copies, through a shim that names
|
|
24
|
+
* only the exports its `src/` uses; a package's would find its names missing there, so a package
|
|
25
|
+
* gets the real library, bundled and tree-shaken like any other dependency.
|
|
26
|
+
*/
|
|
27
|
+
export const fromPackage = (args, dir) =>
|
|
28
|
+
(args.path === "lucide-react" || args.path === "framer-motion") && !!args.importer && !under(args.importer, join(dir, "src"));
|
|
29
|
+
|
|
30
|
+
/** `import "x.css"` with nothing bound: imported for its effect, so it is applied. */
|
|
31
|
+
function importedBare(importer, spec) {
|
|
32
|
+
let text;
|
|
33
|
+
try { text = readFileSync(importer, "utf8"); } catch { return false; }
|
|
34
|
+
const q = spec.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
35
|
+
return new RegExp(`(^|[;\\n])\\s*import\\s*["']${q}["']`).test(text);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* CSS imported for its effect is applied: CSS a package imports, and CSS any code imports with
|
|
40
|
+
* nothing bound (`import "some-lib/styles.css"`, `import "@fontsource/inter"`). It is bundled
|
|
41
|
+
* with its `@import`s and the images and fonts its `url()`s name, and added to the page as one
|
|
42
|
+
* <style> per file, replaced rather than duplicated when the plugin reloads.
|
|
43
|
+
*
|
|
44
|
+
* `import css from "./x.css"` in the plugin's own code still gives it the text, to inject where and
|
|
45
|
+
* when it wants (the canvas plugin does that with Excalidraw's stylesheet).
|
|
46
|
+
*/
|
|
47
|
+
export function packageCss(pluginId, dir) {
|
|
48
|
+
const src = join(dir, "src");
|
|
49
|
+
return {
|
|
50
|
+
name: "deck-package-css",
|
|
51
|
+
setup(b) {
|
|
52
|
+
b.onResolve({ filter: /.*/ }, async (args) => {
|
|
53
|
+
if (args.pluginData?.css || (args.kind !== "import-statement" && args.kind !== "require-call")) return undefined;
|
|
54
|
+
// The plugin's own code: a bare import, which may name a package whose entry is a
|
|
55
|
+
// stylesheet (@fontsource/inter). A package's: any import of a .css file.
|
|
56
|
+
const own = under(args.importer, src);
|
|
57
|
+
if (!(own ? importedBare(args.importer, args.path) : /\.css$/.test(args.path))) return undefined;
|
|
58
|
+
const r = await b.resolve(args.path, { kind: args.kind, resolveDir: args.resolveDir, importer: args.importer, pluginData: { css: true } });
|
|
59
|
+
if (r.errors.length) return { errors: r.errors };
|
|
60
|
+
if (!/\.css$/.test(r.path)) return undefined;
|
|
61
|
+
return { path: r.path, namespace: "deck-package-css" };
|
|
62
|
+
});
|
|
63
|
+
b.onLoad({ filter: /.*/, namespace: "deck-package-css" }, async (args) => {
|
|
64
|
+
const out = await build({ entryPoints: [args.path], outfile: "package.css", bundle: true, write: false, minify: true, loader: assetLoaders, logLevel: "silent" });
|
|
65
|
+
const css = out.outputFiles.find((f) => f.path.endsWith(".css"))?.text ?? "";
|
|
66
|
+
// Named by where it is inside the project, not on this machine: the bundle is shared.
|
|
67
|
+
const where = /[\\/]node_modules[\\/]/.test(args.path) ? args.path.replace(/^.*[\\/]node_modules[\\/]/, "") : relative(dir, args.path);
|
|
68
|
+
const key = `${pluginId}:${where.replace(/\\/g, "/")}`;
|
|
69
|
+
return {
|
|
70
|
+
resolveDir: dirname(args.path),
|
|
71
|
+
loader: "js",
|
|
72
|
+
contents: `const css = ${JSON.stringify(css)};
|
|
73
|
+
if (typeof document !== "undefined") {
|
|
74
|
+
const key = ${JSON.stringify(key)};
|
|
75
|
+
let s = [...document.head.querySelectorAll("style[data-deck-css]")].find((e) => e.dataset.deckCss === key);
|
|
76
|
+
if (!s) { s = document.createElement("style"); s.dataset.deckCss = key; document.head.appendChild(s); }
|
|
77
|
+
s.textContent = css;
|
|
78
|
+
}
|
|
79
|
+
export default css;
|
|
80
|
+
`,
|
|
81
|
+
};
|
|
82
|
+
});
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* `import text from "some/file?raw"` gives a file's contents as a string, the convention Vite
|
|
89
|
+
* uses. What a library needs as text rather than as code: PDF.js's worker, which a plugin starts
|
|
90
|
+
* from a Blob URL because only plugin.js is installed (see the Requests plugin's script worker).
|
|
91
|
+
*
|
|
92
|
+
* `?base64` gives any file's bytes as a base64 string instead: data a library asks for only
|
|
93
|
+
* sometimes (PDF.js's font files and image decoders), decoded when it does rather than every time
|
|
94
|
+
* Deck starts, as a `.wasm` import would be.
|
|
95
|
+
*/
|
|
96
|
+
export const rawImports = {
|
|
97
|
+
name: "deck-raw",
|
|
98
|
+
setup(b) {
|
|
99
|
+
b.onResolve({ filter: /\?(raw|base64)$/ }, async (args) => {
|
|
100
|
+
const as = args.path.endsWith("?raw") ? "text" : "base64";
|
|
101
|
+
const r = await b.resolve(args.path.replace(/\?(raw|base64)$/, ""), { kind: args.kind, resolveDir: args.resolveDir, importer: args.importer });
|
|
102
|
+
if (r.errors.length) return { errors: r.errors };
|
|
103
|
+
return { path: r.path, namespace: "deck-raw", pluginData: { as } };
|
|
104
|
+
});
|
|
105
|
+
b.onLoad({ filter: /.*/, namespace: "deck-raw" }, (args) => ({ contents: readFileSync(args.path), loader: args.pluginData?.as === "base64" ? "base64" : "text" }));
|
|
106
|
+
},
|
|
107
|
+
};
|