@maizzle/framework 6.0.0-rc.24 → 6.0.0-rc.26
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/dist/build.d.ts +19 -1
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +139 -102
- package/dist/build.js.map +1 -1
- package/dist/components/Body.vue +12 -0
- package/dist/components/Button.vue +16 -29
- package/dist/components/CodeBlock.vue +5 -4
- package/dist/components/CodeInline.vue +9 -8
- package/dist/components/Column.vue +17 -4
- package/dist/components/Container.vue +7 -11
- package/dist/components/Hr.vue +1 -1
- package/dist/components/Img.vue +39 -22
- package/dist/components/Layout.vue +1 -1
- package/dist/components/Markdown.vue +9 -14
- package/dist/components/QrCode.vue +2 -2
- package/dist/components/Section.vue +9 -6
- package/dist/components/Text.vue +2 -2
- package/dist/components/utils.d.ts +25 -1
- package/dist/components/utils.d.ts.map +1 -1
- package/dist/components/utils.js +29 -1
- package/dist/components/utils.js.map +1 -1
- package/dist/components/utils.ts +46 -0
- package/dist/composables/useConfig.d.ts.map +1 -1
- package/dist/composables/useCurrentTemplate.d.ts.map +1 -1
- package/dist/composables/useEvent.d.ts.map +1 -1
- package/dist/composables/useFont.js.map +1 -1
- package/dist/config/index.js +1 -1
- package/dist/config/index.js.map +1 -1
- package/dist/events/index.d.ts.map +1 -1
- package/dist/events/index.js.map +1 -1
- package/dist/index.js +2 -2
- package/dist/plaintext.js.map +1 -1
- package/dist/plugins/postcss/mergeMediaQueries.js.map +1 -1
- package/dist/plugins/postcss/pruneVars.js.map +1 -1
- package/dist/plugins/postcss/quoteFontFamilies.d.ts.map +1 -1
- package/dist/plugins/postcss/quoteFontFamilies.js.map +1 -1
- package/dist/plugins/postcss/removeDeclarations.js.map +1 -1
- package/dist/plugins/postcss/resolveProps.d.ts.map +1 -1
- package/dist/plugins/postcss/resolveProps.js +0 -3
- package/dist/plugins/postcss/resolveProps.js.map +1 -1
- package/dist/plugins/postcss/tailwindCleanup.js.map +1 -1
- package/dist/prepare.js +1 -1
- package/dist/prepare.js.map +1 -1
- package/dist/render/active.d.ts.map +1 -1
- package/dist/render/buildTemplate.d.ts +49 -0
- package/dist/render/buildTemplate.d.ts.map +1 -0
- package/dist/render/buildTemplate.js +139 -0
- package/dist/render/buildTemplate.js.map +1 -0
- package/dist/render/createRenderer.d.ts +3 -1
- package/dist/render/createRenderer.d.ts.map +1 -1
- package/dist/render/createRenderer.js +43 -10
- package/dist/render/createRenderer.js.map +1 -1
- package/dist/render/index.js +1 -1
- package/dist/render/index.js.map +1 -1
- package/dist/render/injectFonts.js.map +1 -1
- package/dist/render/parallel/buildWorker.d.ts +31 -0
- package/dist/render/parallel/buildWorker.d.ts.map +1 -0
- package/dist/render/parallel/buildWorker.js +66 -0
- package/dist/render/parallel/buildWorker.js.map +1 -0
- package/dist/render/parallel/worker.mjs +28 -0
- package/dist/render/plugins/codeBlockExtract.d.ts.map +1 -1
- package/dist/render/plugins/codeBlockExtract.js.map +1 -1
- package/dist/render/plugins/markdownExtract.d.ts.map +1 -1
- package/dist/render/plugins/markdownExtract.js.map +1 -1
- package/dist/render/plugins/rawExtract.d.ts.map +1 -1
- package/dist/render/plugins/rawExtract.js.map +1 -1
- package/dist/render/plugins/rowSourceLocation.d.ts.map +1 -1
- package/dist/render/plugins/rowSourceLocation.js.map +1 -1
- package/dist/serve.d.ts.map +1 -1
- package/dist/serve.js +73 -53
- package/dist/serve.js.map +1 -1
- package/dist/server/compatibility.d.ts.map +1 -1
- package/dist/server/compatibility.js.map +1 -1
- package/dist/server/linter.js.map +1 -1
- package/dist/server/sfc-utils.js +1 -1
- package/dist/server/sfc-utils.js.map +1 -1
- package/dist/server/ui/pages/Preview.vue +34 -11
- package/dist/server/ui/vite-env.d.ts +1 -0
- package/dist/tests/render/_helpers.d.ts.map +1 -1
- package/dist/tests/render/_helpers.js.map +1 -1
- package/dist/transformers/addAttributes.js +2 -3
- package/dist/transformers/addAttributes.js.map +1 -1
- package/dist/transformers/base.d.ts +1 -1
- package/dist/transformers/base.d.ts.map +1 -1
- package/dist/transformers/base.js +5 -10
- package/dist/transformers/base.js.map +1 -1
- package/dist/transformers/columnWidth.d.ts.map +1 -1
- package/dist/transformers/columnWidth.js +2 -7
- package/dist/transformers/columnWidth.js.map +1 -1
- package/dist/transformers/entities.js.map +1 -1
- package/dist/transformers/filters/defaults.js.map +1 -1
- package/dist/transformers/filters/index.js.map +1 -1
- package/dist/transformers/format.js.map +1 -1
- package/dist/transformers/imgWidth.d.ts +20 -0
- package/dist/transformers/imgWidth.d.ts.map +1 -0
- package/dist/transformers/imgWidth.js +76 -0
- package/dist/transformers/imgWidth.js.map +1 -0
- package/dist/transformers/index.d.ts.map +1 -1
- package/dist/transformers/index.js +2 -0
- package/dist/transformers/index.js.map +1 -1
- package/dist/transformers/inlineCss.d.ts +3 -2
- package/dist/transformers/inlineCss.d.ts.map +1 -1
- package/dist/transformers/inlineCss.js.map +1 -1
- package/dist/transformers/inlineLink.js +1 -1
- package/dist/transformers/inlineLink.js.map +1 -1
- package/dist/transformers/minify.js.map +1 -1
- package/dist/transformers/minifyCodeInline.js.map +1 -1
- package/dist/transformers/msoPlaceholders.d.ts.map +1 -1
- package/dist/transformers/msoPlaceholders.js +2 -7
- package/dist/transformers/msoPlaceholders.js.map +1 -1
- package/dist/transformers/purgeCss.js.map +1 -1
- package/dist/transformers/replaceStrings.js.map +1 -1
- package/dist/transformers/safeSelectors.js.map +1 -1
- package/dist/transformers/shorthandCss.js.map +1 -1
- package/dist/transformers/tailwindComponent.js.map +1 -1
- package/dist/transformers/tailwindcss.js +1 -1
- package/dist/transformers/tailwindcss.js.map +1 -1
- package/dist/transformers/urlQuery.js.map +1 -1
- package/dist/types/config.d.ts +26 -4
- package/dist/types/config.d.ts.map +1 -1
- package/dist/utils/ast/serializer.js.map +1 -1
- package/dist/utils/compileTailwindCss.js.map +1 -1
- package/dist/utils/componentSources.js.map +1 -1
- package/dist/utils/cssBox.d.ts.map +1 -1
- package/dist/utils/cssBox.js +2 -7
- package/dist/utils/cssBox.js.map +1 -1
- package/dist/utils/decodeStyleEntities.js.map +1 -1
- package/dist/utils/url.js.map +1 -1
- package/dist/utils/watchPaths.js.map +1 -1
- package/node_modules/@clack/core/CHANGELOG.md +30 -0
- package/node_modules/@clack/core/dist/index.d.mts +109 -3
- package/node_modules/@clack/core/dist/index.mjs +972 -17
- package/node_modules/@clack/core/package.json +2 -1
- package/node_modules/@clack/prompts/CHANGELOG.md +42 -0
- package/node_modules/@clack/prompts/README.md +30 -9
- package/node_modules/@clack/prompts/dist/index.d.mts +458 -27
- package/node_modules/@clack/prompts/dist/index.mjs +1378 -141
- package/node_modules/@clack/prompts/package.json +2 -2
- package/node_modules/tinyexec/package.json +4 -4
- package/package.json +13 -11
- package/dist/components/Overlap.vue +0 -156
- package/node_modules/@clack/core/dist/index.mjs.map +0 -1
- package/node_modules/@clack/prompts/dist/index.mjs.map +0 -1
package/dist/build.d.ts
CHANGED
|
@@ -15,6 +15,24 @@ interface BuildResult {
|
|
|
15
15
|
* from the working directory.
|
|
16
16
|
*/
|
|
17
17
|
declare function build(configInput?: Partial<MaizzleConfig> | string): Promise<BuildResult>;
|
|
18
|
+
/**
|
|
19
|
+
* Decide whether to build in parallel and with how many workers.
|
|
20
|
+
*
|
|
21
|
+
* `config.parallel`:
|
|
22
|
+
* - omitted → parallel when `count > 50`, min(CPU count − 1, 8) workers
|
|
23
|
+
* - `true` → always parallel (ignores threshold), default workers
|
|
24
|
+
* - `false` → always sequential
|
|
25
|
+
* - `{ workers, threshold }` → parallel when `count > threshold` (default 50),
|
|
26
|
+
* using `workers` threads (default min(CPU count − 1, 8))
|
|
27
|
+
*
|
|
28
|
+
* Workers reload the config file to recover function hooks, so parallel only
|
|
29
|
+
* applies to file-based configs (a path or the default cwd config) — an inline
|
|
30
|
+
* config object has no file to reload and always builds sequentially.
|
|
31
|
+
*/
|
|
32
|
+
declare function resolveParallel(config: MaizzleConfig, count: number, configInput: Partial<MaizzleConfig> | string | undefined): {
|
|
33
|
+
enabled: boolean;
|
|
34
|
+
workers: number;
|
|
35
|
+
};
|
|
18
36
|
//#endregion
|
|
19
|
-
export { BuildResult, build };
|
|
37
|
+
export { BuildResult, build, resolveParallel };
|
|
20
38
|
//# sourceMappingURL=build.d.ts.map
|
package/dist/build.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build.d.ts","names":[],"sources":["../src/build.ts"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"build.d.ts","names":[],"sources":["../src/build.ts"],"mappings":";;UAaiB,WAAA;EACf,KAAA;EACA,MAAA,EAAQ,aAAa;AAAA;;;;;;;;AAAA;AAavB;;iBAAsB,KAAA,CAAM,WAAA,GAAc,OAAA,CAAQ,aAAA,aAA0B,OAAA,CAAQ,WAAA;;;;;;;;;;;;;AAAW;AA6G/F;iBAAgB,eAAA,CACd,MAAA,EAAQ,aAAA,EACR,KAAA,UACA,WAAA,EAAa,OAAA,CAAQ,aAAA;EAClB,OAAA;EAAkB,OAAA;AAAA"}
|
package/dist/build.js
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
import { resolveConfig } from "./config/index.js";
|
|
2
2
|
import { EventManager } from "./events/index.js";
|
|
3
|
-
import { runTransformers } from "./transformers/index.js";
|
|
4
3
|
import { normalizeComponentSources } from "./utils/componentSources.js";
|
|
5
4
|
import { createRenderer } from "./render/createRenderer.js";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
5
|
+
import { buildTemplate, computeContentBase } from "./render/buildTemplate.js";
|
|
6
|
+
import { cpSync, existsSync, mkdirSync, rmSync } from "node:fs";
|
|
7
|
+
import { dirname, join, relative, resolve } from "node:path";
|
|
8
|
+
import { fileURLToPath } from "node:url";
|
|
9
|
+
import { availableParallelism } from "node:os";
|
|
11
10
|
import { glob } from "tinyglobby";
|
|
12
11
|
import ora from "ora";
|
|
13
|
-
import defu from "defu";
|
|
14
12
|
//#region src/build.ts
|
|
15
13
|
/**
|
|
16
14
|
* Build all SFC email templates to HTML files.
|
|
@@ -48,97 +46,56 @@ async function build(configInput) {
|
|
|
48
46
|
recursive: true,
|
|
49
47
|
force: true
|
|
50
48
|
});
|
|
51
|
-
const renderer = await createRenderer({
|
|
52
|
-
markdown: config.markdown,
|
|
53
|
-
root: config.root,
|
|
54
|
-
componentDirs: normalizeComponentSources(config.components?.source, process.cwd()),
|
|
55
|
-
vite: config.vite
|
|
56
|
-
});
|
|
57
49
|
const outputFiles = [];
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Register SFC event handlers collected during render so they take
|
|
75
|
-
* part in the post-render events (afterRender / afterTransform).
|
|
76
|
-
* They're cleared at the end of the iteration so they don't
|
|
77
|
-
* leak into the next template.
|
|
78
|
-
*/
|
|
79
|
-
for (const { name, handler } of rendered.sfcEventHandlers) events.on(name, handler);
|
|
80
|
-
let html = await events.fireAfterRender({
|
|
81
|
-
config,
|
|
82
|
-
template,
|
|
83
|
-
html: rendered.html
|
|
84
|
-
});
|
|
85
|
-
/**
|
|
86
|
-
* Use the per-template merged config (from defineConfig() in the SFC) so
|
|
87
|
-
* that template-level overrides like css.safe: false are respected
|
|
88
|
-
* by transformers.
|
|
89
|
-
*/
|
|
90
|
-
const templateConfig = rendered.templateConfig;
|
|
91
|
-
const doctype = rendered.doctype ?? templateConfig.doctype ?? "<!DOCTYPE html>";
|
|
92
|
-
if (templateConfig.useTransformers !== false) html = await runTransformers(html, templateConfig, absolutePath, doctype, rendered.tailwindBlocks);
|
|
93
|
-
html = await events.fireAfterTransform({
|
|
94
|
-
config,
|
|
95
|
-
template,
|
|
96
|
-
html
|
|
97
|
-
});
|
|
98
|
-
if (doctype) html = `${doctype}\n${html}`;
|
|
99
|
-
const htmlOut = stripForHtml(html);
|
|
100
|
-
const sfcOutputPath = rendered.outputPath;
|
|
101
|
-
let outputFilePath;
|
|
102
|
-
if (sfcOutputPath) {
|
|
103
|
-
const parsed = parse(resolve(sfcOutputPath));
|
|
104
|
-
const ext = parsed.ext ? parsed.ext.slice(1) : outputExtension;
|
|
105
|
-
outputFilePath = join(parsed.dir, `${parsed.name}.${ext}`);
|
|
106
|
-
} else outputFilePath = resolveOutputPath(templatePath, outputPath, outputExtension, contentBase);
|
|
107
|
-
mkdirSync(dirname(outputFilePath), { recursive: true });
|
|
108
|
-
writeFileSync(outputFilePath, htmlOut);
|
|
109
|
-
outputFiles.push(outputFilePath);
|
|
110
|
-
const globalPlaintext = templateConfig.plaintext;
|
|
111
|
-
const sfcPlaintext = rendered.plaintext;
|
|
112
|
-
if (globalPlaintext || sfcPlaintext) {
|
|
113
|
-
const globalCfg = typeof globalPlaintext === "object" ? globalPlaintext : {};
|
|
114
|
-
const stripOptions = defu(sfcPlaintext?.options, globalCfg.options);
|
|
115
|
-
const plaintext = createPlaintext(stripForPlaintext(html), stripOptions);
|
|
116
|
-
const ptExtension = sfcPlaintext?.extension ?? globalCfg.extension ?? "txt";
|
|
117
|
-
let ptOutputPath;
|
|
118
|
-
if (sfcPlaintext?.destination) {
|
|
119
|
-
const name = basename(templatePath).replace(/\.(vue|md)$/, "");
|
|
120
|
-
ptOutputPath = join(resolve(sfcPlaintext.destination), `${name}.${ptExtension}`);
|
|
121
|
-
} else if (sfcOutputPath) {
|
|
122
|
-
const parsed = parse(outputFilePath);
|
|
123
|
-
ptOutputPath = join(parsed.dir, `${parsed.name}.${ptExtension}`);
|
|
124
|
-
} else if (globalCfg.destination) ptOutputPath = resolveOutputPath(templatePath, resolve(globalCfg.destination), ptExtension, contentBase);
|
|
125
|
-
else ptOutputPath = resolveOutputPath(templatePath, outputPath, ptExtension, contentBase);
|
|
126
|
-
mkdirSync(dirname(ptOutputPath), { recursive: true });
|
|
127
|
-
writeFileSync(ptOutputPath, plaintext);
|
|
128
|
-
}
|
|
129
|
-
} finally {
|
|
130
|
-
_setCurrentTemplate(void 0);
|
|
131
|
-
events.clearSfcHandlers();
|
|
132
|
-
}
|
|
133
|
-
}
|
|
50
|
+
let droppedAfterBuild = 0;
|
|
51
|
+
const parallel = resolveParallel(config, templateFiles.length, configInput);
|
|
52
|
+
if (parallel.enabled) {
|
|
53
|
+
spinner.text = `Building ${templateFiles.length} templates across ${parallel.workers} workers...`;
|
|
54
|
+
const result = await runParallelBuild({
|
|
55
|
+
templateFiles,
|
|
56
|
+
workers: parallel.workers,
|
|
57
|
+
config,
|
|
58
|
+
configInput,
|
|
59
|
+
outputPath,
|
|
60
|
+
outputExtension,
|
|
61
|
+
contentBase
|
|
62
|
+
});
|
|
63
|
+
outputFiles.push(...result.files);
|
|
64
|
+
droppedAfterBuild = result.sfcAfterBuildCount;
|
|
134
65
|
await copyStatic(config, outputPath);
|
|
135
66
|
await events.fireAfterBuild({
|
|
136
67
|
files: outputFiles,
|
|
137
68
|
config
|
|
138
69
|
});
|
|
139
|
-
}
|
|
140
|
-
await
|
|
70
|
+
} else {
|
|
71
|
+
const renderer = await createRenderer({
|
|
72
|
+
markdown: config.markdown,
|
|
73
|
+
root: config.root,
|
|
74
|
+
componentDirs: normalizeComponentSources(config.components?.source, process.cwd()),
|
|
75
|
+
vite: config.vite
|
|
76
|
+
});
|
|
77
|
+
try {
|
|
78
|
+
for (const templatePath of templateFiles) {
|
|
79
|
+
const { files } = await buildTemplate(templatePath, {
|
|
80
|
+
config,
|
|
81
|
+
renderer,
|
|
82
|
+
events,
|
|
83
|
+
outputPath,
|
|
84
|
+
outputExtension,
|
|
85
|
+
contentBase
|
|
86
|
+
});
|
|
87
|
+
outputFiles.push(...files);
|
|
88
|
+
}
|
|
89
|
+
await copyStatic(config, outputPath);
|
|
90
|
+
await events.fireAfterBuild({
|
|
91
|
+
files: outputFiles,
|
|
92
|
+
config
|
|
93
|
+
});
|
|
94
|
+
} finally {
|
|
95
|
+
await renderer.close();
|
|
96
|
+
}
|
|
141
97
|
}
|
|
98
|
+
if (droppedAfterBuild > 0) console.warn(`[maizzle] Skipped ${droppedAfterBuild} SFC-registered afterBuild handler(s): afterBuild can't run inside a parallel build worker. Move build-completion logic to the config's afterBuild hook.`);
|
|
142
99
|
const duration = ((Date.now() - start) / 1e3).toFixed(2);
|
|
143
100
|
const count = outputFiles.length;
|
|
144
101
|
spinner.stopAndPersist({
|
|
@@ -151,20 +108,100 @@ async function build(configInput) {
|
|
|
151
108
|
};
|
|
152
109
|
}
|
|
153
110
|
/**
|
|
154
|
-
*
|
|
111
|
+
* Default template count above which parallel build turns on. Benchmarked
|
|
112
|
+
* crossover (with the worker cap below) is ~25 templates; 50 leaves margin so
|
|
113
|
+
* auto-parallel only kicks in where it's a reliable win across hardware.
|
|
114
|
+
* Override per project with `parallel: { threshold }`.
|
|
115
|
+
*/
|
|
116
|
+
const DEFAULT_PARALLEL_THRESHOLD = 50;
|
|
117
|
+
/**
|
|
118
|
+
* Default worker cap. Each worker runs a full Vite SSR renderer, so startup +
|
|
119
|
+
* contention outweighs added parallelism past ~8 — benchmarks showed 8 beating
|
|
120
|
+
* 12/16/23 at every size. Override with `parallel: { workers }`.
|
|
121
|
+
*/
|
|
122
|
+
const DEFAULT_MAX_WORKERS = 8;
|
|
123
|
+
/**
|
|
124
|
+
* Decide whether to build in parallel and with how many workers.
|
|
155
125
|
*
|
|
156
|
-
*
|
|
126
|
+
* `config.parallel`:
|
|
127
|
+
* - omitted → parallel when `count > 50`, min(CPU count − 1, 8) workers
|
|
128
|
+
* - `true` → always parallel (ignores threshold), default workers
|
|
129
|
+
* - `false` → always sequential
|
|
130
|
+
* - `{ workers, threshold }` → parallel when `count > threshold` (default 50),
|
|
131
|
+
* using `workers` threads (default min(CPU count − 1, 8))
|
|
157
132
|
*
|
|
158
|
-
*
|
|
159
|
-
*
|
|
133
|
+
* Workers reload the config file to recover function hooks, so parallel only
|
|
134
|
+
* applies to file-based configs (a path or the default cwd config) — an inline
|
|
135
|
+
* config object has no file to reload and always builds sequentially.
|
|
136
|
+
*/
|
|
137
|
+
function resolveParallel(config, count, configInput) {
|
|
138
|
+
const setting = config.parallel;
|
|
139
|
+
if (setting === false) return {
|
|
140
|
+
enabled: false,
|
|
141
|
+
workers: 0
|
|
142
|
+
};
|
|
143
|
+
if (!(typeof configInput === "string" || configInput == null)) return {
|
|
144
|
+
enabled: false,
|
|
145
|
+
workers: 0
|
|
146
|
+
};
|
|
147
|
+
const cpus = availableParallelism();
|
|
148
|
+
let maxWorkers = Math.min(Math.max(1, cpus - 1), DEFAULT_MAX_WORKERS);
|
|
149
|
+
let threshold = DEFAULT_PARALLEL_THRESHOLD;
|
|
150
|
+
const ignoreThreshold = setting === true;
|
|
151
|
+
if (typeof setting === "object" && setting !== null) {
|
|
152
|
+
if (typeof setting.workers === "number" && setting.workers > 0) maxWorkers = Math.floor(setting.workers);
|
|
153
|
+
if (typeof setting.threshold === "number" && setting.threshold >= 0) threshold = Math.floor(setting.threshold);
|
|
154
|
+
}
|
|
155
|
+
if (!ignoreThreshold && count <= threshold) return {
|
|
156
|
+
enabled: false,
|
|
157
|
+
workers: 0
|
|
158
|
+
};
|
|
159
|
+
const workers = Math.min(maxWorkers, count);
|
|
160
|
+
return {
|
|
161
|
+
enabled: workers >= 2 && count >= 2,
|
|
162
|
+
workers
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Run the build across worker threads. Each worker reloads the config (for its
|
|
167
|
+
* function hooks), builds its batch via the same `buildTemplate` as the
|
|
168
|
+
* sequential path, and returns the files it wrote. beforeCreate/afterBuild stay
|
|
169
|
+
* on the main thread (handled by the caller).
|
|
160
170
|
*/
|
|
161
|
-
function
|
|
162
|
-
const
|
|
163
|
-
|
|
171
|
+
async function runParallelBuild(opts) {
|
|
172
|
+
const { templateFiles, workers, config, configInput, outputPath, outputExtension, contentBase } = opts;
|
|
173
|
+
const { default: Tinypool } = await import("tinypool");
|
|
174
|
+
const workerPath = resolve(dirname(fileURLToPath(import.meta.url)), "render/parallel/worker.mjs");
|
|
175
|
+
const configPath = typeof configInput === "string" ? configInput : void 0;
|
|
176
|
+
const configData = JSON.parse(JSON.stringify(config));
|
|
177
|
+
const batches = shardEvenly(templateFiles, workers);
|
|
178
|
+
const pool = new Tinypool({
|
|
179
|
+
filename: workerPath,
|
|
180
|
+
minThreads: batches.length,
|
|
181
|
+
maxThreads: batches.length
|
|
182
|
+
});
|
|
183
|
+
try {
|
|
184
|
+
const results = await Promise.all(batches.map((templatePaths) => pool.run({
|
|
185
|
+
templatePaths,
|
|
186
|
+
configPath,
|
|
187
|
+
configData,
|
|
188
|
+
outputPath,
|
|
189
|
+
outputExtension,
|
|
190
|
+
contentBase
|
|
191
|
+
})));
|
|
192
|
+
return {
|
|
193
|
+
files: results.flatMap((r) => r.files),
|
|
194
|
+
sfcAfterBuildCount: results.reduce((n, r) => n + r.sfcAfterBuildCount, 0)
|
|
195
|
+
};
|
|
196
|
+
} finally {
|
|
197
|
+
await pool.destroy();
|
|
198
|
+
}
|
|
164
199
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
200
|
+
/** Round-robin items into up to `buckets` non-empty groups for even balance. */
|
|
201
|
+
function shardEvenly(items, buckets) {
|
|
202
|
+
const out = Array.from({ length: buckets }, () => []);
|
|
203
|
+
items.forEach((item, i) => out[i % buckets].push(item));
|
|
204
|
+
return out.filter((b) => b.length > 0);
|
|
168
205
|
}
|
|
169
206
|
async function copyStatic(config, outputPath) {
|
|
170
207
|
const sources = config.static?.source ?? ["public/**/*.*"];
|
|
@@ -178,6 +215,6 @@ async function copyStatic(config, outputPath) {
|
|
|
178
215
|
}
|
|
179
216
|
}
|
|
180
217
|
//#endregion
|
|
181
|
-
export { build };
|
|
218
|
+
export { build, resolveParallel };
|
|
182
219
|
|
|
183
220
|
//# sourceMappingURL=build.js.map
|
package/dist/build.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build.js","names":["parsePath"],"sources":["../src/build.ts"],"sourcesContent":["import { readFileSync, writeFileSync, mkdirSync, cpSync, existsSync, rmSync } from 'node:fs'\nimport { resolve, dirname, basename, relative, join, parse as parsePath } from 'node:path'\nimport { glob } from 'tinyglobby'\nimport ora from 'ora'\nimport { resolveConfig } from './config/index.ts'\nimport { EventManager } from './events/index.ts'\nimport { runTransformers } from './transformers/index.ts'\nimport { createRenderer } from './render/createRenderer.ts'\nimport { createPlaintext } from './plaintext.ts'\nimport { stripForHtml, stripForPlaintext } from './utils/output-markers.ts'\nimport { normalizeComponentSources } from './utils/componentSources.ts'\nimport { _setCurrentTemplate } from './composables/useCurrentTemplate.ts'\nimport defu from 'defu'\nimport type { MaizzleConfig } from './types/index.ts'\n\nexport interface BuildResult {\n files: string[]\n config: MaizzleConfig\n}\n\n/**\n * Build all SFC email templates to HTML files.\n *\n * Creates a single Renderer instance, then loops through each template\n * calling render → transformers → write to disk.\n *\n * Pass a `Partial<MaizzleConfig>` to override config inline, or a string\n * to load config from a specific file path. Omit to load `maizzle.config`\n * from the working directory.\n */\nexport async function build(configInput?: Partial<MaizzleConfig> | string): Promise<BuildResult> {\n const start = Date.now()\n const spinner = ora({ text: 'Building templates...', spinner: 'circleHalves' }).start()\n\n const config = await resolveConfig(configInput)\n\n const events = new EventManager()\n events.registerConfig(config)\n await events.fireBeforeCreate({ config })\n\n const outputPath = resolve(config.output?.path ?? 'dist')\n const outputExtension = config.output?.extension ?? 'html'\n\n const contentPatterns = config.content ?? ['emails/**/*.vue']\n const contentBase = computeContentBase(contentPatterns)\n const templateFiles = await glob(contentPatterns)\n\n if (templateFiles.length === 0) {\n spinner.succeed('No templates found')\n return { files: [], config }\n }\n\n // Clear the output directory before writing fresh output\n if (existsSync(outputPath)) {\n rmSync(outputPath, { recursive: true, force: true })\n }\n\n const renderer = await createRenderer({ markdown: config.markdown, root: config.root, componentDirs: normalizeComponentSources(config.components?.source, process.cwd()), vite: config.vite })\n const outputFiles: string[] = []\n\n try {\n for (const templatePath of templateFiles) {\n const absolutePath = resolve(templatePath)\n const parsedPath = parsePath(absolutePath)\n const template = { source: readFileSync(absolutePath, 'utf-8'), path: parsedPath }\n\n _setCurrentTemplate(parsedPath)\n\n try {\n await events.fireBeforeRender({ config, template })\n\n const rendered = await renderer.render(absolutePath, config)\n\n /**\n * Register SFC event handlers collected during render so they take\n * part in the post-render events (afterRender / afterTransform).\n * They're cleared at the end of the iteration so they don't\n * leak into the next template.\n */\n for (const { name, handler } of rendered.sfcEventHandlers) {\n events.on(name, handler)\n }\n\n let html = await events.fireAfterRender({ config, template, html: rendered.html })\n\n /**\n * Use the per-template merged config (from defineConfig() in the SFC) so\n * that template-level overrides like css.safe: false are respected\n * by transformers.\n */\n const templateConfig = rendered.templateConfig\n\n const doctype = rendered.doctype ?? templateConfig.doctype ?? '<!DOCTYPE html>'\n\n if (templateConfig.useTransformers !== false) {\n html = await runTransformers(html, templateConfig, absolutePath, doctype, rendered.tailwindBlocks)\n }\n\n html = await events.fireAfterTransform({ config, template, html })\n if (doctype) html = `${doctype}\\n${html}`\n\n const htmlOut = stripForHtml(html)\n const sfcOutputPath = rendered.outputPath\n let outputFilePath: string\n\n if (sfcOutputPath) {\n const parsed = parsePath(resolve(sfcOutputPath))\n const ext = parsed.ext ? parsed.ext.slice(1) : outputExtension\n outputFilePath = join(parsed.dir, `${parsed.name}.${ext}`)\n } else {\n outputFilePath = resolveOutputPath(templatePath, outputPath, outputExtension, contentBase)\n }\n\n mkdirSync(dirname(outputFilePath), { recursive: true })\n writeFileSync(outputFilePath, htmlOut)\n outputFiles.push(outputFilePath)\n\n // Generate plaintext version if configured\n const globalPlaintext = templateConfig.plaintext\n const sfcPlaintext = rendered.plaintext\n\n if (globalPlaintext || sfcPlaintext) {\n const globalCfg = typeof globalPlaintext === 'object' ? globalPlaintext : {}\n const stripOptions = defu(sfcPlaintext?.options, globalCfg.options)\n const plaintext = createPlaintext(stripForPlaintext(html), stripOptions)\n const ptExtension = sfcPlaintext?.extension ?? globalCfg.extension ?? 'txt'\n\n let ptOutputPath: string\n\n if (sfcPlaintext?.destination) {\n const name = basename(templatePath).replace(/\\.(vue|md)$/, '')\n ptOutputPath = join(resolve(sfcPlaintext.destination), `${name}.${ptExtension}`)\n } else if (sfcOutputPath) {\n const parsed = parsePath(outputFilePath)\n ptOutputPath = join(parsed.dir, `${parsed.name}.${ptExtension}`)\n } else if (globalCfg.destination) {\n ptOutputPath = resolveOutputPath(templatePath, resolve(globalCfg.destination), ptExtension, contentBase)\n } else {\n ptOutputPath = resolveOutputPath(templatePath, outputPath, ptExtension, contentBase)\n }\n\n mkdirSync(dirname(ptOutputPath), { recursive: true })\n writeFileSync(ptOutputPath, plaintext)\n }\n } finally {\n _setCurrentTemplate(undefined)\n events.clearSfcHandlers()\n }\n }\n\n await copyStatic(config, outputPath)\n await events.fireAfterBuild({ files: outputFiles, config })\n } finally {\n await renderer.close()\n }\n\n const duration = ((Date.now() - start) / 1000).toFixed(2)\n const count = outputFiles.length\n spinner.stopAndPersist({\n symbol: '✅',\n text: `Built ${count} template${count !== 1 ? 's' : ''} in ${duration}s`,\n })\n\n return { files: outputFiles, config }\n}\n\n/**\n * Extract the static (non-glob) prefix from content patterns.\n *\n * For example, `['/abs/path/emails/**\\/*.vue']` → `'/abs/path/emails'`\n *\n * This is used to strip the content base from template paths\n * so the output preserves only the subdirectory structure.\n */\nfunction computeContentBase(patterns: string[]): string {\n // Use the first non-negated pattern\n const pattern = patterns.find(p => !p.startsWith('!')) ?? patterns[0]\n\n // Split on first glob character (* { ? [) and take the directory part\n const staticPart = pattern.split(/[*{?[]/)[0]\n\n // Ensure we have a clean directory path (not a partial segment)\n return resolve(staticPart.endsWith('/') ? staticPart : dirname(staticPart))\n}\n\nfunction resolveOutputPath(templatePath: string, outputDir: string, extension: string, contentBase: string): string {\n const name = basename(templatePath).replace(/\\.(vue|md)$/, '')\n const absTemplate = resolve(templatePath)\n const rel = relative(contentBase, dirname(absTemplate))\n\n return join(outputDir, rel, `${name}.${extension}`)\n}\n\nasync function copyStatic(config: MaizzleConfig, outputPath: string): Promise<void> {\n const sources = config.static?.source ?? ['public/**/*.*']\n const destination = config.static?.destination ?? 'public'\n\n const files = await glob(sources)\n\n for (const file of files) {\n const destPath = join(outputPath, destination, relative(dirname(sources[0]).replace(/\\*.*$/, ''), file))\n const destDir = dirname(destPath)\n\n if (!existsSync(destDir)) {\n mkdirSync(destDir, { recursive: true })\n }\n\n cpSync(file, destPath)\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"build.js","names":[],"sources":["../src/build.ts"],"sourcesContent":["import { mkdirSync, cpSync, existsSync, rmSync } from 'node:fs'\nimport { resolve, dirname, relative, join } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { availableParallelism } from 'node:os'\nimport { glob } from 'tinyglobby'\nimport ora from 'ora'\nimport { resolveConfig } from './config/index.ts'\nimport { EventManager } from './events/index.ts'\nimport { createRenderer } from './render/createRenderer.ts'\nimport { normalizeComponentSources } from './utils/componentSources.ts'\nimport { buildTemplate, computeContentBase } from './render/buildTemplate.ts'\nimport type { MaizzleConfig } from './types/index.ts'\n\nexport interface BuildResult {\n files: string[]\n config: MaizzleConfig\n}\n\n/**\n * Build all SFC email templates to HTML files.\n *\n * Creates a single Renderer instance, then loops through each template\n * calling render → transformers → write to disk.\n *\n * Pass a `Partial<MaizzleConfig>` to override config inline, or a string\n * to load config from a specific file path. Omit to load `maizzle.config`\n * from the working directory.\n */\nexport async function build(configInput?: Partial<MaizzleConfig> | string): Promise<BuildResult> {\n const start = Date.now()\n const spinner = ora({ text: 'Building templates...', spinner: 'circleHalves' }).start()\n\n const config = await resolveConfig(configInput)\n\n const events = new EventManager()\n events.registerConfig(config)\n await events.fireBeforeCreate({ config })\n\n const outputPath = resolve(config.output?.path ?? 'dist')\n const outputExtension = config.output?.extension ?? 'html'\n\n const contentPatterns = config.content ?? ['emails/**/*.vue']\n const contentBase = computeContentBase(contentPatterns)\n const templateFiles = await glob(contentPatterns)\n\n if (templateFiles.length === 0) {\n spinner.succeed('No templates found')\n return { files: [], config }\n }\n\n // Clear the output directory before writing fresh output\n if (existsSync(outputPath)) {\n rmSync(outputPath, { recursive: true, force: true })\n }\n\n const outputFiles: string[] = []\n let droppedAfterBuild = 0\n\n const parallel = resolveParallel(config, templateFiles.length, configInput)\n\n if (parallel.enabled) {\n spinner.text = `Building ${templateFiles.length} templates across ${parallel.workers} workers...`\n\n const result = await runParallelBuild({\n templateFiles,\n workers: parallel.workers,\n config,\n configInput,\n outputPath,\n outputExtension,\n contentBase,\n })\n\n outputFiles.push(...result.files)\n droppedAfterBuild = result.sfcAfterBuildCount\n\n await copyStatic(config, outputPath)\n await events.fireAfterBuild({ files: outputFiles, config })\n } else {\n const renderer = await createRenderer({ markdown: config.markdown, root: config.root, componentDirs: normalizeComponentSources(config.components?.source, process.cwd()), vite: config.vite })\n\n try {\n for (const templatePath of templateFiles) {\n const { files } = await buildTemplate(templatePath, { config, renderer, events, outputPath, outputExtension, contentBase })\n outputFiles.push(...files)\n }\n\n await copyStatic(config, outputPath)\n await events.fireAfterBuild({ files: outputFiles, config })\n } finally {\n await renderer.close()\n }\n }\n\n if (droppedAfterBuild > 0) {\n console.warn(`[maizzle] Skipped ${droppedAfterBuild} SFC-registered afterBuild handler(s): afterBuild can't run inside a parallel build worker. Move build-completion logic to the config's afterBuild hook.`)\n }\n\n const duration = ((Date.now() - start) / 1000).toFixed(2)\n const count = outputFiles.length\n spinner.stopAndPersist({\n symbol: '✅',\n text: `Built ${count} template${count !== 1 ? 's' : ''} in ${duration}s`,\n })\n\n return { files: outputFiles, config }\n}\n\n/**\n * Default template count above which parallel build turns on. Benchmarked\n * crossover (with the worker cap below) is ~25 templates; 50 leaves margin so\n * auto-parallel only kicks in where it's a reliable win across hardware.\n * Override per project with `parallel: { threshold }`.\n */\nconst DEFAULT_PARALLEL_THRESHOLD = 50\n\n/**\n * Default worker cap. Each worker runs a full Vite SSR renderer, so startup +\n * contention outweighs added parallelism past ~8 — benchmarks showed 8 beating\n * 12/16/23 at every size. Override with `parallel: { workers }`.\n */\nconst DEFAULT_MAX_WORKERS = 8\n\n/**\n * Decide whether to build in parallel and with how many workers.\n *\n * `config.parallel`:\n * - omitted → parallel when `count > 50`, min(CPU count − 1, 8) workers\n * - `true` → always parallel (ignores threshold), default workers\n * - `false` → always sequential\n * - `{ workers, threshold }` → parallel when `count > threshold` (default 50),\n * using `workers` threads (default min(CPU count − 1, 8))\n *\n * Workers reload the config file to recover function hooks, so parallel only\n * applies to file-based configs (a path or the default cwd config) — an inline\n * config object has no file to reload and always builds sequentially.\n */\nexport function resolveParallel(\n config: MaizzleConfig,\n count: number,\n configInput: Partial<MaizzleConfig> | string | undefined,\n): { enabled: boolean; workers: number } {\n const setting = config.parallel\n if (setting === false) return { enabled: false, workers: 0 }\n\n const fileBased = typeof configInput === 'string' || configInput == null\n if (!fileBased) return { enabled: false, workers: 0 }\n\n const cpus = availableParallelism()\n const defaultWorkers = Math.min(Math.max(1, cpus - 1), DEFAULT_MAX_WORKERS)\n\n let maxWorkers = defaultWorkers\n let threshold = DEFAULT_PARALLEL_THRESHOLD\n // `true` opts in regardless of count; object/omitted stay threshold-gated.\n const ignoreThreshold = setting === true\n\n if (typeof setting === 'object' && setting !== null) {\n if (typeof setting.workers === 'number' && setting.workers > 0) maxWorkers = Math.floor(setting.workers)\n if (typeof setting.threshold === 'number' && setting.threshold >= 0) threshold = Math.floor(setting.threshold)\n }\n\n if (!ignoreThreshold && count <= threshold) return { enabled: false, workers: 0 }\n\n const workers = Math.min(maxWorkers, count)\n return { enabled: workers >= 2 && count >= 2, workers }\n}\n\n/**\n * Run the build across worker threads. Each worker reloads the config (for its\n * function hooks), builds its batch via the same `buildTemplate` as the\n * sequential path, and returns the files it wrote. beforeCreate/afterBuild stay\n * on the main thread (handled by the caller).\n */\nasync function runParallelBuild(opts: {\n templateFiles: string[]\n workers: number\n config: MaizzleConfig\n configInput: Partial<MaizzleConfig> | string | undefined\n outputPath: string\n outputExtension: string\n contentBase: string\n}): Promise<{ files: string[]; sfcAfterBuildCount: number }> {\n const { templateFiles, workers, config, configInput, outputPath, outputExtension, contentBase } = opts\n\n const { default: Tinypool } = await import('tinypool')\n const workerPath = resolve(dirname(fileURLToPath(import.meta.url)), 'render/parallel/worker.mjs')\n\n const configPath = typeof configInput === 'string' ? configInput : undefined\n // Serializable snapshot of the post-beforeCreate config (functions dropped).\n const configData = JSON.parse(JSON.stringify(config)) as Partial<MaizzleConfig>\n\n const batches = shardEvenly(templateFiles, workers)\n\n const pool = new Tinypool({ filename: workerPath, minThreads: batches.length, maxThreads: batches.length })\n\n try {\n const results = await Promise.all(\n batches.map(templatePaths => pool.run({\n templatePaths,\n configPath,\n configData,\n outputPath,\n outputExtension,\n contentBase,\n })),\n )\n\n return {\n files: results.flatMap(r => r.files),\n sfcAfterBuildCount: results.reduce((n, r) => n + r.sfcAfterBuildCount, 0),\n }\n } finally {\n await pool.destroy()\n }\n}\n\n/** Round-robin items into up to `buckets` non-empty groups for even balance. */\nfunction shardEvenly<T>(items: T[], buckets: number): T[][] {\n const out: T[][] = Array.from({ length: buckets }, () => [])\n items.forEach((item, i) => out[i % buckets].push(item))\n return out.filter(b => b.length > 0)\n}\n\nasync function copyStatic(config: MaizzleConfig, outputPath: string): Promise<void> {\n const sources = config.static?.source ?? ['public/**/*.*']\n const destination = config.static?.destination ?? 'public'\n\n const files = await glob(sources)\n\n for (const file of files) {\n const destPath = join(outputPath, destination, relative(dirname(sources[0]).replace(/\\*.*$/, ''), file))\n const destDir = dirname(destPath)\n\n if (!existsSync(destDir)) {\n mkdirSync(destDir, { recursive: true })\n }\n\n cpSync(file, destPath)\n }\n}\n"],"mappings":"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"}
|
package/dist/components/Body.vue
CHANGED
|
@@ -85,6 +85,17 @@ const outlookFallback = useOutlookFallback(props.outlookFallback)
|
|
|
85
85
|
|
|
86
86
|
const htmlLang = inject<string>('htmlLang', 'en')
|
|
87
87
|
|
|
88
|
+
const msoBody = `<!--[if mso]>
|
|
89
|
+
<xml>
|
|
90
|
+
<o:OfficeDocumentSettings>
|
|
91
|
+
<o:PixelsPerInch>96</o:PixelsPerInch>
|
|
92
|
+
</o:OfficeDocumentSettings>
|
|
93
|
+
<w:WordDocument>
|
|
94
|
+
<w:DontUseAdvancedTypographyReadingMail />
|
|
95
|
+
</w:WordDocument>
|
|
96
|
+
</xml>
|
|
97
|
+
<![endif]-->`
|
|
98
|
+
|
|
88
99
|
const render = () => {
|
|
89
100
|
const extraAttrs = Object.entries(attrs)
|
|
90
101
|
.map(([key, value]) => value === true ? key : `${key}="${value}"`)
|
|
@@ -115,6 +126,7 @@ const render = () => {
|
|
|
115
126
|
|
|
116
127
|
return [
|
|
117
128
|
createStaticVNode(`<body ${parts.join(' ')}>`, 1),
|
|
129
|
+
outlookFallback ? createStaticVNode(`<span style="display: none">${msoBody}</span>`, 1) : null,
|
|
118
130
|
createStaticVNode(`<div ${articleParts}>`, 1),
|
|
119
131
|
slots.default?.(),
|
|
120
132
|
createStaticVNode('</div>', 1),
|
|
@@ -129,48 +129,35 @@ const alignClass = computed(() => props.align ? ({
|
|
|
129
129
|
right: 'text-right',
|
|
130
130
|
})[props.align] || '' : '')
|
|
131
131
|
|
|
132
|
-
const
|
|
132
|
+
const baseClasses = computed(() => {
|
|
133
133
|
if (props.variant === 'link') {
|
|
134
|
-
return 'text-
|
|
134
|
+
return 'no-underline text-gray-950'
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
-
const
|
|
138
|
-
'
|
|
139
|
-
'
|
|
140
|
-
'
|
|
141
|
-
'
|
|
142
|
-
'
|
|
143
|
-
'
|
|
137
|
+
const classes = [
|
|
138
|
+
'inline-block',
|
|
139
|
+
'no-underline',
|
|
140
|
+
'px-6',
|
|
141
|
+
'py-4',
|
|
142
|
+
'text-base',
|
|
143
|
+
'leading-none',
|
|
144
|
+
'rounded',
|
|
144
145
|
]
|
|
145
146
|
|
|
146
147
|
if (props.variant === 'outline') {
|
|
147
|
-
|
|
148
|
-
'background-color: transparent;',
|
|
149
|
-
'border: 1px solid #4338ca;',
|
|
150
|
-
'color: #4338ca;',
|
|
151
|
-
)
|
|
148
|
+
classes.push('bg-transparent', 'border', 'border-solid', 'border-indigo-700', 'text-indigo-700')
|
|
152
149
|
} else if (props.variant === 'ghost') {
|
|
153
|
-
|
|
154
|
-
'background-color: transparent;',
|
|
155
|
-
'color: #4338ca;',
|
|
156
|
-
)
|
|
150
|
+
classes.push('bg-transparent', 'text-indigo-700', 'hover:bg-indigo-50')
|
|
157
151
|
} else {
|
|
158
|
-
|
|
159
|
-
'background-color: #4338ca;',
|
|
160
|
-
'color: #fffffe;',
|
|
161
|
-
)
|
|
152
|
+
classes.push('bg-indigo-700', 'text-white')
|
|
162
153
|
}
|
|
163
154
|
|
|
164
|
-
return
|
|
155
|
+
return classes.join(' ')
|
|
165
156
|
})
|
|
166
157
|
|
|
167
158
|
const isLink = computed(() => props.variant === 'link')
|
|
168
159
|
|
|
169
|
-
const
|
|
170
|
-
props.variant === 'ghost' ? 'hover:bg-indigo-50' : '',
|
|
171
|
-
)
|
|
172
|
-
|
|
173
|
-
const mergedClass = computed(() => twMerge(variantClasses.value, attrs.class as string))
|
|
160
|
+
const mergedClass = computed(() => twMerge(baseClasses.value, attrs.class as string))
|
|
174
161
|
|
|
175
162
|
const textSpanStyle = computed(() =>
|
|
176
163
|
outlookFallback ? `mso-text-raise: ${props.msoPt};` : undefined,
|
|
@@ -209,7 +196,7 @@ const MsoIconGap = () => createStaticVNode(
|
|
|
209
196
|
<a
|
|
210
197
|
v-bind="{ ...$attrs, class: undefined, style: undefined }"
|
|
211
198
|
:href="href"
|
|
212
|
-
:style="
|
|
199
|
+
:style="$attrs.style as any"
|
|
213
200
|
:class="mergedClass"
|
|
214
201
|
>
|
|
215
202
|
<template v-if="!isLink">
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { createStaticVNode, type PropType } from 'vue'
|
|
3
3
|
import { twMerge } from 'tailwind-merge'
|
|
4
4
|
import { codeToHtml, getSingletonHighlighter, type BundledLanguage, type BundledTheme } from 'shiki'
|
|
5
|
+
import { buildCodeBlock, codeBlockPreClass } from './utils'
|
|
5
6
|
|
|
6
7
|
export default {
|
|
7
8
|
props: {
|
|
@@ -57,11 +58,11 @@ export default {
|
|
|
57
58
|
.replace(/^<pre[^>]*><code>/, '')
|
|
58
59
|
.replace(/<\/code><\/pre>$/, '')
|
|
59
60
|
|
|
60
|
-
const
|
|
61
|
-
const
|
|
62
|
-
const
|
|
61
|
+
const preClass = twMerge(codeBlockPreClass(bg), attrs.class as string)
|
|
62
|
+
const tdClass = twMerge(`bg-[${bg}]`, props.tdClass)
|
|
63
|
+
const styleAttr = attrs.style ? ` style="${attrs.style}"` : ''
|
|
63
64
|
|
|
64
|
-
const html =
|
|
65
|
+
const html = buildCodeBlock(codeContent, bg, { preClass, tdClass, styleAttr })
|
|
65
66
|
|
|
66
67
|
return () => createStaticVNode(html, 1)
|
|
67
68
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { createStaticVNode, type PropType } from 'vue'
|
|
3
|
+
import { twMerge } from 'tailwind-merge'
|
|
3
4
|
import { codeToHtml, getSingletonHighlighter, type BundledLanguage, type BundledTheme } from 'shiki'
|
|
4
5
|
|
|
5
6
|
export default {
|
|
@@ -50,8 +51,6 @@ export default {
|
|
|
50
51
|
return () => createStaticVNode('', 0)
|
|
51
52
|
}
|
|
52
53
|
|
|
53
|
-
const classes = attrs.class ? ` class="${attrs.class}"` : ''
|
|
54
|
-
|
|
55
54
|
if (props.theme) {
|
|
56
55
|
const highlighted = await codeToHtml(source, {
|
|
57
56
|
lang: props.language,
|
|
@@ -95,15 +94,17 @@ export default {
|
|
|
95
94
|
.replace(/</g, '§MZLT§')
|
|
96
95
|
.replace(/>/g, '§MZGT§')
|
|
97
96
|
|
|
98
|
-
const
|
|
99
|
-
const
|
|
97
|
+
const base = `bg-[${bg}] rounded-md py-0.5 px-1.5 text-[11px]`
|
|
98
|
+
const merged = twMerge(base, (attrs.class as string) ?? '')
|
|
99
|
+
const styleAttr = attrs.style ? ` style="${attrs.style}"` : ''
|
|
100
100
|
|
|
101
|
-
const html = `<code${
|
|
101
|
+
const html = `<code class="${merged}"${styleAttr} data-minify-inline>${escaped}</code>`
|
|
102
102
|
return () => createStaticVNode(html, 1)
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
-
const
|
|
106
|
-
const
|
|
105
|
+
const base = 'whitespace-normal rounded-md [border:1px_solid_#d1d5db] bg-gray-100 py-0.5 px-1.5 text-[11px] text-inherit'
|
|
106
|
+
const merged = twMerge(base, (attrs.class as string) ?? '')
|
|
107
|
+
const styleAttr = attrs.style ? ` style="${attrs.style}"` : ''
|
|
107
108
|
|
|
108
109
|
const escaped = source
|
|
109
110
|
.replace(/&/g, '&')
|
|
@@ -111,7 +112,7 @@ export default {
|
|
|
111
112
|
.replace(/>/g, '>')
|
|
112
113
|
.replace(/"/g, '"')
|
|
113
114
|
|
|
114
|
-
const html = `<code${
|
|
115
|
+
const html = `<code class="${merged}"${styleAttr}>${escaped}</code>`
|
|
115
116
|
|
|
116
117
|
return () => createStaticVNode(html, 1)
|
|
117
118
|
}
|
|
@@ -70,14 +70,27 @@ const msoWidth = computed(() => {
|
|
|
70
70
|
* `display: inline-block` would silently shadow a class like
|
|
71
71
|
* `inline-table` during CSS inlining; routing both through twMerge lets
|
|
72
72
|
* the user's utility cleanly replace ours instead of being dropped.
|
|
73
|
+
*
|
|
74
|
+
* When `width` is set as a prop the resolved pixel value also goes
|
|
75
|
+
* through the class list (`min-w-[Npx]`) so it dedupes against the
|
|
76
|
+
* user's `min-w-*` utility. The marker path (no prop) has to stay
|
|
77
|
+
* inline because the placeholder string is replaced post-render and
|
|
78
|
+
* Tailwind's content scanner can't compile a class whose value is
|
|
79
|
+
* still a marker.
|
|
73
80
|
*/
|
|
74
|
-
const baseClass = 'inline-block
|
|
75
|
-
const mergedClass = computed(() =>
|
|
81
|
+
const baseClass = 'inline-block text-[medium]'
|
|
82
|
+
const mergedClass = computed(() => {
|
|
83
|
+
const parts = [baseClass]
|
|
84
|
+
if (props.width != null) parts.push(`min-w-[${normalizeToPixels(props.width)}]`)
|
|
85
|
+
return twMerge(parts.join(' '), (attrs.class as string) ?? '')
|
|
86
|
+
})
|
|
76
87
|
|
|
77
|
-
const styles = computed(() =>
|
|
88
|
+
const styles = computed(() =>
|
|
89
|
+
props.width != null ? undefined : `min-width: ${minWidth.value};`
|
|
90
|
+
)
|
|
78
91
|
|
|
79
92
|
const tdStyle = computed(() => {
|
|
80
|
-
const parts = [`width: ${msoWidth.value}
|
|
93
|
+
const parts = [`width: ${msoWidth.value}`]
|
|
81
94
|
if (useMarker) parts.push(`__MAIZZLE_COLTDX_${colId}__`)
|
|
82
95
|
if (props.msoStyle) parts.push(props.msoStyle)
|
|
83
96
|
return parts.join('; ')
|
|
@@ -60,18 +60,15 @@ const useMarker = outlookFallback && props.width == null
|
|
|
60
60
|
const msoId = useMarker ? nextId('c') : null
|
|
61
61
|
const tdId = outlookFallback ? nextId('ct') : null
|
|
62
62
|
|
|
63
|
-
const styles = computed(() => {
|
|
64
|
-
if (props.width == null) return undefined
|
|
65
|
-
return `max-width: ${normalizeToPixels(props.width)}; margin: 0 auto;`
|
|
66
|
-
})
|
|
67
|
-
|
|
68
63
|
const mergedClass = computed(() => {
|
|
69
|
-
if (props.width != null) return attrs.class as string | undefined
|
|
70
64
|
const userClass = (attrs.class as string) ?? ''
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
65
|
+
const parts: string[] = ['m-0', 'mx-auto']
|
|
66
|
+
if (props.width != null) {
|
|
67
|
+
parts.push(`max-w-[${normalizeToPixels(props.width)}]`)
|
|
68
|
+
} else if (!hasWidthUtility(userClass)) {
|
|
69
|
+
parts.push('max-w-150')
|
|
70
|
+
}
|
|
71
|
+
return twMerge(parts.join(' '), userClass)
|
|
75
72
|
})
|
|
76
73
|
|
|
77
74
|
const msoWidth = computed(() => {
|
|
@@ -101,7 +98,6 @@ const MsoAfter = () => createStaticVNode(
|
|
|
101
98
|
<div
|
|
102
99
|
v-bind="{ ...attrs, class: undefined }"
|
|
103
100
|
:class="mergedClass"
|
|
104
|
-
:style="styles"
|
|
105
101
|
:data-maizzle-msow-id="msoId"
|
|
106
102
|
:data-maizzle-cw="colWidthSource"
|
|
107
103
|
:data-maizzle-mso-td-id="tdId"
|
package/dist/components/Hr.vue
CHANGED
|
@@ -15,7 +15,7 @@ const mergedClass = computed(() => {
|
|
|
15
15
|
const userHeight = heights.length ? heights[heights.length - 1][1] : null
|
|
16
16
|
const userHasLeading = LEADING_RE.test(userClass)
|
|
17
17
|
|
|
18
|
-
const defaults = ['my-6', 'bg-
|
|
18
|
+
const defaults = ['my-6', 'bg-gray-300']
|
|
19
19
|
if (!userHeight) defaults.push('h-px')
|
|
20
20
|
if (!userHasLeading && !userHeight) defaults.push('leading-px')
|
|
21
21
|
|