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/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
+ };