@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
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import { EmailBrand } from "@samva/markup/compiler";
|
|
2
|
+
import { AssetEntry } from "@samva/markup/email/assets";
|
|
3
|
+
import { EmailDiagnostic } from "@samva/markup/email/diagnostics";
|
|
4
|
+
import { RenderOptions, RenderedIrEmail, RenderedIrWhatsApp, TemplateIr } from "@samva/markup/ir";
|
|
5
|
+
//#region src/layout.d.ts
|
|
6
|
+
/** Directories scanned for templates when no `dir` is configured, in scan order. */
|
|
7
|
+
declare const DEFAULT_TEMPLATE_DIRS: readonly ["templates", "emails"];
|
|
8
|
+
/** The folder for files this package generates, relative to the project root. Gitignore it. */
|
|
9
|
+
declare const SAMVA_DIR = ".samva";
|
|
10
|
+
/** A project's files by root-relative POSIX path: text as strings, imported assets as bytes. */
|
|
11
|
+
type ProjectFiles = Readonly<Record<string, string | Uint8Array>>;
|
|
12
|
+
/**
|
|
13
|
+
* The absolute directories a project's templates live in. A configured `dir`
|
|
14
|
+
* is the only one scanned; otherwise every default directory that exists. A
|
|
15
|
+
* directory outside the root cannot be compiled, because a project's files are
|
|
16
|
+
* addressed relative to its root, so it is refused rather than half-read.
|
|
17
|
+
*/
|
|
18
|
+
declare const templateDirectories: (root: string, dir?: string | undefined) => Promise<readonly string[]>;
|
|
19
|
+
/** Every `.tsx` file beneath the given directories, as root-relative paths, sorted. */
|
|
20
|
+
declare const discoverEntries: (root: string, dirs: readonly string[]) => Promise<readonly string[]>;
|
|
21
|
+
/**
|
|
22
|
+
* Read the project into the map the compiler consumes: scripts, stylesheets
|
|
23
|
+
* and JSON as text, imported assets as bytes. Hidden folders (`.samva`, `.git`)
|
|
24
|
+
* and `node_modules` are not part of a project.
|
|
25
|
+
*/
|
|
26
|
+
declare const readProjectFiles: (root: string) => Promise<ProjectFiles>;
|
|
27
|
+
//#endregion
|
|
28
|
+
//#region src/brand.d.ts
|
|
29
|
+
/** Where brand records are cached, relative to the project root. */
|
|
30
|
+
declare const BRAND_SNAPSHOT_DIR = ".samva/brands";
|
|
31
|
+
declare const SNAPSHOT_VERSION = 1;
|
|
32
|
+
/** A brand as a build compiles it, with the digest the API knows its content by. */
|
|
33
|
+
interface ResolvedBrand extends EmailBrand {
|
|
34
|
+
readonly digest: string;
|
|
35
|
+
}
|
|
36
|
+
/** The cached copy of a brand record, written by a live fetch or `samva brands pull`. */
|
|
37
|
+
interface BrandSnapshot extends ResolvedBrand {
|
|
38
|
+
readonly version: typeof SNAPSHOT_VERSION;
|
|
39
|
+
readonly name: string;
|
|
40
|
+
readonly isDefault: boolean;
|
|
41
|
+
/** ISO timestamp of the pull. */
|
|
42
|
+
readonly pulledAt: string;
|
|
43
|
+
}
|
|
44
|
+
type BrandWarningCode = "brand-snapshot-stale" | "brand-offline";
|
|
45
|
+
interface BrandWarning {
|
|
46
|
+
readonly code: BrandWarningCode;
|
|
47
|
+
readonly message: string;
|
|
48
|
+
}
|
|
49
|
+
type BrandResolution = {
|
|
50
|
+
readonly ok: true;
|
|
51
|
+
/** `undefined` when the project imports no brand. */
|
|
52
|
+
readonly brand: ResolvedBrand | undefined;
|
|
53
|
+
readonly source: "live" | "snapshot" | "none";
|
|
54
|
+
readonly warnings: readonly BrandWarning[];
|
|
55
|
+
} | {
|
|
56
|
+
readonly ok: false;
|
|
57
|
+
readonly message: string;
|
|
58
|
+
};
|
|
59
|
+
interface ResolveProjectBrandInput {
|
|
60
|
+
readonly root: string;
|
|
61
|
+
readonly files: ProjectFiles;
|
|
62
|
+
/** Project-relative theme path; `undefined` when theming is disabled. */
|
|
63
|
+
readonly themePath?: string | undefined;
|
|
64
|
+
/** Environment holding `SAMVA_API_KEY`, `SAMVA_API_URL` and `PATH`; see `loadBrandEnv`. */
|
|
65
|
+
readonly env: Readonly<Record<string, string | undefined>>;
|
|
66
|
+
/** Skip the live fetch and resolve from the cached copy. */
|
|
67
|
+
readonly offline?: boolean | undefined;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Resolve the brand a project's `samva:brand` import names: live when the
|
|
71
|
+
* environment's credentials reach the API, else the pulled snapshot with a
|
|
72
|
+
* warning, else a failure naming both fixes.
|
|
73
|
+
*/
|
|
74
|
+
declare const resolveProjectBrand: (input: ResolveProjectBrandInput) => Promise<BrandResolution>;
|
|
75
|
+
/**
|
|
76
|
+
* The environment a local build resolves the brand with: the process
|
|
77
|
+
* environment plus the project's `.env` files, as the editor's dev server
|
|
78
|
+
* reads them, so `SAMVA_API_KEY` in `.env` reaches `check` and `export` too.
|
|
79
|
+
*/
|
|
80
|
+
declare const loadBrandEnv: (root: string) => Promise<Readonly<Record<string, string | undefined>>>;
|
|
81
|
+
interface BrandResolver {
|
|
82
|
+
readonly resolve: (files: ProjectFiles, themePath: string | undefined) => Promise<BrandResolution>;
|
|
83
|
+
}
|
|
84
|
+
//#endregion
|
|
85
|
+
//#region src/compile.d.ts
|
|
86
|
+
type TemplateChannel = "email" | "sms" | "whatsapp";
|
|
87
|
+
/** The channels a compiled template declares, in a fixed order. */
|
|
88
|
+
declare const channelsOf: (ir: TemplateIr) => readonly TemplateChannel[];
|
|
89
|
+
declare const finding: (code: string, severity: EmailDiagnostic["severity"], message: string, extra?: Partial<EmailDiagnostic>) => EmailDiagnostic;
|
|
90
|
+
/** One entry that declares a template and compiled. */
|
|
91
|
+
interface CompiledEntry {
|
|
92
|
+
/** The identity the definition declares for itself (`ir.template`). */
|
|
93
|
+
readonly id: string;
|
|
94
|
+
/** Project-relative path of the authored entry. */
|
|
95
|
+
readonly entryPath: string;
|
|
96
|
+
readonly ir: TemplateIr;
|
|
97
|
+
readonly fixtures: Readonly<Record<string, unknown>>;
|
|
98
|
+
readonly assets: readonly AssetEntry[];
|
|
99
|
+
/** Compile warnings. Errors leave no IR, so they are on a `FailedEntry` instead. */
|
|
100
|
+
readonly diagnostics: readonly EmailDiagnostic[];
|
|
101
|
+
readonly channels: readonly TemplateChannel[];
|
|
102
|
+
}
|
|
103
|
+
/** An entry that is a template but did not compile. */
|
|
104
|
+
interface FailedEntry {
|
|
105
|
+
readonly entryPath: string;
|
|
106
|
+
readonly diagnostics: readonly EmailDiagnostic[];
|
|
107
|
+
}
|
|
108
|
+
interface CompiledProject {
|
|
109
|
+
/** Sorted by id. */
|
|
110
|
+
readonly templates: readonly CompiledEntry[];
|
|
111
|
+
/** Entries that declare a template and did not compile, sorted by path. */
|
|
112
|
+
readonly failures: readonly FailedEntry[];
|
|
113
|
+
/** Candidate entries that declare no template: ordinary modules, partials and components. */
|
|
114
|
+
readonly helpers: readonly string[];
|
|
115
|
+
/** Project-level findings: a duplicate id. */
|
|
116
|
+
readonly diagnostics: readonly EmailDiagnostic[];
|
|
117
|
+
/** Every asset the templates import, deduplicated by content. */
|
|
118
|
+
readonly assets: readonly AssetEntry[];
|
|
119
|
+
}
|
|
120
|
+
interface CompileProjectInput {
|
|
121
|
+
readonly files: ProjectFiles;
|
|
122
|
+
/** Candidate entries, project-relative. */
|
|
123
|
+
readonly entries: readonly string[];
|
|
124
|
+
/**
|
|
125
|
+
* Where imported assets are served from. Empty is deliberately invalid for
|
|
126
|
+
* assets: no invented serving origin. A project that imports none needs none.
|
|
127
|
+
*/
|
|
128
|
+
readonly assetBase: string;
|
|
129
|
+
readonly theme?: {
|
|
130
|
+
readonly css: string;
|
|
131
|
+
readonly path: string;
|
|
132
|
+
} | undefined;
|
|
133
|
+
readonly brand?: ResolvedBrand | undefined;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Compile every candidate entry. An entry the compiler reports `no-template`
|
|
137
|
+
* for is a helper module, not a template; that is how a partial is told apart
|
|
138
|
+
* from a template without evaluating either. One failure stays with its entry.
|
|
139
|
+
*/
|
|
140
|
+
declare const compileProject: (input: CompileProjectInput) => Promise<CompiledProject>;
|
|
141
|
+
/** One fixture rendered through the email channel of the template it belongs to. */
|
|
142
|
+
interface RenderedFixture {
|
|
143
|
+
readonly templateId: string;
|
|
144
|
+
readonly fixture: string;
|
|
145
|
+
readonly rendered: RenderedIrEmail | undefined;
|
|
146
|
+
readonly diagnostics: readonly EmailDiagnostic[];
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Render every fixture a template declares through its email channel. A fixture
|
|
150
|
+
* the renderer refuses is a finding on that fixture, never a thrown error, so
|
|
151
|
+
* one bad input does not take the rest of the catalog with it.
|
|
152
|
+
*/
|
|
153
|
+
declare const renderEmailFixtures: (entry: Pick<CompiledEntry, "id" | "ir" | "fixtures">, options?: RenderOptions) => readonly RenderedFixture[];
|
|
154
|
+
/** Client-matrix findings across the fixtures that rendered. */
|
|
155
|
+
declare const emailCompatibility: (entry: Pick<CompiledEntry, "ir" | "fixtures">, rendered: readonly RenderedFixture[]) => readonly EmailDiagnostic[];
|
|
156
|
+
/** A channel's rendered output for one fixture, or the findings that stopped it. */
|
|
157
|
+
type RenderedChannel = {
|
|
158
|
+
readonly ok: true;
|
|
159
|
+
readonly channel: "sms";
|
|
160
|
+
readonly text: string;
|
|
161
|
+
} | {
|
|
162
|
+
readonly ok: true;
|
|
163
|
+
readonly channel: "whatsapp";
|
|
164
|
+
readonly message: RenderedIrWhatsApp;
|
|
165
|
+
} | {
|
|
166
|
+
readonly ok: false;
|
|
167
|
+
readonly diagnostics: readonly EmailDiagnostic[];
|
|
168
|
+
};
|
|
169
|
+
/** Render the SMS or WhatsApp channel of one fixture. */
|
|
170
|
+
declare const renderChannelFixture: (entry: Pick<CompiledEntry, "id" | "ir" | "fixtures">, channel: "sms" | "whatsapp", fixture: string, options?: RenderOptions) => RenderedChannel;
|
|
171
|
+
declare const hasErrors: (diagnostics: readonly EmailDiagnostic[]) => boolean;
|
|
172
|
+
//#endregion
|
|
173
|
+
//#region src/project.d.ts
|
|
174
|
+
interface ProjectOptions {
|
|
175
|
+
/** The project root: `theme.css`, shared imports and `.samva/` live here. Default: the working directory. */
|
|
176
|
+
readonly root?: string | undefined;
|
|
177
|
+
/**
|
|
178
|
+
* The templates directory, relative to `root` (or absolute, inside it). When set, only it is
|
|
179
|
+
* scanned; otherwise `templates/` and `emails/` are, whichever exist.
|
|
180
|
+
*/
|
|
181
|
+
readonly dir?: string | undefined;
|
|
182
|
+
/**
|
|
183
|
+
* Skip the live brand fetch and use the cached copy in `.samva/brands`, as with no network.
|
|
184
|
+
*/
|
|
185
|
+
readonly offline?: boolean | undefined;
|
|
186
|
+
/** Theme stylesheet relative to `root`; `false` compiles with no project theme. Default `theme.css`. */
|
|
187
|
+
readonly theme?: string | false | undefined;
|
|
188
|
+
/**
|
|
189
|
+
* Where imported assets are served from. A template that imports none needs none; an import
|
|
190
|
+
* with no base is a finding.
|
|
191
|
+
*/
|
|
192
|
+
readonly assetBase?: string | undefined;
|
|
193
|
+
}
|
|
194
|
+
/** A project compiled: its catalog, and the findings about the project as a whole. */
|
|
195
|
+
interface LoadedProject {
|
|
196
|
+
readonly root: string;
|
|
197
|
+
readonly files: ProjectFiles;
|
|
198
|
+
readonly catalog: CompiledProject;
|
|
199
|
+
/** The theme path, project-relative; `undefined` when theming is off or the file is absent. */
|
|
200
|
+
readonly themePath: string | undefined;
|
|
201
|
+
readonly brand: BrandResolution;
|
|
202
|
+
/** Brand findings and project-level findings (a duplicate id). */
|
|
203
|
+
readonly diagnostics: readonly EmailDiagnostic[];
|
|
204
|
+
}
|
|
205
|
+
/** Resolve the root a set of options names. */
|
|
206
|
+
declare const projectRoot: (options: Pick<ProjectOptions, "root">) => string;
|
|
207
|
+
/**
|
|
208
|
+
* Read, resolve the brand of, and compile every template in a project. `brands`
|
|
209
|
+
* lets a long-lived caller reuse a brand resolution between loads, and `only`
|
|
210
|
+
* limits compilation to the named project-relative entries.
|
|
211
|
+
*/
|
|
212
|
+
declare const loadProject: (options?: ProjectOptions, brands?: BrandResolver | undefined, only?: readonly string[] | undefined) => Promise<LoadedProject>;
|
|
213
|
+
/** A live project over a directory, reloaded when its source changes. */
|
|
214
|
+
interface ProjectSession {
|
|
215
|
+
readonly load: () => Promise<LoadedProject>;
|
|
216
|
+
readonly current: () => LoadedProject | undefined;
|
|
217
|
+
/** Called with the project-relative path of each changed file; `.samva/` and `node_modules` are ignored. */
|
|
218
|
+
readonly subscribe: (listener: (path: string) => void) => () => void;
|
|
219
|
+
readonly close: () => Promise<void>;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* The authoring loop: one watcher, one brand resolver, a catalog reloaded on
|
|
223
|
+
* demand. Each `load` re-reads the project and recompiles every template, so a
|
|
224
|
+
* partial that changed reaches every template that imports it.
|
|
225
|
+
*/
|
|
226
|
+
declare const createProjectSession: (options?: ProjectOptions) => ProjectSession;
|
|
227
|
+
interface ExportedFixture {
|
|
228
|
+
readonly fixture: string;
|
|
229
|
+
readonly html?: string | undefined;
|
|
230
|
+
readonly text?: string | undefined;
|
|
231
|
+
readonly sms?: string | undefined;
|
|
232
|
+
readonly whatsapp?: string | undefined;
|
|
233
|
+
}
|
|
234
|
+
interface ProjectExportReceipt {
|
|
235
|
+
readonly templates: readonly {
|
|
236
|
+
readonly id: string;
|
|
237
|
+
readonly entryPath: string;
|
|
238
|
+
readonly fixtures: readonly ExportedFixture[];
|
|
239
|
+
}[];
|
|
240
|
+
readonly assets: {
|
|
241
|
+
readonly base: string;
|
|
242
|
+
readonly assets: readonly AssetEntry[];
|
|
243
|
+
};
|
|
244
|
+
readonly diagnostics: readonly EmailDiagnostic[];
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Where an export serves its assets from when the caller names no origin.
|
|
248
|
+
*
|
|
249
|
+
* An export writes one page at `templates/<id>/<fixture>.html` and every asset
|
|
250
|
+
* once at `assets/<content address>`, so the base a page resolves against is
|
|
251
|
+
* the path from its own directory back to that one. A template id is one path
|
|
252
|
+
* segment, so every page sits at the same depth and one base serves them all.
|
|
253
|
+
*/
|
|
254
|
+
declare const DEFAULT_EXPORT_ASSET_BASE = "../../assets";
|
|
255
|
+
/**
|
|
256
|
+
* Render every template's fixtures to files beside the bytes of every asset they
|
|
257
|
+
* reference, with the manifest that maps each address to its file. Email
|
|
258
|
+
* fixtures write `<fixture>.html` and `<fixture>.txt`; a template's SMS and
|
|
259
|
+
* WhatsApp channels write `<fixture>.sms.txt` and `<fixture>.whatsapp.json`.
|
|
260
|
+
*
|
|
261
|
+
* A relative `assetBase` is accepted, and the written page resolves its images
|
|
262
|
+
* from the directory it sits in. Delivery keeps refusing relative references,
|
|
263
|
+
* because an inbox has no directory to resolve one against, so an export
|
|
264
|
+
* destined for sending names the https origin it will be served from instead.
|
|
265
|
+
*/
|
|
266
|
+
declare const exportProject: (input: ProjectOptions & {
|
|
267
|
+
readonly out: string;
|
|
268
|
+
}) => Promise<ProjectExportReceipt>;
|
|
269
|
+
//#endregion
|
|
270
|
+
export { loadBrandEnv as A, renderChannelFixture as C, BrandSnapshot as D, BrandResolution as E, discoverEntries as F, readProjectFiles as I, templateDirectories as L, DEFAULT_TEMPLATE_DIRS as M, ProjectFiles as N, BrandWarning as O, SAMVA_DIR as P, hasErrors as S, BRAND_SNAPSHOT_DIR as T, TemplateChannel as _, ProjectOptions as a, emailCompatibility as b, exportProject as c, CompileProjectInput as d, CompiledEntry as f, RenderedFixture as g, RenderedChannel as h, ProjectExportReceipt as i, resolveProjectBrand as j, ResolvedBrand as k, loadProject as l, FailedEntry as m, ExportedFixture as n, ProjectSession as o, CompiledProject as p, LoadedProject as r, createProjectSession as s, DEFAULT_EXPORT_ASSET_BASE as t, projectRoot as u, channelsOf as v, renderEmailFixtures as w, finding as x, compileProject as y };
|
|
271
|
+
//# sourceMappingURL=project-CiUuzKsC.d.ts.map
|