@samva/vite 0.7.0 → 0.9.0
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/AGENTS.md +63 -54
- package/README.md +151 -79
- package/dist/build.d.ts +47 -42
- package/dist/build.js +89 -191
- package/dist/dev.d.ts +22 -0
- package/dist/dev.js +43 -0
- package/dist/editor/assets/index-BtfwuWcY.js +32 -0
- package/dist/editor/assets/index-DIuS_EZu.css +2 -0
- package/dist/editor/index.html +2 -2
- package/dist/editor-Cow7zZ_9.js +780 -0
- package/dist/index.d.ts +11 -4
- package/dist/index.js +1 -1015
- package/dist/project-CiUuzKsC.d.ts +271 -0
- package/dist/project-Cp8qb3s0.js +774 -0
- package/dist/project.d.ts +2 -116
- package/dist/project.js +2 -294
- package/dist/render.d.ts +54 -0
- package/dist/render.js +98 -0
- package/package.json +11 -12
- package/dist/editor/assets/index-BeVA-Ur2.js +0 -32
- package/dist/editor/assets/index-Dz2CE64u.css +0 -2
- package/dist/email.d.ts +0 -60
- package/dist/email.js +0 -126
- package/dist/emit.d.ts +0 -99
- package/dist/emit.js +0 -142
- package/dist/theme-CBF6Czjm.js +0 -50
package/dist/build.js
CHANGED
|
@@ -1,205 +1,103 @@
|
|
|
1
|
-
import { n as
|
|
2
|
-
import
|
|
3
|
-
import { createEmailRenderer } from "./email.js";
|
|
4
|
-
import { isTemplateDefinition, readEmailProject, renderEmailFixtures } from "./project.js";
|
|
5
|
-
import { dirname, parse, relative, resolve, sep } from "node:path";
|
|
1
|
+
import { d as emailCompatibility, f as finding, h as renderEmailFixtures, i as loadProject, m as renderChannelFixture, n as createProjectSession, p as hasErrors, s as isDeliverableBase } from "./project-Cp8qb3s0.js";
|
|
2
|
+
import markupPackage from "@samva/markup/package.json" with { type: "json" };
|
|
6
3
|
//#region src/build.ts
|
|
7
|
-
/**
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
code,
|
|
18
|
-
severity: "error",
|
|
19
|
-
message,
|
|
20
|
-
origins: []
|
|
21
|
-
});
|
|
22
|
-
const emailFailure = (cause, code = "email-build-error") => ({
|
|
23
|
-
kind: "email",
|
|
24
|
-
ok: false,
|
|
25
|
-
compilerVersion: COMPILER_VERSION,
|
|
26
|
-
templates: [],
|
|
27
|
-
diagnostics: Array.isArray(cause?.diagnostics) ? cause.diagnostics : [emailFinding(code, cause instanceof Error ? cause.message : String(cause))]
|
|
4
|
+
/** The compiler a build ran, as a publication records it. */
|
|
5
|
+
const COMPILER = `@samva/markup@${markupPackage.version}`;
|
|
6
|
+
/**
|
|
7
|
+
* A build needs an https base for imported assets and reports one finding when it has none, so a
|
|
8
|
+
* template that imports an asset still compiles and renders and the finding is the only noise.
|
|
9
|
+
*/
|
|
10
|
+
const PLACEHOLDER_ORIGIN = "https://assets.invalid";
|
|
11
|
+
const withCompileBase = (options) => ({
|
|
12
|
+
...options,
|
|
13
|
+
assetBase: isDeliverableBase(options.assetBase ?? "") ? options.assetBase : PLACEHOLDER_ORIGIN
|
|
28
14
|
});
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
for (const file of files) {
|
|
55
|
-
const evaluated = await server.ssrLoadModule(file.path).then((moduleExports) => ({
|
|
56
|
-
ok: true,
|
|
57
|
-
moduleExports
|
|
58
|
-
}), (cause) => ({
|
|
59
|
-
ok: false,
|
|
60
|
-
cause
|
|
61
|
-
}));
|
|
62
|
-
if (!evaluated.ok) {
|
|
63
|
-
entries.push({
|
|
64
|
-
id: file.id,
|
|
65
|
-
kind: file.kind,
|
|
66
|
-
preview: emailFailure(evaluated.cause)
|
|
67
|
-
});
|
|
68
|
-
continue;
|
|
69
|
-
}
|
|
70
|
-
const definition = evaluated.moduleExports.default;
|
|
71
|
-
if (definition === void 0) continue;
|
|
72
|
-
if (isTemplateDefinition(definition)) {
|
|
73
|
-
const entryPath = relative(root, file.path).split(sep).join("/");
|
|
74
|
-
const previous = emailIds.get(definition.id);
|
|
75
|
-
if (previous !== void 0) {
|
|
76
|
-
entries.push({
|
|
77
|
-
id: file.id,
|
|
78
|
-
kind: file.kind,
|
|
79
|
-
preview: emailFailure(`Duplicate template id ${definition.id}: ${previous} and ${file.id}`, "duplicate-template-id")
|
|
80
|
-
});
|
|
81
|
-
continue;
|
|
82
|
-
}
|
|
83
|
-
emailIds.set(definition.id, file.id);
|
|
84
|
-
const preview = await (async () => {
|
|
85
|
-
emailRenderer ??= await createEmailRenderer({
|
|
86
|
-
assetBase: options.assetBase ?? "",
|
|
87
|
-
...themeCss === void 0 ? {} : { tailwind: { css: themeCss } }
|
|
88
|
-
});
|
|
89
|
-
const loaded = await emailRenderer.load(sourceFiles, entryPath, entryPath);
|
|
90
|
-
const template = loaded.template;
|
|
91
|
-
if (!isTemplateDefinition(template)) return emailFailure(`${file.id} does not export defineEmail`);
|
|
92
|
-
const fixtures = renderEmailFixtures({
|
|
93
|
-
id: template.id,
|
|
94
|
-
entryPath,
|
|
95
|
-
definition: template,
|
|
96
|
-
loaded
|
|
97
|
-
}).map((fixture) => ({
|
|
98
|
-
name: fixture.fixture,
|
|
99
|
-
input: template.fixtures[fixture.fixture],
|
|
100
|
-
ok: fixture.rendered !== void 0 && !fixture.diagnostics.some((finding) => finding.severity === "error"),
|
|
101
|
-
...fixture.rendered === void 0 ? {} : {
|
|
102
|
-
subject: fixture.rendered.subject,
|
|
103
|
-
preheader: fixture.rendered.preheader,
|
|
104
|
-
html: fixture.rendered.html,
|
|
105
|
-
text: fixture.rendered.text
|
|
106
|
-
},
|
|
107
|
-
diagnostics: fixture.diagnostics
|
|
108
|
-
}));
|
|
109
|
-
const diagnostics = [...loaded.compiled.diagnostics];
|
|
110
|
-
if (fixtures.length === 0) diagnostics.push(emailFinding("missing-fixtures", `${template.id} must declare at least one fixture for check and build.`));
|
|
111
|
-
if (loaded.compiled.assets.length > 0 && options.assetBase === void 0) diagnostics.push(emailFinding("missing-asset-base", "Imported email assets require --asset-base with their HTTPS serving origin."));
|
|
112
|
-
if (loaded.compiled.assets.length > 0 && options.assetBase !== void 0 && !/^https:\/\//i.test(options.assetBase)) diagnostics.push(emailFinding("invalid-asset-base", "Email check and build require an HTTPS --asset-base; relative URLs belong to templates export."));
|
|
113
|
-
const ok = !diagnostics.some((finding) => finding.severity === "error") && fixtures.every((fixture) => fixture.ok);
|
|
114
|
-
return {
|
|
115
|
-
kind: "email",
|
|
116
|
-
ok,
|
|
117
|
-
compilerVersion: COMPILER_VERSION,
|
|
118
|
-
diagnostics,
|
|
119
|
-
templates: [{
|
|
120
|
-
name: template.id,
|
|
121
|
-
channel: "email",
|
|
122
|
-
ok,
|
|
123
|
-
source: String(sourceFiles[entryPath]),
|
|
124
|
-
inputSchema: template.schema.schema,
|
|
125
|
-
compiled: loaded.compiled,
|
|
126
|
-
fixtures
|
|
127
|
-
}]
|
|
128
|
-
};
|
|
129
|
-
})().catch(emailFailure);
|
|
130
|
-
entries.push({
|
|
131
|
-
id: file.id,
|
|
132
|
-
kind: file.kind,
|
|
133
|
-
preview
|
|
134
|
-
});
|
|
135
|
-
continue;
|
|
136
|
-
}
|
|
137
|
-
const preview = emitModulePreview(parse(file.id).name, evaluated.moduleExports);
|
|
138
|
-
if (preview.templates.length === 0) continue;
|
|
139
|
-
if (preview.templates.some((template) => /^\s*<Email(?:\s|>)/.test(template.source ?? ""))) entries.push({
|
|
140
|
-
id: file.id,
|
|
141
|
-
kind: file.kind,
|
|
142
|
-
preview: emailFailure(`${file.id}: email templates must default-export defineEmail with an input schema and fixtures.`, "email-requires-object-template")
|
|
143
|
-
});
|
|
144
|
-
else entries.push({
|
|
145
|
-
id: file.id,
|
|
146
|
-
kind: file.kind,
|
|
147
|
-
preview: {
|
|
148
|
-
...preview,
|
|
149
|
-
kind: "structured"
|
|
150
|
-
}
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
return { files: entries };
|
|
154
|
-
};
|
|
155
|
-
const fileEvents = /* @__PURE__ */ new Set([
|
|
156
|
-
"add",
|
|
157
|
-
"change",
|
|
158
|
-
"unlink"
|
|
159
|
-
]);
|
|
160
|
-
const subscribe = (listener) => {
|
|
161
|
-
const onChange = (event, path) => {
|
|
162
|
-
if (fileEvents.has(event)) listener(path);
|
|
15
|
+
const assetFindings = (entry, assetBase) => {
|
|
16
|
+
if (entry.assets.length === 0) return [];
|
|
17
|
+
if (assetBase === void 0 || assetBase === "") return [finding("missing-asset-base", "error", "Imported email assets require --asset-base with their HTTPS serving origin.")];
|
|
18
|
+
return /^https:\/\//i.test(assetBase) ? [] : [finding("invalid-asset-base", "error", "Email check and build require an HTTPS --asset-base; relative URLs belong to templates export.")];
|
|
19
|
+
};
|
|
20
|
+
const buildEntry = (entry, assetBase) => {
|
|
21
|
+
const email = entry.channels.includes("email") ? renderEmailFixtures(entry) : [];
|
|
22
|
+
const fixtures = Object.entries(entry.fixtures).map(([name, input]) => {
|
|
23
|
+
const rendered = email.find((result) => result.fixture === name);
|
|
24
|
+
const sms = entry.channels.includes("sms") ? renderChannelFixture(entry, "sms", name) : void 0;
|
|
25
|
+
const whatsapp = entry.channels.includes("whatsapp") ? renderChannelFixture(entry, "whatsapp", name) : void 0;
|
|
26
|
+
const diagnostics = [...rendered?.diagnostics ?? [], ...[sms, whatsapp].flatMap((result) => result?.ok === false ? result.diagnostics : [])];
|
|
27
|
+
return {
|
|
28
|
+
name,
|
|
29
|
+
input,
|
|
30
|
+
ok: !hasErrors(diagnostics),
|
|
31
|
+
...rendered?.rendered === void 0 ? {} : { email: {
|
|
32
|
+
subject: rendered.rendered.subject,
|
|
33
|
+
preheader: rendered.rendered.preheader,
|
|
34
|
+
html: rendered.rendered.html,
|
|
35
|
+
text: rendered.rendered.text
|
|
36
|
+
} },
|
|
37
|
+
...sms?.ok === true && sms.channel === "sms" ? { sms: sms.text } : {},
|
|
38
|
+
...whatsapp?.ok === true && whatsapp.channel === "whatsapp" ? { whatsapp: whatsapp.message } : {},
|
|
39
|
+
diagnostics
|
|
163
40
|
};
|
|
164
|
-
|
|
165
|
-
|
|
41
|
+
});
|
|
42
|
+
const compatibility = emailCompatibility(entry, email);
|
|
43
|
+
const diagnostics = [...entry.diagnostics, ...assetFindings(entry, assetBase)];
|
|
44
|
+
const ok = !hasErrors(diagnostics) && !hasErrors(compatibility) && fixtures.every((fixture) => fixture.ok);
|
|
45
|
+
const template = {
|
|
46
|
+
template: entry.id,
|
|
47
|
+
channels: entry.channels,
|
|
48
|
+
ok,
|
|
49
|
+
source: entry.entryPath,
|
|
50
|
+
inputSchema: entry.ir.schema,
|
|
51
|
+
ir: entry.ir,
|
|
52
|
+
fixtures,
|
|
53
|
+
diagnostics,
|
|
54
|
+
compatibility
|
|
55
|
+
};
|
|
56
|
+
return {
|
|
57
|
+
id: entry.entryPath,
|
|
58
|
+
kind: "tsx",
|
|
59
|
+
ok,
|
|
60
|
+
template,
|
|
61
|
+
diagnostics
|
|
166
62
|
};
|
|
63
|
+
};
|
|
64
|
+
const summarize = (project, assetBase) => {
|
|
65
|
+
const built = project.catalog.templates.map((entry) => buildEntry(entry, assetBase));
|
|
66
|
+
const failed = project.catalog.failures.map((entry) => ({
|
|
67
|
+
id: entry.entryPath,
|
|
68
|
+
kind: "tsx",
|
|
69
|
+
ok: false,
|
|
70
|
+
diagnostics: entry.diagnostics
|
|
71
|
+
}));
|
|
72
|
+
const files = [...built, ...failed].sort((left, right) => left.id < right.id ? -1 : 1);
|
|
167
73
|
return {
|
|
168
|
-
|
|
169
|
-
|
|
74
|
+
compilerVersion: COMPILER,
|
|
75
|
+
files,
|
|
76
|
+
diagnostics: project.diagnostics,
|
|
77
|
+
ok: !hasErrors(project.diagnostics) && files.every((file) => file.ok)
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* Compile every template in a project and render every fixture it declares
|
|
82
|
+
* through each of its channels, then check the email fixtures against the
|
|
83
|
+
* client matrix. Nothing is evaluated.
|
|
84
|
+
*/
|
|
85
|
+
const buildTemplates = async (options = {}) => summarize(await loadProject(withCompileBase(options)), options.assetBase);
|
|
86
|
+
/** A watcher and compiler over a project, for `check --watch`. */
|
|
87
|
+
const createTemplateBuildSession = (options = {}) => {
|
|
88
|
+
const session = createProjectSession(withCompileBase(options));
|
|
89
|
+
let closed = false;
|
|
90
|
+
return {
|
|
91
|
+
build: async () => summarize(await session.load(), options.assetBase),
|
|
92
|
+
subscribe: session.subscribe,
|
|
170
93
|
close: async () => {
|
|
171
94
|
if (closed) return;
|
|
172
95
|
closed = true;
|
|
173
|
-
|
|
174
|
-
await emailRenderer?.close();
|
|
175
|
-
} finally {
|
|
176
|
-
await server.close();
|
|
177
|
-
}
|
|
96
|
+
await session.close();
|
|
178
97
|
}
|
|
179
98
|
};
|
|
180
99
|
};
|
|
181
|
-
const createEvaluator = async (root) => {
|
|
182
|
-
const vite = await import("vite").catch(() => void 0);
|
|
183
|
-
if (vite === void 0) throw new Error("`vite` is required for template authoring. Install it as a dev dependency.");
|
|
184
|
-
return vite.createServer({
|
|
185
|
-
root,
|
|
186
|
-
configFile: false,
|
|
187
|
-
logLevel: "silent",
|
|
188
|
-
server: {
|
|
189
|
-
middlewareMode: true,
|
|
190
|
-
hmr: false
|
|
191
|
-
},
|
|
192
|
-
oxc: { jsx: {
|
|
193
|
-
runtime: "automatic",
|
|
194
|
-
importSource: "@samva/markup"
|
|
195
|
-
} },
|
|
196
|
-
optimizeDeps: {
|
|
197
|
-
noDiscovery: true,
|
|
198
|
-
include: []
|
|
199
|
-
}
|
|
200
|
-
});
|
|
201
|
-
};
|
|
202
100
|
//#endregion
|
|
203
|
-
export { buildTemplates, createTemplateBuildSession };
|
|
101
|
+
export { COMPILER, buildTemplates, createTemplateBuildSession };
|
|
204
102
|
|
|
205
103
|
//# sourceMappingURL=build.js.map
|
package/dist/dev.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
//#region src/dev.d.ts
|
|
2
|
+
interface DevServerOptions {
|
|
3
|
+
/** The project root. Default: the working directory. */
|
|
4
|
+
readonly root?: string | undefined;
|
|
5
|
+
/** The templates directory, relative to `root`. Default: `templates/` and `emails/`, whichever exist. */
|
|
6
|
+
readonly dir?: string | undefined;
|
|
7
|
+
readonly port?: number | undefined;
|
|
8
|
+
/** The interface to listen on. Default: loopback only. */
|
|
9
|
+
readonly host?: string | undefined;
|
|
10
|
+
/** Open the editor in a browser once the server is listening. */
|
|
11
|
+
readonly open?: boolean | undefined;
|
|
12
|
+
}
|
|
13
|
+
interface DevServer {
|
|
14
|
+
/** Where the editor is served. */
|
|
15
|
+
readonly url: string;
|
|
16
|
+
readonly close: () => Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
/** Start the local editor for a template project. */
|
|
19
|
+
declare const startDevServer: (options?: DevServerOptions) => Promise<DevServer>;
|
|
20
|
+
//#endregion
|
|
21
|
+
export { DevServer, DevServerOptions, startDevServer };
|
|
22
|
+
//# sourceMappingURL=dev.d.ts.map
|
package/dist/dev.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { t as samvaEditor } from "./editor-Cow7zZ_9.js";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
//#region src/dev.ts
|
|
4
|
+
/** Start the local editor for a template project. */
|
|
5
|
+
const startDevServer = async (options = {}) => {
|
|
6
|
+
const vite = await import("vite").catch(() => void 0);
|
|
7
|
+
if (vite === void 0) throw new Error("`vite` is required for the template dev server. Install it as a dev dependency.");
|
|
8
|
+
const root = resolve(options.root ?? process.cwd());
|
|
9
|
+
const server = await vite.createServer({
|
|
10
|
+
root,
|
|
11
|
+
configFile: false,
|
|
12
|
+
appType: "custom",
|
|
13
|
+
optimizeDeps: {
|
|
14
|
+
noDiscovery: true,
|
|
15
|
+
include: []
|
|
16
|
+
},
|
|
17
|
+
server: {
|
|
18
|
+
...options.port === void 0 ? {} : { port: options.port },
|
|
19
|
+
...options.host === void 0 ? {} : { host: options.host },
|
|
20
|
+
open: options.open === true
|
|
21
|
+
},
|
|
22
|
+
plugins: [samvaEditor({ ...options.dir === void 0 ? {} : { templatesDir: options.dir } })]
|
|
23
|
+
});
|
|
24
|
+
try {
|
|
25
|
+
await server.listen();
|
|
26
|
+
} catch (cause) {
|
|
27
|
+
await server.close();
|
|
28
|
+
throw cause;
|
|
29
|
+
}
|
|
30
|
+
const url = server.resolvedUrls?.local[0] ?? server.resolvedUrls?.network[0];
|
|
31
|
+
if (url === void 0) {
|
|
32
|
+
await server.close();
|
|
33
|
+
throw new Error("The template dev server started without a URL.");
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
url,
|
|
37
|
+
close: () => server.close()
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
//#endregion
|
|
41
|
+
export { startDevServer };
|
|
42
|
+
|
|
43
|
+
//# sourceMappingURL=dev.js.map
|