@dirwell/theme-authoring 0.0.0-stage → 0.0.1-alpha.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Dirwell contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,67 @@
1
- # Temporary Holding Version
1
+ # @dirwell/theme-authoring
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Compile trusted Svelte/Vue wrapper components
4
+ into matching SSR and browser implementations. Plain and core do not depend on
5
+ this package or its compilers.
6
+
7
+ ```js
8
+ import { buildFrameworkTheme } from "@dirwell/theme-authoring";
9
+ const result = await buildFrameworkTheme({
10
+ framework: "svelte", // 'vue' for Crosswave
11
+ component: "./Ledger.svelte",
12
+ output: "./generated-theme",
13
+ });
14
+ const { createTheme } = await import(result.themeUrl);
15
+ // Pass createTheme({title: 'Downloads'}) to generateExplorer or createExplorerHandler.
16
+ ```
17
+
18
+ Import Ledger from `@dirwell/theme-ledger/source`, or Crosswave from
19
+ `@dirwell/theme-crosswave/source`. Your wrapper accepts the `model` prop; public
20
+ model and row context types are available from each theme's `/model` entry.
21
+ Ledger accepts `header`, `row` and `footer` snippets. Crosswave exposes named slots
22
+ with those names. Header/footer receive `{model}`; row receives
23
+ `{model, entry, selected}`. These contexts track the current directory after
24
+ navigation or live updates. Row content lives inside the official link: do not
25
+ nest links, buttons, or other interactive controls inside it. Official list
26
+ identity, keyboard selection, link actions, virtualization, pagination, dialogs,
27
+ appearance controls and gamepad lifecycle remain owned by the theme.
28
+
29
+ See `examples/authoring` in the repository for complete wrappers. These are source
30
+ customizations, not serializable CLI options. The output's `theme.mjs#createTheme`
31
+ can be selected with the CLI's existing explicit module-factory argument.
32
+
33
+ Both targets use pinned framework runtimes and official Vite plugins. Nested
34
+ components, TypeScript script blocks, component CSS and static imported assets
35
+ are compiled together. Dedicated output directories have ownership markers and
36
+ are replaced only after both targets succeed. Failed builds retain the previous
37
+ output. Do not run the compiler while serving files from its output; rebuild,
38
+ create a fresh theme instance, then restart/swap the consumer. Node module caches
39
+ require a fresh import URL after replacing an already-imported `theme.mjs`.
40
+
41
+ ## Local development
42
+
43
+ ```js
44
+ import { startAuthoringServer } from "@dirwell/theme-authoring/dev";
45
+ const server = await startAuthoringServer({
46
+ framework: "vue",
47
+ component: "./Crosswave.vue",
48
+ explorer: { root: "./files", live: { intervalMs: 500 } },
49
+ });
50
+ console.log(server.url);
51
+ // Await server.close() when finished; it owns Vite, sockets and filesystem watchers.
52
+ ```
53
+
54
+ The server binds to loopback only. Use Portless to provide a named development
55
+ URL. This is a trusted local source-development server, not a production
56
+ transport or authorization boundary: Vite serves project source modules. Do not
57
+ proxy it publicly. The production server remains `@dirwell/server`.
58
+
59
+ Source edits use official framework HMR; file changes use the explorer's separate
60
+ SSE revision protocol. CSS-only edits preserve the current component state. Wrapper template/snippet
61
+ edits and script changes can remount child components and reset search/selection;
62
+ HMR retains the current directory model, but does not guarantee filter/selection
63
+ persistence for those edits. Source writes are coalesced until stable for 100 ms
64
+ before compilation, so partial editor writes can recover cleanly. Compiler errors
65
+ produce Vite's overlay and recover after a valid edit. Development CSS loads
66
+ through Vite's client, so production output is the no-JavaScript/CSS acceptance
67
+ path. No Farm watcher is involved.
@@ -0,0 +1,76 @@
1
+ import { createRequire } from "node:module";
2
+ import path from "node:path";
3
+ import { svelte } from "@sveltejs/vite-plugin-svelte";
4
+ import vue from "@vitejs/plugin-vue";
5
+ //#region src/compiler.ts
6
+ const require = createRequire(import.meta.url);
7
+ const vueRequire = createRequire(require.resolve("vue/package.json"));
8
+ const domRequire = createRequire(vueRequire.resolve("@vue/runtime-dom/package.json"));
9
+ const coreRequire = createRequire(domRequire.resolve("@vue/runtime-core/package.json"));
10
+ const vueAliases = [
11
+ ["vue", require.resolve("vue/dist/vue.runtime.esm-bundler.js")],
12
+ ["vue/server-renderer", vueRequire.resolve("@vue/server-renderer/dist/server-renderer.esm-bundler.js")],
13
+ ["@vue/server-renderer", vueRequire.resolve("@vue/server-renderer/dist/server-renderer.esm-bundler.js")],
14
+ ["@vue/runtime-dom", vueRequire.resolve("@vue/runtime-dom/dist/runtime-dom.esm-bundler.js")],
15
+ ["@vue/runtime-core", domRequire.resolve("@vue/runtime-core/dist/runtime-core.esm-bundler.js")],
16
+ ["@vue/reactivity", coreRequire.resolve("@vue/reactivity/dist/reactivity.esm-bundler.js")],
17
+ ["@vue/shared", vueRequire.resolve("@vue/shared/dist/shared.esm-bundler.js")]
18
+ ].map(([name, replacement]) => ({
19
+ find: new RegExp(`^${name}$`),
20
+ replacement
21
+ }));
22
+ /** Both targets resolve the exact runtime shipped with this compiler. */
23
+ function frameworkConfig(framework, root) {
24
+ return {
25
+ configFile: false,
26
+ root,
27
+ publicDir: false,
28
+ logLevel: "warn",
29
+ plugins: [
30
+ {
31
+ name: "dirwell-pinned-runtime",
32
+ enforce: "pre",
33
+ resolveId(source, _importer, options) {
34
+ if (source === "svelte") return options.ssr ? require.resolve("svelte") : path.join(path.dirname(require.resolve("svelte/package.json")), "src/index-client.js");
35
+ if (source.startsWith("svelte/") || source === "@dirwell/theme-ledger/source" || source === "@dirwell/theme-crosswave/source" || source === "vue/server-renderer") return require.resolve(source);
36
+ }
37
+ },
38
+ ...framework === "svelte" ? [svelte({
39
+ configFile: false,
40
+ prebundleSvelteLibraries: false,
41
+ disableDependencyReinclusion: true
42
+ })] : [vue()],
43
+ {
44
+ name: "dirwell-runtime-optimization",
45
+ enforce: "post",
46
+ configResolved(config) {
47
+ if (config.optimizeDeps.include) config.optimizeDeps.include = config.optimizeDeps.include.filter((name) => !name.startsWith("svelte"));
48
+ for (const environment of Object.values(config.environments)) if (environment.optimizeDeps.include) environment.optimizeDeps.include = environment.optimizeDeps.include.filter((name) => !name.startsWith("svelte"));
49
+ }
50
+ }
51
+ ],
52
+ resolve: {
53
+ dedupe: [
54
+ "svelte",
55
+ "vue",
56
+ "@vue/runtime-core",
57
+ "@vue/runtime-dom",
58
+ "@vue/reactivity"
59
+ ],
60
+ alias: vueAliases
61
+ },
62
+ ssr: { noExternal: true }
63
+ };
64
+ }
65
+ function entrySource(framework, component, target) {
66
+ const app = `import App from ${JSON.stringify(component)};`;
67
+ if (target === "server") return app + (framework === "svelte" ? "import {render} from 'svelte/server';export const renderBody=model=>render(App,{props:{model}}).body;" : "import {createSSRApp} from 'vue';import {renderToString} from 'vue/server-renderer';export const renderBody=model=>renderToString(createSSRApp(App,{model}));");
68
+ return app + "const model=JSON.parse(document.querySelector('#dirwell-page-data').textContent);const target=document.querySelector('[data-dirwell-theme]');" + (framework === "svelte" ? "import {hydrate,unmount} from 'svelte';const app=hydrate(App,{target,props:{model}});globalThis.__dirwellUnmount=()=>unmount(app);" : "import {createSSRApp} from 'vue';const app=createSSRApp(App,{model});app.mount(target);globalThis.__dirwellUnmount=()=>app.unmount();");
69
+ }
70
+ function validateFramework(framework) {
71
+ if (framework !== "svelte" && framework !== "vue") throw new TypeError("Unsupported theme framework");
72
+ }
73
+ //#endregion
74
+ export { frameworkConfig as n, validateFramework as r, entrySource as t };
75
+
76
+ //# sourceMappingURL=compiler-C0rEfJCH.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compiler-C0rEfJCH.mjs","names":[],"sources":["../src/compiler.ts"],"sourcesContent":["import { createRequire } from \"node:module\";\nimport path from \"node:path\";\nimport { svelte } from \"@sveltejs/vite-plugin-svelte\";\nimport vue from \"@vitejs/plugin-vue\";\nimport type { InlineConfig } from \"vite\";\nexport type Framework = \"svelte\" | \"vue\";\nconst require = createRequire(import.meta.url);\nconst vueRequire = createRequire(require.resolve(\"vue/package.json\"));\nconst domRequire = createRequire(vueRequire.resolve(\"@vue/runtime-dom/package.json\"));\nconst coreRequire = createRequire(domRequire.resolve(\"@vue/runtime-core/package.json\"));\nconst vueAliases = [\n [\"vue\", require.resolve(\"vue/dist/vue.runtime.esm-bundler.js\")],\n [\n \"vue/server-renderer\",\n vueRequire.resolve(\"@vue/server-renderer/dist/server-renderer.esm-bundler.js\"),\n ],\n [\n \"@vue/server-renderer\",\n vueRequire.resolve(\"@vue/server-renderer/dist/server-renderer.esm-bundler.js\"),\n ],\n [\"@vue/runtime-dom\", vueRequire.resolve(\"@vue/runtime-dom/dist/runtime-dom.esm-bundler.js\")],\n [\"@vue/runtime-core\", domRequire.resolve(\"@vue/runtime-core/dist/runtime-core.esm-bundler.js\")],\n [\"@vue/reactivity\", coreRequire.resolve(\"@vue/reactivity/dist/reactivity.esm-bundler.js\")],\n [\"@vue/shared\", vueRequire.resolve(\"@vue/shared/dist/shared.esm-bundler.js\")],\n].map(([name, replacement]) => ({ find: new RegExp(`^${name}$`), replacement: replacement! }));\n/** Both targets resolve the exact runtime shipped with this compiler. */\nexport function frameworkConfig(framework: Framework, root: string): InlineConfig {\n return {\n configFile: false,\n root,\n publicDir: false,\n logLevel: \"warn\",\n plugins: [\n {\n name: \"dirwell-pinned-runtime\",\n enforce: \"pre\",\n resolveId(source, _importer, options) {\n if (source === \"svelte\")\n return options.ssr\n ? require.resolve(\"svelte\")\n : path.join(\n path.dirname(require.resolve(\"svelte/package.json\")),\n \"src/index-client.js\",\n );\n if (\n source.startsWith(\"svelte/\") ||\n source === \"@dirwell/theme-ledger/source\" ||\n source === \"@dirwell/theme-crosswave/source\" ||\n source === \"vue/server-renderer\"\n )\n return require.resolve(source);\n return undefined;\n },\n },\n ...(framework === \"svelte\"\n ? [\n svelte({\n configFile: false,\n prebundleSvelteLibraries: false,\n disableDependencyReinclusion: true,\n }),\n ]\n : [vue()]),\n {\n name: \"dirwell-runtime-optimization\",\n enforce: \"post\",\n configResolved(config) {\n // Runtime imports resolve to compiler-owned absolute paths, not project dependencies.\n if (config.optimizeDeps.include)\n config.optimizeDeps.include = config.optimizeDeps.include.filter(\n (name) => !name.startsWith(\"svelte\"),\n );\n for (const environment of Object.values(config.environments)) {\n if (environment.optimizeDeps.include)\n environment.optimizeDeps.include = environment.optimizeDeps.include.filter(\n (name) => !name.startsWith(\"svelte\"),\n );\n }\n },\n },\n ],\n resolve: {\n dedupe: [\"svelte\", \"vue\", \"@vue/runtime-core\", \"@vue/runtime-dom\", \"@vue/reactivity\"],\n alias: vueAliases,\n },\n ssr: { noExternal: true },\n };\n}\nexport function entrySource(framework: Framework, component: string, target: \"server\" | \"client\") {\n const app = `import App from ${JSON.stringify(component)};`;\n if (target === \"server\")\n return (\n app +\n (framework === \"svelte\"\n ? \"import {render} from 'svelte/server';export const renderBody=model=>render(App,{props:{model}}).body;\"\n : \"import {createSSRApp} from 'vue';import {renderToString} from 'vue/server-renderer';export const renderBody=model=>renderToString(createSSRApp(App,{model}));\")\n );\n const data =\n \"const model=JSON.parse(document.querySelector('#dirwell-page-data').textContent);const target=document.querySelector('[data-dirwell-theme]');\";\n return (\n app +\n data +\n (framework === \"svelte\"\n ? \"import {hydrate,unmount} from 'svelte';const app=hydrate(App,{target,props:{model}});globalThis.__dirwellUnmount=()=>unmount(app);\"\n : \"import {createSSRApp} from 'vue';const app=createSSRApp(App,{model});app.mount(target);globalThis.__dirwellUnmount=()=>app.unmount();\")\n );\n}\nexport function validateFramework(framework: Framework) {\n if (framework !== \"svelte\" && framework !== \"vue\")\n throw new TypeError(\"Unsupported theme framework\");\n}\n"],"mappings":";;;;;AAMA,MAAM,UAAU,cAAc,YAAY,GAAG;AAC7C,MAAM,aAAa,cAAc,QAAQ,QAAQ,kBAAkB,CAAC;AACpE,MAAM,aAAa,cAAc,WAAW,QAAQ,+BAA+B,CAAC;AACpF,MAAM,cAAc,cAAc,WAAW,QAAQ,gCAAgC,CAAC;AACtF,MAAM,aAAa;CACjB,CAAC,OAAO,QAAQ,QAAQ,qCAAqC,CAAC;CAC9D,CACE,uBACA,WAAW,QAAQ,0DAA0D,CAC/E;CACA,CACE,wBACA,WAAW,QAAQ,0DAA0D,CAC/E;CACA,CAAC,oBAAoB,WAAW,QAAQ,kDAAkD,CAAC;CAC3F,CAAC,qBAAqB,WAAW,QAAQ,oDAAoD,CAAC;CAC9F,CAAC,mBAAmB,YAAY,QAAQ,gDAAgD,CAAC;CACzF,CAAC,eAAe,WAAW,QAAQ,wCAAwC,CAAC;AAC9E,CAAC,CAAC,KAAK,CAAC,MAAM,kBAAkB;CAAE,MAAM,IAAI,OAAO,IAAI,KAAK,EAAE;CAAgB;AAAa,EAAE;;AAE7F,SAAgB,gBAAgB,WAAsB,MAA4B;CAChF,OAAO;EACL,YAAY;EACZ;EACA,WAAW;EACX,UAAU;EACV,SAAS;GACP;IACE,MAAM;IACN,SAAS;IACT,UAAU,QAAQ,WAAW,SAAS;KACpC,IAAI,WAAW,UACb,OAAO,QAAQ,MACX,QAAQ,QAAQ,QAAQ,IACxB,KAAK,KACH,KAAK,QAAQ,QAAQ,QAAQ,qBAAqB,CAAC,GACnD,qBACF;KACN,IACE,OAAO,WAAW,SAAS,KAC3B,WAAW,kCACX,WAAW,qCACX,WAAW,uBAEX,OAAO,QAAQ,QAAQ,MAAM;IAEjC;GACF;GACA,GAAI,cAAc,WACd,CACE,OAAO;IACL,YAAY;IACZ,0BAA0B;IAC1B,8BAA8B;GAChC,CAAC,CACH,IACA,CAAC,IAAI,CAAC;GACV;IACE,MAAM;IACN,SAAS;IACT,eAAe,QAAQ;KAErB,IAAI,OAAO,aAAa,SACtB,OAAO,aAAa,UAAU,OAAO,aAAa,QAAQ,QACvD,SAAS,CAAC,KAAK,WAAW,QAAQ,CACrC;KACF,KAAK,MAAM,eAAe,OAAO,OAAO,OAAO,YAAY,GACzD,IAAI,YAAY,aAAa,SAC3B,YAAY,aAAa,UAAU,YAAY,aAAa,QAAQ,QACjE,SAAS,CAAC,KAAK,WAAW,QAAQ,CACrC;IAEN;GACF;EACF;EACA,SAAS;GACP,QAAQ;IAAC;IAAU;IAAO;IAAqB;IAAoB;GAAiB;GACpF,OAAO;EACT;EACA,KAAK,EAAE,YAAY,KAAK;CAC1B;AACF;AACA,SAAgB,YAAY,WAAsB,WAAmB,QAA6B;CAChG,MAAM,MAAM,mBAAmB,KAAK,UAAU,SAAS,EAAE;CACzD,IAAI,WAAW,UACb,OACE,OACC,cAAc,WACX,0GACA;CAIR,OACE,MACA,mJACC,cAAc,WACX,uIACA;AAER;AACA,SAAgB,kBAAkB,WAAsB;CACtD,IAAI,cAAc,YAAY,cAAc,OAC1C,MAAM,IAAI,UAAU,6BAA6B;AACrD"}
@@ -0,0 +1,6 @@
1
+ import "vite";
2
+ //#region src/compiler.d.ts
3
+ type Framework = "svelte" | "vue";
4
+ //#endregion
5
+ export { Framework as t };
6
+ //# sourceMappingURL=compiler-DwxVCNyK.d.mts.map
package/dist/dev.d.mts ADDED
@@ -0,0 +1,19 @@
1
+ import { t as Framework } from "./compiler-DwxVCNyK.mjs";
2
+ import { LedgerThemeOptions } from "@dirwell/theme-ledger";
3
+ import { CrosswaveThemeOptions } from "@dirwell/theme-crosswave";
4
+ import { ExplorerHandlerOptions } from "@dirwell/server";
5
+ //#region src/dev.d.ts
6
+ export interface AuthoringServerOptions {
7
+ readonly framework: Framework;
8
+ readonly component: string;
9
+ readonly explorer: Omit<ExplorerHandlerOptions, "theme" | "mountPath">;
10
+ readonly themeOptions?: LedgerThemeOptions & CrosswaveThemeOptions;
11
+ readonly port?: number;
12
+ }
13
+ /** Trusted local development only: Vite serves executable source modules as well as explorer data. */
14
+ export declare function startAuthoringServer(options: AuthoringServerOptions): Promise<{
15
+ url: string;
16
+ close: () => Promise<void>;
17
+ }>;
18
+ //#endregion
19
+ //# sourceMappingURL=dev.d.mts.map
package/dist/dev.mjs ADDED
@@ -0,0 +1,105 @@
1
+ import { n as frameworkConfig, r as validateFramework, t as entrySource } from "./compiler-C0rEfJCH.mjs";
2
+ import { createRequire } from "node:module";
3
+ import { createServer, searchForWorkspaceRoot } from "vite";
4
+ import { realpath } from "node:fs/promises";
5
+ import path from "node:path";
6
+ import { createNodeRequestListener } from "@dirwell/server/node";
7
+ import { createLedgerTheme } from "@dirwell/theme-ledger";
8
+ import { createCrosswaveTheme } from "@dirwell/theme-crosswave";
9
+ //#region src/dev.ts
10
+ /** Trusted local development only: Vite serves executable source modules as well as explorer data. */
11
+ async function startAuthoringServer(options) {
12
+ validateFramework(options.framework);
13
+ const component = await realpath(path.resolve(options.component));
14
+ if (path.extname(component) !== (options.framework === "svelte" ? ".svelte" : ".vue")) throw new TypeError("Component extension does not match framework");
15
+ const config = frameworkConfig(options.framework, path.dirname(component));
16
+ const require = createRequire(import.meta.url);
17
+ const id = "/@dirwell/authoring-";
18
+ const vite = await createServer({
19
+ ...config,
20
+ appType: "custom",
21
+ mode: "development",
22
+ define: {
23
+ "process.env.NODE_ENV": JSON.stringify("development"),
24
+ __VUE_OPTIONS_API__: "true",
25
+ __VUE_PROD_DEVTOOLS__: "false",
26
+ __VUE_PROD_HYDRATION_MISMATCH_DETAILS__: "false"
27
+ },
28
+ optimizeDeps: {
29
+ noDiscovery: true,
30
+ include: []
31
+ },
32
+ ssr: {
33
+ noExternal: true,
34
+ optimizeDeps: {
35
+ noDiscovery: true,
36
+ include: []
37
+ }
38
+ },
39
+ server: {
40
+ host: "127.0.0.1",
41
+ port: options.port ?? 0,
42
+ strictPort: true,
43
+ watch: { awaitWriteFinish: {
44
+ stabilityThreshold: 100,
45
+ pollInterval: 20
46
+ } },
47
+ fs: { allow: [searchForWorkspaceRoot(path.dirname(component)), searchForWorkspaceRoot(path.dirname(require.resolve("@dirwell/theme-ledger/source")))] }
48
+ },
49
+ plugins: [...config.plugins ?? [], {
50
+ name: "dirwell-authoring-entry",
51
+ resolveId: (source) => source.startsWith(id) ? `\0${source}` : void 0,
52
+ load(source) {
53
+ if (source === `\0${id}client`) return entrySource(options.framework, component, "client");
54
+ if (source === `\0${id}server`) return entrySource(options.framework, component, "server");
55
+ }
56
+ }]
57
+ });
58
+ try {
59
+ const renderBody = async (model) => {
60
+ const module = await vite.ssrLoadModule(`${id}server`).catch((error) => {
61
+ if (error instanceof Error) {
62
+ vite.ssrFixStacktrace(error);
63
+ vite.config.logger.error(error.stack ?? error.message);
64
+ }
65
+ throw error;
66
+ });
67
+ if (module === null || typeof module !== "object" || !("renderBody" in module) || typeof module.renderBody !== "function") throw new Error("Invalid compiled renderer");
68
+ const body = await module.renderBody(model);
69
+ if (typeof body !== "string") throw new Error("Invalid compiled body");
70
+ return body;
71
+ };
72
+ const renderer = {
73
+ renderBody,
74
+ clientScript: `import '/@vite/client';import '${id}client';`,
75
+ clientType: "module"
76
+ };
77
+ const theme = options.framework === "svelte" ? createLedgerTheme(options.themeOptions, renderer) : createCrosswaveTheme(options.themeOptions, renderer);
78
+ const listener = await createNodeRequestListener({
79
+ files: "media",
80
+ ...options.explorer,
81
+ theme
82
+ });
83
+ vite.middlewares.use((request, response, next) => {
84
+ if (request.url?.startsWith("/@") || request.url?.startsWith("/node_modules/")) {
85
+ next();
86
+ return;
87
+ }
88
+ listener(request, response);
89
+ });
90
+ await vite.listen();
91
+ const address = vite.httpServer?.address();
92
+ if (!address || typeof address === "string") throw new Error("Authoring server address unavailable");
93
+ return {
94
+ url: `http://127.0.0.1:${address.port}`,
95
+ close: () => vite.close()
96
+ };
97
+ } catch (error) {
98
+ await vite.close();
99
+ throw error;
100
+ }
101
+ }
102
+ //#endregion
103
+ export { startAuthoringServer };
104
+
105
+ //# sourceMappingURL=dev.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev.mjs","names":[],"sources":["../src/dev.ts"],"sourcesContent":["import { createServer, searchForWorkspaceRoot } from \"vite\";\nimport { realpath } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { createRequire } from \"node:module\";\nimport { createNodeRequestListener } from \"@dirwell/server/node\";\nimport type { ExplorerHandlerOptions } from \"@dirwell/server\";\nimport { createLedgerTheme, type LedgerThemeOptions, type ThemePage } from \"@dirwell/theme-ledger\";\nimport { createCrosswaveTheme, type CrosswaveThemeOptions } from \"@dirwell/theme-crosswave\";\nimport { frameworkConfig, entrySource, validateFramework, type Framework } from \"./compiler.ts\";\nexport interface AuthoringServerOptions {\n readonly framework: Framework;\n readonly component: string;\n readonly explorer: Omit<ExplorerHandlerOptions, \"theme\" | \"mountPath\">;\n readonly themeOptions?: LedgerThemeOptions & CrosswaveThemeOptions;\n readonly port?: number;\n}\n/** Trusted local development only: Vite serves executable source modules as well as explorer data. */\nexport async function startAuthoringServer(options: AuthoringServerOptions) {\n validateFramework(options.framework);\n const component = await realpath(path.resolve(options.component));\n if (path.extname(component) !== (options.framework === \"svelte\" ? \".svelte\" : \".vue\"))\n throw new TypeError(\"Component extension does not match framework\");\n const config = frameworkConfig(options.framework, path.dirname(component));\n const require = createRequire(import.meta.url);\n const id = \"/@dirwell/authoring-\";\n const vite = await createServer({\n ...config,\n appType: \"custom\",\n mode: \"development\",\n define: {\n \"process.env.NODE_ENV\": JSON.stringify(\"development\"),\n __VUE_OPTIONS_API__: \"true\",\n __VUE_PROD_DEVTOOLS__: \"false\",\n __VUE_PROD_HYDRATION_MISMATCH_DETAILS__: \"false\",\n },\n optimizeDeps: { noDiscovery: true, include: [] },\n ssr: { noExternal: true, optimizeDeps: { noDiscovery: true, include: [] } },\n server: {\n host: \"127.0.0.1\",\n port: options.port ?? 0,\n strictPort: true,\n watch: { awaitWriteFinish: { stabilityThreshold: 100, pollInterval: 20 } },\n fs: {\n allow: [\n searchForWorkspaceRoot(path.dirname(component)),\n searchForWorkspaceRoot(path.dirname(require.resolve(\"@dirwell/theme-ledger/source\"))),\n ],\n },\n },\n plugins: [\n ...(config.plugins ?? []),\n {\n name: \"dirwell-authoring-entry\",\n resolveId: (source) => (source.startsWith(id) ? `\\0${source}` : undefined),\n load(source) {\n if (source === `\\0${id}client`)\n return entrySource(options.framework, component, \"client\");\n if (source === `\\0${id}server`)\n return entrySource(options.framework, component, \"server\");\n return undefined;\n },\n },\n ],\n });\n try {\n const renderBody = async (model: ThemePage) => {\n const module: unknown = await vite.ssrLoadModule(`${id}server`).catch((error: unknown) => {\n if (error instanceof Error) {\n vite.ssrFixStacktrace(error);\n vite.config.logger.error(error.stack ?? error.message);\n }\n throw error;\n });\n if (\n module === null ||\n typeof module !== \"object\" ||\n !(\"renderBody\" in module) ||\n typeof module.renderBody !== \"function\"\n )\n throw new Error(\"Invalid compiled renderer\");\n const body: unknown = await module.renderBody(model);\n if (typeof body !== \"string\") throw new Error(\"Invalid compiled body\");\n return body;\n };\n const renderer = {\n renderBody,\n clientScript: `import '/@vite/client';import '${id}client';`,\n clientType: \"module\" as const,\n };\n const theme =\n options.framework === \"svelte\"\n ? createLedgerTheme(options.themeOptions, renderer)\n : createCrosswaveTheme(options.themeOptions, renderer);\n const listener = await createNodeRequestListener({\n files: \"media\",\n ...options.explorer,\n theme,\n });\n vite.middlewares.use((request, response, next) => {\n if (request.url?.startsWith(\"/@\") || request.url?.startsWith(\"/node_modules/\")) {\n next();\n return;\n }\n void listener(request, response);\n });\n await vite.listen();\n const address = vite.httpServer?.address();\n if (!address || typeof address === \"string\")\n throw new Error(\"Authoring server address unavailable\");\n return { url: `http://127.0.0.1:${address.port}`, close: () => vite.close() };\n } catch (error) {\n await vite.close();\n throw error;\n }\n}\n"],"mappings":";;;;;;;;;;AAiBA,eAAsB,qBAAqB,SAAiC;CAC1E,kBAAkB,QAAQ,SAAS;CACnC,MAAM,YAAY,MAAM,SAAS,KAAK,QAAQ,QAAQ,SAAS,CAAC;CAChE,IAAI,KAAK,QAAQ,SAAS,OAAO,QAAQ,cAAc,WAAW,YAAY,SAC5E,MAAM,IAAI,UAAU,8CAA8C;CACpE,MAAM,SAAS,gBAAgB,QAAQ,WAAW,KAAK,QAAQ,SAAS,CAAC;CACzE,MAAM,UAAU,cAAc,YAAY,GAAG;CAC7C,MAAM,KAAK;CACX,MAAM,OAAO,MAAM,aAAa;EAC9B,GAAG;EACH,SAAS;EACT,MAAM;EACN,QAAQ;GACN,wBAAwB,KAAK,UAAU,aAAa;GACpD,qBAAqB;GACrB,uBAAuB;GACvB,yCAAyC;EAC3C;EACA,cAAc;GAAE,aAAa;GAAM,SAAS,CAAC;EAAE;EAC/C,KAAK;GAAE,YAAY;GAAM,cAAc;IAAE,aAAa;IAAM,SAAS,CAAC;GAAE;EAAE;EAC1E,QAAQ;GACN,MAAM;GACN,MAAM,QAAQ,QAAQ;GACtB,YAAY;GACZ,OAAO,EAAE,kBAAkB;IAAE,oBAAoB;IAAK,cAAc;GAAG,EAAE;GACzE,IAAI,EACF,OAAO,CACL,uBAAuB,KAAK,QAAQ,SAAS,CAAC,GAC9C,uBAAuB,KAAK,QAAQ,QAAQ,QAAQ,8BAA8B,CAAC,CAAC,CACtF,EACF;EACF;EACA,SAAS,CACP,GAAI,OAAO,WAAW,CAAC,GACvB;GACE,MAAM;GACN,YAAY,WAAY,OAAO,WAAW,EAAE,IAAI,KAAK,WAAW,KAAA;GAChE,KAAK,QAAQ;IACX,IAAI,WAAW,KAAK,GAAG,SACrB,OAAO,YAAY,QAAQ,WAAW,WAAW,QAAQ;IAC3D,IAAI,WAAW,KAAK,GAAG,SACrB,OAAO,YAAY,QAAQ,WAAW,WAAW,QAAQ;GAE7D;EACF,CACF;CACF,CAAC;CACD,IAAI;EACF,MAAM,aAAa,OAAO,UAAqB;GAC7C,MAAM,SAAkB,MAAM,KAAK,cAAc,GAAG,GAAG,OAAO,CAAC,CAAC,OAAO,UAAmB;IACxF,IAAI,iBAAiB,OAAO;KAC1B,KAAK,iBAAiB,KAAK;KAC3B,KAAK,OAAO,OAAO,MAAM,MAAM,SAAS,MAAM,OAAO;IACvD;IACA,MAAM;GACR,CAAC;GACD,IACE,WAAW,QACX,OAAO,WAAW,YAClB,EAAE,gBAAgB,WAClB,OAAO,OAAO,eAAe,YAE7B,MAAM,IAAI,MAAM,2BAA2B;GAC7C,MAAM,OAAgB,MAAM,OAAO,WAAW,KAAK;GACnD,IAAI,OAAO,SAAS,UAAU,MAAM,IAAI,MAAM,uBAAuB;GACrE,OAAO;EACT;EACA,MAAM,WAAW;GACf;GACA,cAAc,kCAAkC,GAAG;GACnD,YAAY;EACd;EACA,MAAM,QACJ,QAAQ,cAAc,WAClB,kBAAkB,QAAQ,cAAc,QAAQ,IAChD,qBAAqB,QAAQ,cAAc,QAAQ;EACzD,MAAM,WAAW,MAAM,0BAA0B;GAC/C,OAAO;GACP,GAAG,QAAQ;GACX;EACF,CAAC;EACD,KAAK,YAAY,KAAK,SAAS,UAAU,SAAS;GAChD,IAAI,QAAQ,KAAK,WAAW,IAAI,KAAK,QAAQ,KAAK,WAAW,gBAAgB,GAAG;IAC9E,KAAK;IACL;GACF;GACA,SAAc,SAAS,QAAQ;EACjC,CAAC;EACD,MAAM,KAAK,OAAO;EAClB,MAAM,UAAU,KAAK,YAAY,QAAQ;EACzC,IAAI,CAAC,WAAW,OAAO,YAAY,UACjC,MAAM,IAAI,MAAM,sCAAsC;EACxD,OAAO;GAAE,KAAK,oBAAoB,QAAQ;GAAQ,aAAa,KAAK,MAAM;EAAE;CAC9E,SAAS,OAAO;EACd,MAAM,KAAK,MAAM;EACjB,MAAM;CACR;AACF"}
@@ -0,0 +1,18 @@
1
+ import { t as Framework } from "./compiler-DwxVCNyK.mjs";
2
+ //#region src/index.d.ts
3
+ export interface BuildFrameworkThemeOptions {
4
+ readonly framework: Framework;
5
+ /** A wrapper component accepting the public model prop. Source code is trusted executable input. */
6
+ readonly component: string;
7
+ /** A dedicated compiler-owned directory. Existing unowned directories are rejected. */
8
+ readonly output: string;
9
+ }
10
+ export interface BuiltFrameworkTheme {
11
+ readonly themeUrl: string;
12
+ readonly output: string;
13
+ }
14
+ /** Publishes a paired build only after both targets compile. Failed builds retain the previous output. */
15
+ export declare function buildFrameworkTheme(options: BuildFrameworkThemeOptions): Promise<BuiltFrameworkTheme>;
16
+ //#endregion
17
+ export type { Framework };
18
+ //# sourceMappingURL=index.d.mts.map
package/dist/index.mjs ADDED
@@ -0,0 +1,181 @@
1
+ import { n as frameworkConfig, r as validateFramework, t as entrySource } from "./compiler-C0rEfJCH.mjs";
2
+ import { build } from "vite";
3
+ import { lstat, mkdir, mkdtemp, readFile, readdir, realpath, rename, rm, writeFile } from "node:fs/promises";
4
+ import path from "node:path";
5
+ import { pathToFileURL } from "node:url";
6
+ import { createHash } from "node:crypto";
7
+ //#region src/notices.ts
8
+ /** Preserve notices for dependencies bundled into either runtime. */
9
+ function noticeCollector(notices) {
10
+ return {
11
+ name: "dirwell-bundle-notices",
12
+ async generateBundle() {
13
+ for (const id of this.getModuleIds()) {
14
+ if (!id.includes("node_modules") || id.startsWith("\0")) continue;
15
+ let directory = path.dirname(id.split("?")[0]);
16
+ while (directory !== path.dirname(directory)) try {
17
+ const manifest = JSON.parse(await readFile(path.join(directory, "package.json"), "utf8"));
18
+ if (manifest === null || typeof manifest !== "object") throw new Error("Invalid bundled package manifest");
19
+ if (!("name" in manifest) || typeof manifest.name !== "string") {
20
+ directory = path.dirname(directory);
21
+ continue;
22
+ }
23
+ const version = "version" in manifest ? String(manifest.version) : "unknown";
24
+ if (!notices.has(manifest.name)) {
25
+ const filename = (await readdir(directory)).find((name) => /^licen[sc]e(?:\.|$)/i.test(name));
26
+ if (!filename) throw new Error(`Bundled package ${manifest.name} has no license file`);
27
+ notices.set(manifest.name, `${manifest.name}@${version}\n${await readFile(path.join(directory, filename), "utf8")}`);
28
+ }
29
+ break;
30
+ } catch (error) {
31
+ if (!(error instanceof Error && "code" in error && error.code === "ENOENT")) throw error;
32
+ directory = path.dirname(directory);
33
+ }
34
+ }
35
+ }
36
+ };
37
+ }
38
+ //#endregion
39
+ //#region src/index.ts
40
+ const marker = ".dirwell-theme-authoring.json";
41
+ function contains(parent, child) {
42
+ const relative = path.relative(parent, child);
43
+ return relative === "" || !relative.startsWith(`..${path.sep}`) && relative !== ".." && !path.isAbsolute(relative);
44
+ }
45
+ /** Publishes a paired build only after both targets compile. Failed builds retain the previous output. */
46
+ async function buildFrameworkTheme(options) {
47
+ validateFramework(options.framework);
48
+ const component = await realpath(path.resolve(options.component));
49
+ if (path.extname(component) !== (options.framework === "svelte" ? ".svelte" : ".vue")) throw new TypeError("Component extension does not match framework");
50
+ const requested = path.resolve(options.output);
51
+ await mkdir(path.dirname(requested), { recursive: true });
52
+ const output = path.join(await realpath(path.dirname(requested)), path.basename(requested));
53
+ if (contains(output, component) || output === path.parse(output).root) throw new Error("Output cannot contain the component source");
54
+ const lock = `${output}.dirwell-lock`;
55
+ await mkdir(lock);
56
+ const priorNodeEnv = process.env.NODE_ENV;
57
+ let stage;
58
+ let backup;
59
+ try {
60
+ let exists = false;
61
+ try {
62
+ const stat = await lstat(output);
63
+ if (stat.isSymbolicLink() || !stat.isDirectory()) throw new Error("Output must be a compiler-owned directory");
64
+ const ownership = JSON.parse(await readFile(path.join(output, marker), "utf8"));
65
+ if (ownership.version !== 1 || ownership.component !== component) throw new Error("Output belongs to a different component");
66
+ exists = true;
67
+ } catch (error) {
68
+ if (!(error instanceof Error && "code" in error && error.code === "ENOENT")) throw error;
69
+ try {
70
+ await lstat(output);
71
+ throw new Error("Refusing to replace unowned output");
72
+ } catch (missing) {
73
+ if (!(missing instanceof Error && "code" in missing && missing.code === "ENOENT")) throw missing;
74
+ }
75
+ }
76
+ stage = await mkdtemp(`${output}.dirwell-stage-`);
77
+ const config = frameworkConfig(options.framework, path.dirname(component));
78
+ const notices = /* @__PURE__ */ new Map();
79
+ for (const target of ["server", "client"]) {
80
+ const id = path.join(path.dirname(component), `.dirwell-virtual-${target}.mjs`);
81
+ await build({
82
+ ...config,
83
+ define: {
84
+ "process.env.NODE_ENV": JSON.stringify("production"),
85
+ __VUE_OPTIONS_API__: "true",
86
+ __VUE_PROD_DEVTOOLS__: "false",
87
+ __VUE_PROD_HYDRATION_MISMATCH_DETAILS__: "false"
88
+ },
89
+ plugins: [
90
+ ...config.plugins ?? [],
91
+ noticeCollector(notices),
92
+ {
93
+ name: "dirwell-paired-entry",
94
+ resolveId: (source) => source === id ? `\0${id}` : void 0,
95
+ load: (source) => source === `\0${id}` ? entrySource(options.framework, component, target) : void 0
96
+ }
97
+ ],
98
+ build: {
99
+ outDir: stage,
100
+ emptyOutDir: false,
101
+ ssr: target === "server",
102
+ minify: target === "client",
103
+ target: target === "client" ? "es2020" : "node22",
104
+ lib: {
105
+ entry: id,
106
+ formats: ["es"],
107
+ cssFileName: "component",
108
+ fileName: () => `${target}.mjs`
109
+ },
110
+ cssCodeSplit: false,
111
+ rollupOptions: { output: {
112
+ inlineDynamicImports: true,
113
+ entryFileNames: `${target}.mjs`
114
+ } }
115
+ }
116
+ });
117
+ }
118
+ const notice = [...notices].sort(([a], [b]) => a.localeCompare(b)).map(([, text]) => text).join("\n\n");
119
+ await writeFile(path.join(stage, "THIRD_PARTY_NOTICES.txt"), notice);
120
+ for (const filename of ["server.mjs", "client.mjs"]) {
121
+ const code = await readFile(path.join(stage, filename), "utf8");
122
+ await writeFile(path.join(stage, filename), `/*!\n${notice.replaceAll("*/", "* /")}\n*/\n${code}`);
123
+ }
124
+ const server = await readFile(path.join(stage, "server.mjs"), "utf8");
125
+ const hash = createHash("sha256").update(server).digest("hex").slice(0, 16);
126
+ await rename(path.join(stage, "server.mjs"), path.join(stage, `server-${hash}.mjs`));
127
+ const styles = (await Promise.all((await readdir(stage)).filter((name) => name.endsWith(".css")).map((name) => readFile(path.join(stage, name), "utf8")))).join("\n");
128
+ await writeFile(path.join(stage, "styles.css"), styles);
129
+ const theme = options.framework === "svelte" ? "ledger" : "crosswave";
130
+ const factory = options.framework === "svelte" ? "createLedgerTheme" : "createCrosswaveTheme";
131
+ await writeFile(path.join(stage, "theme.mjs"), `import {readFile} from 'node:fs/promises';
132
+ import {${factory}} from '@dirwell/theme-${theme}';
133
+ import {renderBody} from './server-${hash}.mjs';
134
+ const clientScript=await readFile(new URL('./client.mjs',import.meta.url),'utf8');
135
+ const styles=await readFile(new URL('./styles.css',import.meta.url),'utf8');
136
+ export const createTheme=(options={})=>${factory}(options,{renderBody,clientScript,styles,clientType:'module'});
137
+ `);
138
+ await writeFile(path.join(stage, "theme.d.mts"), `import type {ExplorerTheme} from '@dirwell/core';\nimport type {${options.framework === "svelte" ? "Ledger" : "Crosswave"}ThemeOptions} from '@dirwell/theme-${theme}';\nexport declare const createTheme: (options?: ${options.framework === "svelte" ? "Ledger" : "Crosswave"}ThemeOptions) => ExplorerTheme;\n`);
139
+ await writeFile(path.join(stage, marker), JSON.stringify({
140
+ version: 1,
141
+ component,
142
+ framework: options.framework
143
+ }));
144
+ if (exists) {
145
+ backup = `${stage}-previous`;
146
+ await rename(output, backup);
147
+ }
148
+ try {
149
+ await rename(stage, output);
150
+ stage = void 0;
151
+ } catch (error) {
152
+ if (backup) {
153
+ await rename(backup, output);
154
+ backup = void 0;
155
+ }
156
+ throw error;
157
+ }
158
+ if (backup) {
159
+ await rm(backup, { recursive: true });
160
+ backup = void 0;
161
+ }
162
+ return {
163
+ themeUrl: pathToFileURL(path.join(output, "theme.mjs")).href,
164
+ output
165
+ };
166
+ } finally {
167
+ if (priorNodeEnv === void 0 && process.env.NODE_ENV === "production") delete process.env.NODE_ENV;
168
+ if (stage) await rm(stage, {
169
+ recursive: true,
170
+ force: true
171
+ });
172
+ await rm(lock, {
173
+ recursive: true,
174
+ force: true
175
+ });
176
+ }
177
+ }
178
+ //#endregion
179
+ export { buildFrameworkTheme };
180
+
181
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/notices.ts","../src/index.ts"],"sourcesContent":["import { readFile, readdir } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport type { Plugin } from \"vite\";\n/** Preserve notices for dependencies bundled into either runtime. */\nexport function noticeCollector(notices: Map<string, string>): Plugin {\n return {\n name: \"dirwell-bundle-notices\",\n async generateBundle() {\n for (const id of this.getModuleIds()) {\n if (!id.includes(\"node_modules\") || id.startsWith(\"\\0\")) continue;\n let directory = path.dirname(id.split(\"?\")[0]!);\n while (directory !== path.dirname(directory)) {\n try {\n const manifest: unknown = JSON.parse(\n await readFile(path.join(directory, \"package.json\"), \"utf8\"),\n );\n if (manifest === null || typeof manifest !== \"object\")\n throw new Error(\"Invalid bundled package manifest\");\n if (!(\"name\" in manifest) || typeof manifest.name !== \"string\") {\n directory = path.dirname(directory);\n continue;\n }\n const version = \"version\" in manifest ? String(manifest.version) : \"unknown\";\n if (!notices.has(manifest.name)) {\n const filename = (await readdir(directory)).find((name) =>\n /^licen[sc]e(?:\\.|$)/i.test(name),\n );\n if (!filename)\n throw new Error(`Bundled package ${manifest.name} has no license file`);\n notices.set(\n manifest.name,\n `${manifest.name}@${version}\\n${await readFile(path.join(directory, filename), \"utf8\")}`,\n );\n }\n break;\n } catch (error) {\n if (!(error instanceof Error && \"code\" in error && error.code === \"ENOENT\"))\n throw error;\n directory = path.dirname(directory);\n }\n }\n }\n },\n };\n}\n","import { build } from \"vite\";\nimport {\n mkdir,\n mkdtemp,\n readFile,\n writeFile,\n readdir,\n realpath,\n rename,\n rm,\n lstat,\n} from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { createHash } from \"node:crypto\";\nimport { frameworkConfig, entrySource, validateFramework, type Framework } from \"./compiler.ts\";\nimport { noticeCollector } from \"./notices.ts\";\nexport type { Framework } from \"./compiler.ts\";\nexport interface BuildFrameworkThemeOptions {\n readonly framework: Framework;\n /** A wrapper component accepting the public model prop. Source code is trusted executable input. */\n readonly component: string;\n /** A dedicated compiler-owned directory. Existing unowned directories are rejected. */\n readonly output: string;\n}\nexport interface BuiltFrameworkTheme {\n readonly themeUrl: string;\n readonly output: string;\n}\nconst marker = \".dirwell-theme-authoring.json\";\nfunction contains(parent: string, child: string) {\n const relative = path.relative(parent, child);\n return (\n relative === \"\" ||\n (!relative.startsWith(`..${path.sep}`) && relative !== \"..\" && !path.isAbsolute(relative))\n );\n}\n/** Publishes a paired build only after both targets compile. Failed builds retain the previous output. */\nexport async function buildFrameworkTheme(\n options: BuildFrameworkThemeOptions,\n): Promise<BuiltFrameworkTheme> {\n validateFramework(options.framework);\n const component = await realpath(path.resolve(options.component));\n if (path.extname(component) !== (options.framework === \"svelte\" ? \".svelte\" : \".vue\"))\n throw new TypeError(\"Component extension does not match framework\");\n const requested = path.resolve(options.output);\n await mkdir(path.dirname(requested), { recursive: true });\n const output = path.join(await realpath(path.dirname(requested)), path.basename(requested));\n if (contains(output, component) || output === path.parse(output).root)\n throw new Error(\"Output cannot contain the component source\");\n const lock = `${output}.dirwell-lock`;\n await mkdir(lock);\n const priorNodeEnv = process.env.NODE_ENV;\n let stage: string | undefined;\n let backup: string | undefined;\n try {\n let exists = false;\n try {\n const stat = await lstat(output);\n if (stat.isSymbolicLink() || !stat.isDirectory())\n throw new Error(\"Output must be a compiler-owned directory\");\n const ownership = JSON.parse(await readFile(path.join(output, marker), \"utf8\"));\n if (ownership.version !== 1 || ownership.component !== component)\n throw new Error(\"Output belongs to a different component\");\n exists = true;\n } catch (error) {\n if (!(error instanceof Error && \"code\" in error && error.code === \"ENOENT\")) throw error;\n // A missing marker inside an existing directory is not ownership.\n try {\n await lstat(output);\n throw new Error(\"Refusing to replace unowned output\");\n } catch (missing) {\n if (!(missing instanceof Error && \"code\" in missing && missing.code === \"ENOENT\"))\n throw missing;\n }\n }\n stage = await mkdtemp(`${output}.dirwell-stage-`);\n const config = frameworkConfig(options.framework, path.dirname(component));\n const notices = new Map<string, string>();\n for (const target of [\"server\", \"client\"] as const) {\n const id = path.join(path.dirname(component), `.dirwell-virtual-${target}.mjs`);\n await build({\n ...config,\n define: {\n \"process.env.NODE_ENV\": JSON.stringify(\"production\"),\n __VUE_OPTIONS_API__: \"true\",\n __VUE_PROD_DEVTOOLS__: \"false\",\n __VUE_PROD_HYDRATION_MISMATCH_DETAILS__: \"false\",\n },\n plugins: [\n ...(config.plugins ?? []),\n noticeCollector(notices),\n {\n name: \"dirwell-paired-entry\",\n resolveId: (source) => (source === id ? `\\0${id}` : undefined),\n load: (source) =>\n source === `\\0${id}` ? entrySource(options.framework, component, target) : undefined,\n },\n ],\n build: {\n outDir: stage,\n emptyOutDir: false,\n ssr: target === \"server\",\n minify: target === \"client\",\n target: target === \"client\" ? \"es2020\" : \"node22\",\n lib: {\n entry: id,\n formats: [\"es\"],\n cssFileName: \"component\",\n fileName: () => `${target}.mjs`,\n },\n cssCodeSplit: false,\n rollupOptions: {\n output: { inlineDynamicImports: true, entryFileNames: `${target}.mjs` },\n },\n },\n });\n }\n const notice = [...notices]\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([, text]) => text)\n .join(\"\\n\\n\");\n await writeFile(path.join(stage, \"THIRD_PARTY_NOTICES.txt\"), notice);\n for (const filename of [\"server.mjs\", \"client.mjs\"]) {\n const code = await readFile(path.join(stage, filename), \"utf8\");\n await writeFile(\n path.join(stage, filename),\n `/*!\\n${notice.replaceAll(\"*/\", \"* /\")}\\n*/\\n${code}`,\n );\n }\n const server = await readFile(path.join(stage, \"server.mjs\"), \"utf8\");\n const hash = createHash(\"sha256\").update(server).digest(\"hex\").slice(0, 16);\n await rename(path.join(stage, \"server.mjs\"), path.join(stage, `server-${hash}.mjs`));\n const styles = (\n await Promise.all(\n (await readdir(stage))\n .filter((name) => name.endsWith(\".css\"))\n .map((name) => readFile(path.join(stage!, name), \"utf8\")),\n )\n ).join(\"\\n\");\n await writeFile(path.join(stage, \"styles.css\"), styles);\n const theme = options.framework === \"svelte\" ? \"ledger\" : \"crosswave\";\n const factory = options.framework === \"svelte\" ? \"createLedgerTheme\" : \"createCrosswaveTheme\";\n await writeFile(\n path.join(stage, \"theme.mjs\"),\n `import {readFile} from 'node:fs/promises';\nimport {${factory}} from '@dirwell/theme-${theme}';\nimport {renderBody} from './server-${hash}.mjs';\nconst clientScript=await readFile(new URL('./client.mjs',import.meta.url),'utf8');\nconst styles=await readFile(new URL('./styles.css',import.meta.url),'utf8');\nexport const createTheme=(options={})=>${factory}(options,{renderBody,clientScript,styles,clientType:'module'});\n`,\n );\n await writeFile(\n path.join(stage, \"theme.d.mts\"),\n `import type {ExplorerTheme} from '@dirwell/core';\\nimport type {${options.framework === \"svelte\" ? \"Ledger\" : \"Crosswave\"}ThemeOptions} from '@dirwell/theme-${theme}';\\nexport declare const createTheme: (options?: ${options.framework === \"svelte\" ? \"Ledger\" : \"Crosswave\"}ThemeOptions) => ExplorerTheme;\\n`,\n );\n await writeFile(\n path.join(stage, marker),\n JSON.stringify({ version: 1, component, framework: options.framework }),\n );\n if (exists) {\n backup = `${stage}-previous`;\n await rename(output, backup);\n }\n try {\n await rename(stage, output);\n stage = undefined;\n } catch (error) {\n if (backup) {\n await rename(backup, output);\n backup = undefined;\n }\n throw error;\n }\n if (backup) {\n await rm(backup, { recursive: true });\n backup = undefined;\n }\n return { themeUrl: pathToFileURL(path.join(output, \"theme.mjs\")).href, output };\n } finally {\n if (priorNodeEnv === undefined && process.env.NODE_ENV === \"production\")\n delete process.env.NODE_ENV;\n if (stage) await rm(stage, { recursive: true, force: true });\n await rm(lock, { recursive: true, force: true });\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,50 @@
1
1
  {
2
2
  "name": "@dirwell/theme-authoring",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.0.1-alpha.1",
4
+ "description": "Paired Svelte/Vue theme compilation and native development HMR.",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/vp-tw/dirwell.git",
9
+ "directory": "packages/theme-authoring"
10
+ },
11
+ "files": [
12
+ "dist",
13
+ "README.md",
14
+ "LICENSE"
15
+ ],
16
+ "type": "module",
17
+ "exports": {
18
+ ".": {
19
+ "types": "./dist/index.d.mts",
20
+ "import": "./dist/index.mjs"
21
+ },
22
+ "./dev": {
23
+ "types": "./dist/dev.d.mts",
24
+ "import": "./dist/dev.mjs"
25
+ }
26
+ },
27
+ "publishConfig": {
28
+ "access": "public",
29
+ "provenance": true,
30
+ "tag": "alpha"
31
+ },
32
+ "dependencies": {
33
+ "@dirwell/core": "0.0.1-alpha.1",
34
+ "@dirwell/server": "0.0.1-alpha.1",
35
+ "@dirwell/theme-crosswave": "0.0.1-alpha.1",
36
+ "@dirwell/theme-ledger": "0.0.1-alpha.1",
37
+ "@sveltejs/vite-plugin-svelte": "7.3.1",
38
+ "@vitejs/plugin-vue": "6.0.10",
39
+ "@vue/compiler-sfc": "3.5.43",
40
+ "svelte": "5.57.2",
41
+ "vite": "8.3.0",
42
+ "vue": "3.5.43"
43
+ },
44
+ "engines": {
45
+ "node": ">=22.12.0"
46
+ },
47
+ "scripts": {
48
+ "build": "vp pack"
49
+ }
6
50
  }