@samva/vite 0.8.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 +62 -58
- package/README.md +140 -94
- package/dist/build.d.ts +47 -42
- package/dist/build.js +88 -213
- 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 -1054
- package/dist/project-CiUuzKsC.d.ts +271 -0
- package/dist/project-Cp8qb3s0.js +774 -0
- package/dist/project.d.ts +2 -175
- package/dist/project.js +2 -2
- package/dist/render.d.ts +54 -0
- package/dist/render.js +98 -0
- package/package.json +11 -12
- package/dist/discovery-D2eR-rzi.js +0 -35
- package/dist/editor/assets/index-6Qlbkdhz.js +0 -36
- package/dist/editor/assets/index-Ed_V5wEW.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/project-RON2Ei7d.js +0 -603
package/dist/build.js
CHANGED
|
@@ -1,228 +1,103 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import { COMPILER_VERSION, emitModulePreview } from "./emit.js";
|
|
4
|
-
import { createEmailRenderer } from "./email.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
|
-
|
|
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
|
|
40
|
+
};
|
|
45
41
|
});
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
await emailRenderer.close();
|
|
60
|
-
emailRenderer = void 0;
|
|
61
|
-
}
|
|
62
|
-
previousTheme = themeCss;
|
|
63
|
-
previousBrand = brand?.digest;
|
|
64
|
-
emailRenderer?.invalidate();
|
|
65
|
-
const entries = [];
|
|
66
|
-
const emailIds = /* @__PURE__ */ new Map();
|
|
67
|
-
for (const file of files) {
|
|
68
|
-
const evaluated = await server.ssrLoadModule(file.path).then((moduleExports) => ({
|
|
69
|
-
ok: true,
|
|
70
|
-
moduleExports
|
|
71
|
-
}), (cause) => ({
|
|
72
|
-
ok: false,
|
|
73
|
-
cause
|
|
74
|
-
}));
|
|
75
|
-
if (!evaluated.ok) {
|
|
76
|
-
entries.push({
|
|
77
|
-
id: file.id,
|
|
78
|
-
kind: file.kind,
|
|
79
|
-
preview: emailFailure(evaluated.cause)
|
|
80
|
-
});
|
|
81
|
-
continue;
|
|
82
|
-
}
|
|
83
|
-
const definition = evaluated.moduleExports.default;
|
|
84
|
-
if (definition === void 0) continue;
|
|
85
|
-
if (isTemplateDefinition(definition)) {
|
|
86
|
-
const entryPath = relative(root, file.path).split(sep).join("/");
|
|
87
|
-
const previous = emailIds.get(definition.id);
|
|
88
|
-
if (previous !== void 0) {
|
|
89
|
-
entries.push({
|
|
90
|
-
id: file.id,
|
|
91
|
-
kind: file.kind,
|
|
92
|
-
preview: emailFailure(`Duplicate template id ${definition.id}: ${previous} and ${file.id}`, "duplicate-template-id")
|
|
93
|
-
});
|
|
94
|
-
continue;
|
|
95
|
-
}
|
|
96
|
-
emailIds.set(definition.id, file.id);
|
|
97
|
-
const preview = await (async () => {
|
|
98
|
-
emailRenderer ??= await createEmailRenderer({
|
|
99
|
-
assetBase: options.assetBase ?? "",
|
|
100
|
-
...themeCss === void 0 || themeFile === void 0 ? {} : { tailwind: {
|
|
101
|
-
css: themeCss,
|
|
102
|
-
cssPath: themeFile
|
|
103
|
-
} },
|
|
104
|
-
...brand === void 0 ? {} : { brand }
|
|
105
|
-
});
|
|
106
|
-
const loaded = await emailRenderer.load(sourceFiles, entryPath, entryPath);
|
|
107
|
-
const template = loaded.template;
|
|
108
|
-
if (!isTemplateDefinition(template)) return emailFailure(`${file.id} does not export defineEmail`);
|
|
109
|
-
const fixtures = renderEmailFixtures({
|
|
110
|
-
id: template.id,
|
|
111
|
-
entryPath,
|
|
112
|
-
definition: template,
|
|
113
|
-
loaded
|
|
114
|
-
}).map((fixture) => ({
|
|
115
|
-
name: fixture.fixture,
|
|
116
|
-
input: template.fixtures[fixture.fixture],
|
|
117
|
-
ok: fixture.rendered !== void 0 && !fixture.diagnostics.some((finding) => finding.severity === "error"),
|
|
118
|
-
...fixture.rendered === void 0 ? {} : {
|
|
119
|
-
subject: fixture.rendered.subject,
|
|
120
|
-
preheader: fixture.rendered.preheader,
|
|
121
|
-
html: fixture.rendered.html,
|
|
122
|
-
text: fixture.rendered.text
|
|
123
|
-
},
|
|
124
|
-
diagnostics: fixture.diagnostics
|
|
125
|
-
}));
|
|
126
|
-
const diagnostics = [...brandFindings, ...loaded.compiled.diagnostics];
|
|
127
|
-
if (fixtures.length === 0) diagnostics.push(emailFinding("missing-fixtures", `${template.id} must declare at least one fixture for check and build.`));
|
|
128
|
-
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."));
|
|
129
|
-
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."));
|
|
130
|
-
const ok = !diagnostics.some((finding) => finding.severity === "error") && fixtures.every((fixture) => fixture.ok);
|
|
131
|
-
return {
|
|
132
|
-
kind: "email",
|
|
133
|
-
ok,
|
|
134
|
-
compilerVersion: COMPILER_VERSION,
|
|
135
|
-
diagnostics,
|
|
136
|
-
templates: [{
|
|
137
|
-
name: template.id,
|
|
138
|
-
channel: "email",
|
|
139
|
-
ok,
|
|
140
|
-
source: String(sourceFiles[entryPath]),
|
|
141
|
-
inputSchema: template.schema.schema,
|
|
142
|
-
compiled: loaded.compiled,
|
|
143
|
-
fixtures
|
|
144
|
-
}]
|
|
145
|
-
};
|
|
146
|
-
})().catch((cause) => {
|
|
147
|
-
const failure = emailFailure(cause);
|
|
148
|
-
return {
|
|
149
|
-
...failure,
|
|
150
|
-
diagnostics: [...brandFindings, ...failure.diagnostics]
|
|
151
|
-
};
|
|
152
|
-
});
|
|
153
|
-
entries.push({
|
|
154
|
-
id: file.id,
|
|
155
|
-
kind: file.kind,
|
|
156
|
-
preview
|
|
157
|
-
});
|
|
158
|
-
continue;
|
|
159
|
-
}
|
|
160
|
-
const preview = emitModulePreview(parse(file.id).name, evaluated.moduleExports);
|
|
161
|
-
if (preview.templates.length === 0) continue;
|
|
162
|
-
if (preview.templates.some((template) => /^\s*<Email(?:\s|>)/.test(template.source ?? ""))) entries.push({
|
|
163
|
-
id: file.id,
|
|
164
|
-
kind: file.kind,
|
|
165
|
-
preview: emailFailure(`${file.id}: email templates must default-export defineEmail with an input schema and fixtures.`, "email-requires-object-template")
|
|
166
|
-
});
|
|
167
|
-
else entries.push({
|
|
168
|
-
id: file.id,
|
|
169
|
-
kind: file.kind,
|
|
170
|
-
preview: {
|
|
171
|
-
...preview,
|
|
172
|
-
kind: "structured"
|
|
173
|
-
}
|
|
174
|
-
});
|
|
175
|
-
}
|
|
176
|
-
return { files: entries };
|
|
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
|
|
177
55
|
};
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
"
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
const onChange = (event, path) => {
|
|
185
|
-
if (fileEvents.has(event)) listener(path);
|
|
186
|
-
};
|
|
187
|
-
server.watcher.on("all", onChange);
|
|
188
|
-
return () => server.watcher.off("all", onChange);
|
|
56
|
+
return {
|
|
57
|
+
id: entry.entryPath,
|
|
58
|
+
kind: "tsx",
|
|
59
|
+
ok,
|
|
60
|
+
template,
|
|
61
|
+
diagnostics
|
|
189
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);
|
|
73
|
+
return {
|
|
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;
|
|
190
90
|
return {
|
|
191
|
-
build,
|
|
192
|
-
subscribe,
|
|
91
|
+
build: async () => summarize(await session.load(), options.assetBase),
|
|
92
|
+
subscribe: session.subscribe,
|
|
193
93
|
close: async () => {
|
|
194
94
|
if (closed) return;
|
|
195
95
|
closed = true;
|
|
196
|
-
|
|
197
|
-
await emailRenderer?.close();
|
|
198
|
-
} finally {
|
|
199
|
-
await server.close();
|
|
200
|
-
}
|
|
96
|
+
await session.close();
|
|
201
97
|
}
|
|
202
98
|
};
|
|
203
99
|
};
|
|
204
|
-
const createEvaluator = async (root) => {
|
|
205
|
-
const vite = await import("vite").catch(() => void 0);
|
|
206
|
-
if (vite === void 0) throw new Error("`vite` is required for template authoring. Install it as a dev dependency.");
|
|
207
|
-
return vite.createServer({
|
|
208
|
-
root,
|
|
209
|
-
configFile: false,
|
|
210
|
-
logLevel: "silent",
|
|
211
|
-
server: {
|
|
212
|
-
middlewareMode: true,
|
|
213
|
-
hmr: false
|
|
214
|
-
},
|
|
215
|
-
oxc: { jsx: {
|
|
216
|
-
runtime: "automatic",
|
|
217
|
-
importSource: "@samva/markup"
|
|
218
|
-
} },
|
|
219
|
-
optimizeDeps: {
|
|
220
|
-
noDiscovery: true,
|
|
221
|
-
include: []
|
|
222
|
-
}
|
|
223
|
-
});
|
|
224
|
-
};
|
|
225
100
|
//#endregion
|
|
226
|
-
export { buildTemplates, createTemplateBuildSession };
|
|
101
|
+
export { COMPILER, buildTemplates, createTemplateBuildSession };
|
|
227
102
|
|
|
228
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
|