@maizzle/framework 5.0.0-beta.3 → 5.0.0-beta.30
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/package.json +32 -30
- package/src/commands/build.js +146 -73
- package/src/generators/plaintext.js +26 -23
- package/src/generators/render.js +15 -14
- package/src/posthtml/defaultComponentsConfig.js +2 -2
- package/src/posthtml/defaultConfig.js +13 -3
- package/src/posthtml/index.js +38 -9
- package/src/posthtml/plugins/envAttributes.js +32 -0
- package/src/posthtml/plugins/envTags.js +33 -0
- package/src/server/index.js +159 -96
- package/src/server/routes/index.js +51 -13
- package/src/server/views/404.html +59 -0
- package/src/server/views/index.html +162 -14
- package/src/transformers/addAttributes.js +2 -3
- package/src/transformers/attributeToStyle.js +1 -3
- package/src/transformers/baseUrl.js +6 -6
- package/src/transformers/comb.js +7 -6
- package/src/transformers/core.js +12 -0
- package/src/transformers/filters/index.js +1 -2
- package/src/transformers/index.js +56 -67
- package/src/transformers/inline.js +53 -16
- package/src/transformers/markdown.js +14 -7
- package/src/transformers/minify.js +4 -3
- package/src/transformers/posthtmlMso.js +1 -3
- package/src/transformers/prettify.js +4 -3
- package/src/transformers/preventWidows.js +15 -65
- package/src/transformers/removeAttributes.js +3 -4
- package/src/transformers/replaceStrings.js +7 -5
- package/src/transformers/safeClassNames.js +1 -2
- package/src/transformers/shorthandCss.js +1 -3
- package/src/transformers/sixHex.js +1 -3
- package/src/transformers/template.js +26 -0
- package/src/transformers/urlParameters.js +1 -3
- package/src/transformers/useAttributeSizes.js +1 -3
- package/src/utils/string.js +89 -0
- package/types/build.d.ts +53 -24
- package/types/config.d.ts +60 -49
- package/types/css/inline.d.ts +2 -0
- package/types/css/purge.d.ts +1 -1
- package/types/events.d.ts +153 -5
- package/types/index.d.ts +4 -3
- package/types/posthtml.d.ts +3 -3
- package/types/urlParameters.d.ts +1 -1
- package/types/widowWords.d.ts +16 -36
- package/types/components.d.ts +0 -195
- package/types/expressions.d.ts +0 -100
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maizzle/framework",
|
|
3
|
-
"version": "5.0.0-beta.
|
|
3
|
+
"version": "5.0.0-beta.30",
|
|
4
4
|
"description": "Maizzle is a framework that helps you quickly build HTML emails with Tailwind CSS.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -48,58 +48,60 @@
|
|
|
48
48
|
"html-emails"
|
|
49
49
|
],
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@csstools/css-calc": "^
|
|
51
|
+
"@csstools/css-calc": "^2.0.1",
|
|
52
52
|
"@maizzle/cli": "next",
|
|
53
|
-
"cheerio": "^1.0.0
|
|
53
|
+
"cheerio": "^1.0.0",
|
|
54
54
|
"chokidar": "^3.6.0",
|
|
55
55
|
"cli-table3": "^0.6.5",
|
|
56
56
|
"color-shorthand-hex-to-six-digit": "^5.0.16",
|
|
57
57
|
"defu": "^6.1.4",
|
|
58
58
|
"email-comb": "^7.0.21",
|
|
59
|
-
"express": "^4.
|
|
59
|
+
"express": "^4.21.0",
|
|
60
60
|
"fast-glob": "^3.3.2",
|
|
61
61
|
"gray-matter": "^4.0.3",
|
|
62
|
-
"html-crush": "^6.0.
|
|
62
|
+
"html-crush": "^6.0.19",
|
|
63
63
|
"is-url-superb": "^6.1.0",
|
|
64
64
|
"istextorbinary": "^9.5.0",
|
|
65
|
-
"juice": "^
|
|
65
|
+
"juice": "^11.0.0",
|
|
66
66
|
"lodash-es": "^4.17.21",
|
|
67
|
-
"morphdom": "^2.7.
|
|
68
|
-
"ora": "^8.0
|
|
67
|
+
"morphdom": "^2.7.4",
|
|
68
|
+
"ora": "^8.1.0",
|
|
69
69
|
"pathe": "^1.1.2",
|
|
70
|
-
"postcss": "^8.4.
|
|
71
|
-
"postcss-custom-properties": "^
|
|
70
|
+
"postcss": "^8.4.49",
|
|
71
|
+
"postcss-custom-properties": "^14.0.4",
|
|
72
72
|
"postcss-import": "^16.1.0",
|
|
73
73
|
"postcss-safe-parser": "^7.0.0",
|
|
74
74
|
"posthtml": "^0.16.6",
|
|
75
|
-
"posthtml-attrs-parser": "^1.1.
|
|
76
|
-
"posthtml-base-url": "^3.1.
|
|
77
|
-
"posthtml-component": "^
|
|
78
|
-
"posthtml-content": "^2.0
|
|
79
|
-
"posthtml-
|
|
80
|
-
"posthtml-
|
|
81
|
-
"posthtml-
|
|
82
|
-
"posthtml-
|
|
83
|
-
"posthtml-
|
|
84
|
-
"posthtml-
|
|
75
|
+
"posthtml-attrs-parser": "^1.1.1",
|
|
76
|
+
"posthtml-base-url": "^3.1.4",
|
|
77
|
+
"posthtml-component": "^2.0.0",
|
|
78
|
+
"posthtml-content": "^2.1.0",
|
|
79
|
+
"posthtml-expressions": "^1.11.4",
|
|
80
|
+
"posthtml-extra-attributes": "^3.1.0",
|
|
81
|
+
"posthtml-fetch": "^4.0.0",
|
|
82
|
+
"posthtml-markdownit": "^3.1.0",
|
|
83
|
+
"posthtml-mso": "^3.1.0",
|
|
84
|
+
"posthtml-parser": "^0.12.1",
|
|
85
|
+
"posthtml-postcss": "^1.0.2",
|
|
86
|
+
"posthtml-postcss-merge-longhand": "^3.1.2",
|
|
85
87
|
"posthtml-render": "^3.0.0",
|
|
86
|
-
"posthtml-safe-class-names": "^4.0
|
|
87
|
-
"posthtml-url-parameters": "^3.
|
|
88
|
+
"posthtml-safe-class-names": "^4.1.0",
|
|
89
|
+
"posthtml-url-parameters": "^3.1.0",
|
|
90
|
+
"posthtml-widows": "^1.0.0",
|
|
88
91
|
"pretty": "^2.0.0",
|
|
89
|
-
"string-remove-widows": "^4.0.22",
|
|
90
92
|
"string-strip-html": "^13.4.8",
|
|
91
|
-
"tailwindcss": "^3.4.
|
|
92
|
-
"ws": "^8.
|
|
93
|
+
"tailwindcss": "^3.4.15",
|
|
94
|
+
"ws": "^8.18.0"
|
|
93
95
|
},
|
|
94
96
|
"devDependencies": {
|
|
95
|
-
"@biomejs/biome": "
|
|
97
|
+
"@biomejs/biome": "1.9.2",
|
|
96
98
|
"@types/js-beautify": "^1.14.3",
|
|
97
|
-
"@types/markdown-it": "^14.1.
|
|
98
|
-
"@vitest/coverage-v8": "^2.
|
|
99
|
+
"@types/markdown-it": "^14.1.2",
|
|
100
|
+
"@vitest/coverage-v8": "^2.1.5",
|
|
99
101
|
"supertest": "^7.0.0",
|
|
100
|
-
"vitest": "^2.
|
|
102
|
+
"vitest": "^2.1.5"
|
|
101
103
|
},
|
|
102
104
|
"engines": {
|
|
103
|
-
"node": ">=18.
|
|
105
|
+
"node": ">=18.20"
|
|
104
106
|
}
|
|
105
107
|
}
|
package/src/commands/build.js
CHANGED
|
@@ -4,7 +4,8 @@ import {
|
|
|
4
4
|
copyFile,
|
|
5
5
|
lstat,
|
|
6
6
|
mkdir,
|
|
7
|
-
rm
|
|
7
|
+
rm,
|
|
8
|
+
cp,
|
|
8
9
|
} from 'node:fs/promises'
|
|
9
10
|
import path from 'pathe'
|
|
10
11
|
import fg from 'fast-glob'
|
|
@@ -12,22 +13,50 @@ import { defu as merge } from 'defu'
|
|
|
12
13
|
|
|
13
14
|
import get from 'lodash/get.js'
|
|
14
15
|
import isEmpty from 'lodash-es/isEmpty.js'
|
|
15
|
-
import { isBinary } from 'istextorbinary'
|
|
16
16
|
|
|
17
17
|
import ora from 'ora'
|
|
18
18
|
import pico from 'picocolors'
|
|
19
19
|
import cliTable from 'cli-table3'
|
|
20
20
|
|
|
21
21
|
import { render } from '../generators/render.js'
|
|
22
|
-
|
|
22
|
+
|
|
23
|
+
import {
|
|
24
|
+
formatTime,
|
|
25
|
+
getRootDirectories,
|
|
26
|
+
getFileExtensionsFromPattern,
|
|
27
|
+
} from '../utils/string.js'
|
|
28
|
+
|
|
23
29
|
import { getColorizedFileSize } from '../utils/node.js'
|
|
24
|
-
|
|
30
|
+
|
|
25
31
|
import {
|
|
26
32
|
generatePlaintext,
|
|
27
33
|
handlePlaintextTags,
|
|
28
34
|
writePlaintextFile
|
|
29
35
|
} from '../generators/plaintext.js'
|
|
30
36
|
|
|
37
|
+
import { readFileConfig } from '../utils/getConfigByFilePath.js'
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Ensures that a directory exists, creating it if needed.
|
|
41
|
+
*
|
|
42
|
+
* @param {string} filePath - The path to the file to check.
|
|
43
|
+
*/
|
|
44
|
+
async function ensureDirectoryExistence(filePath) {
|
|
45
|
+
const dirname = path.dirname(filePath)
|
|
46
|
+
await mkdir(dirname, { recursive: true })
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Copy a file from source to target.
|
|
51
|
+
*
|
|
52
|
+
* @param {string} source - The source file path.
|
|
53
|
+
* @param {string} target - The target file path.
|
|
54
|
+
*/
|
|
55
|
+
async function copyFileAsync(source, target) {
|
|
56
|
+
await ensureDirectoryExistence(target)
|
|
57
|
+
await copyFile(source, target)
|
|
58
|
+
}
|
|
59
|
+
|
|
31
60
|
/**
|
|
32
61
|
* Compile templates and output to the build directory.
|
|
33
62
|
* Returns a promise containing an object with files output and the config object.
|
|
@@ -41,7 +70,10 @@ export default async (config = {}) => {
|
|
|
41
70
|
try {
|
|
42
71
|
const startTime = Date.now()
|
|
43
72
|
|
|
44
|
-
|
|
73
|
+
/**
|
|
74
|
+
* Read the config file for this environment,
|
|
75
|
+
* merging it with the default config.
|
|
76
|
+
*/
|
|
45
77
|
config = await readFileConfig(config).catch(() => { throw new Error('Could not compute config') })
|
|
46
78
|
|
|
47
79
|
/**
|
|
@@ -72,9 +104,12 @@ export default async (config = {}) => {
|
|
|
72
104
|
head: ['File name', 'File size', 'Build time'].map(item => pico.bold(item)),
|
|
73
105
|
})
|
|
74
106
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
107
|
+
/**
|
|
108
|
+
* Check that templates to be built, actually exist
|
|
109
|
+
*/
|
|
110
|
+
const contentPaths = get(config, 'build.content', ['src/templates/**/*.html'])
|
|
111
|
+
|
|
112
|
+
const templateFolders = Array.isArray(contentPaths) ? contentPaths : [contentPaths]
|
|
78
113
|
const templatePaths = await fg.glob([...new Set(templateFolders)])
|
|
79
114
|
|
|
80
115
|
// If there are no templates to build, throw error
|
|
@@ -82,37 +117,86 @@ export default async (config = {}) => {
|
|
|
82
117
|
throw new Error(`No templates found in ${pico.inverse(templateFolders)}`)
|
|
83
118
|
}
|
|
84
119
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
120
|
+
/**
|
|
121
|
+
* Copy source directories to destination
|
|
122
|
+
*
|
|
123
|
+
* Copies each `build.content` path to the `build.output.path` directory.
|
|
124
|
+
*/
|
|
125
|
+
let from = get(config, 'build.output.from', ['src/templates', 'src'])
|
|
126
|
+
|
|
127
|
+
const globPathsToCopy = contentPaths.map(glob => {
|
|
128
|
+
// Keep negated paths as they are
|
|
129
|
+
if (glob.startsWith('!')) {
|
|
130
|
+
return glob
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Keep single-file sources as they are
|
|
134
|
+
if (!/\*/.test(glob)) {
|
|
135
|
+
return glob
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Update non-negated paths to target all files, avoiding duplication
|
|
139
|
+
return glob.replace(/\/\*\*\/\*\.\{.*?\}$|\/\*\*\/\*\.[^/]*$|\/*\.[^/]*$/, '/**/*')
|
|
89
140
|
})
|
|
90
141
|
|
|
142
|
+
try {
|
|
143
|
+
from = Array.isArray(from) ? from : [from]
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Copy files from source to destination
|
|
147
|
+
*
|
|
148
|
+
* The array/set conversion is to remove duplicates
|
|
149
|
+
*/
|
|
150
|
+
for (const file of await fg(Array.from(new Set(globPathsToCopy)))) {
|
|
151
|
+
let relativePath
|
|
152
|
+
for (const dir of from) {
|
|
153
|
+
if (file.startsWith(dir)) {
|
|
154
|
+
relativePath = path.relative(dir, file)
|
|
155
|
+
break
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
if (!relativePath) {
|
|
159
|
+
relativePath = path.relative('.', file)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const targetPath = path.join(buildOutputPath, relativePath)
|
|
163
|
+
await copyFileAsync(file, targetPath)
|
|
164
|
+
}
|
|
165
|
+
} catch (error) {
|
|
166
|
+
console.error('Error while processing pattern:', error)
|
|
167
|
+
}
|
|
168
|
+
|
|
91
169
|
/**
|
|
92
|
-
*
|
|
170
|
+
* Get a list of files to render, from the output directory
|
|
93
171
|
*
|
|
94
|
-
*
|
|
95
|
-
* to
|
|
96
|
-
* be copied directly to the output directory, just like the
|
|
97
|
-
* `build.static` folders.
|
|
172
|
+
* Uses all file extensions from non-negated glob paths in `build.content`
|
|
173
|
+
* to determine which files to render from the output directory.
|
|
98
174
|
*/
|
|
99
|
-
const
|
|
100
|
-
|
|
175
|
+
const outputExtensions = new Set()
|
|
176
|
+
|
|
177
|
+
for (const pattern of contentPaths) {
|
|
178
|
+
outputExtensions.add(...getFileExtensionsFromPattern(pattern))
|
|
179
|
+
}
|
|
101
180
|
|
|
102
181
|
/**
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
* Render each template and write the output to the output directory,
|
|
106
|
-
* preserving the relative path.
|
|
182
|
+
* Create a list of templates to compile
|
|
107
183
|
*/
|
|
108
|
-
|
|
109
|
-
|
|
184
|
+
const extensions = outputExtensions.size > 1
|
|
185
|
+
? `{${[...outputExtensions].join(',')}}`
|
|
186
|
+
: [...outputExtensions][0] || 'html'
|
|
110
187
|
|
|
111
|
-
|
|
112
|
-
|
|
188
|
+
const templatesToCompile = await fg.glob(
|
|
189
|
+
path.join(
|
|
190
|
+
buildOutputPath,
|
|
191
|
+
`**/*.${extensions}`
|
|
192
|
+
)
|
|
193
|
+
)
|
|
113
194
|
|
|
114
|
-
|
|
115
|
-
|
|
195
|
+
/**
|
|
196
|
+
* Render templates
|
|
197
|
+
*/
|
|
198
|
+
for await (const templatePath of templatesToCompile) {
|
|
199
|
+
const templateBuildStartTime = Date.now()
|
|
116
200
|
|
|
117
201
|
/**
|
|
118
202
|
* Add the current template path to the config
|
|
@@ -122,8 +206,6 @@ export default async (config = {}) => {
|
|
|
122
206
|
*/
|
|
123
207
|
config.build.current = {
|
|
124
208
|
path: path.parse(templatePath),
|
|
125
|
-
baseDir,
|
|
126
|
-
relativePath,
|
|
127
209
|
}
|
|
128
210
|
|
|
129
211
|
const html = await readFile(templatePath, 'utf8')
|
|
@@ -141,12 +223,15 @@ export default async (config = {}) => {
|
|
|
141
223
|
if (Boolean(plaintextConfig) || !isEmpty(plaintextConfig)) {
|
|
142
224
|
const posthtmlOptions = get(rendered.config, 'posthtml.options', {})
|
|
143
225
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
226
|
+
await writePlaintextFile(
|
|
227
|
+
await generatePlaintext(rendered.html, merge(plaintextConfig, posthtmlOptions)),
|
|
228
|
+
rendered.config
|
|
229
|
+
)
|
|
147
230
|
.catch(error => {
|
|
148
231
|
throw new Error(`Error writing plaintext file: ${error}`)
|
|
149
232
|
})
|
|
233
|
+
|
|
234
|
+
rendered.html = await handlePlaintextTags(rendered.html, posthtmlOptions)
|
|
150
235
|
}
|
|
151
236
|
|
|
152
237
|
/**
|
|
@@ -158,8 +243,9 @@ export default async (config = {}) => {
|
|
|
158
243
|
* We do this before generating plaintext, so that
|
|
159
244
|
* any paths will already have been created.
|
|
160
245
|
*/
|
|
161
|
-
const outputPathFromConfig = get(rendered.config, 'permalink',
|
|
246
|
+
const outputPathFromConfig = get(rendered.config, 'permalink', templatePath)
|
|
162
247
|
const parsedOutputPath = path.parse(outputPathFromConfig)
|
|
248
|
+
// This keeps original file extension if no output extension is set
|
|
163
249
|
const extension = get(rendered.config, 'build.output.extension', parsedOutputPath.ext.slice(1))
|
|
164
250
|
const outputPath = `${parsedOutputPath.dir}/${parsedOutputPath.name}.${extension}`
|
|
165
251
|
|
|
@@ -175,62 +261,49 @@ export default async (config = {}) => {
|
|
|
175
261
|
await writeFile(outputPath, rendered.html)
|
|
176
262
|
|
|
177
263
|
/**
|
|
178
|
-
*
|
|
264
|
+
* Remove original file if its path is different
|
|
265
|
+
* from the final destination path.
|
|
179
266
|
*/
|
|
180
|
-
if (
|
|
181
|
-
|
|
182
|
-
path.relative(get(rendered.config, 'build.output.path'), outputPath),
|
|
183
|
-
getColorizedFileSize(rendered.html),
|
|
184
|
-
formatTime(Date.now() - templateBuildStartTime)
|
|
185
|
-
])
|
|
267
|
+
if (outputPath !== templatePath) {
|
|
268
|
+
await rm(templatePath)
|
|
186
269
|
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Add file to CLI table for build summary logging
|
|
273
|
+
*/
|
|
274
|
+
table.push([
|
|
275
|
+
path.relative(get(rendered.config, 'build.output.path'), outputPath),
|
|
276
|
+
getColorizedFileSize(rendered.html),
|
|
277
|
+
formatTime(Date.now() - templateBuildStartTime)
|
|
278
|
+
])
|
|
187
279
|
}
|
|
188
280
|
|
|
189
281
|
/**
|
|
190
282
|
* Copy static files
|
|
191
283
|
*
|
|
192
|
-
*
|
|
193
|
-
*
|
|
194
|
-
*
|
|
195
|
-
* TODO: support an array of objects with source and destination, i.e. static: [{ source: 'src/assets', destination: 'assets' }, ...]
|
|
284
|
+
* TODO: support an array of objects with source and destination,
|
|
285
|
+
* i.e. static: [{ source: 'src/assets', destination: 'assets' }, ...]
|
|
196
286
|
*/
|
|
287
|
+
const staticSourcePaths = getRootDirectories([...new Set(get(config, 'build.static.source', []))])
|
|
197
288
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
const relativePath = path.relative(get(config, 'build.current.baseDir'), binaryPath)
|
|
201
|
-
const outputPath = path.join(get(config, 'build.output.path'), get(config, 'build.static.destination'), relativePath)
|
|
202
|
-
|
|
203
|
-
await mkdir(path.dirname(outputPath), { recursive: true })
|
|
204
|
-
await copyFile(binaryPath, outputPath)
|
|
289
|
+
for await (const rootDir of staticSourcePaths) {
|
|
290
|
+
await cp(rootDir, path.join(buildOutputPath, get(config, 'build.static.destination')), { recursive: true })
|
|
205
291
|
}
|
|
206
292
|
|
|
207
|
-
|
|
208
|
-
const staticSourcePaths = await fg.glob([...new Set(get(config, 'build.static.source', []))])
|
|
209
|
-
.then(paths => paths.filter(file => isBinary(file)))
|
|
210
|
-
|
|
211
|
-
if (!isEmpty(staticSourcePaths)) {
|
|
212
|
-
for await (const staticPath of staticSourcePaths) {
|
|
213
|
-
const relativePath = path.relative(get(config, 'build.current.baseDir'), staticPath)
|
|
214
|
-
const outputPath = path.join(get(config, 'build.output.path'), get(config, 'build.static.destination'), relativePath)
|
|
215
|
-
|
|
216
|
-
await mkdir(path.dirname(outputPath), { recursive: true })
|
|
217
|
-
await copyFile(staticPath, outputPath)
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
const compiledFiles = await fg.glob(path.join(config.build.output.path, '**/*'))
|
|
293
|
+
const allOutputFiles = await fg.glob(path.join(buildOutputPath, '**/*'))
|
|
222
294
|
|
|
223
295
|
/**
|
|
224
296
|
* Run `afterBuild` event
|
|
225
297
|
*/
|
|
226
298
|
if (typeof config.afterBuild === 'function') {
|
|
227
|
-
await config.afterBuild({
|
|
299
|
+
await config.afterBuild({
|
|
300
|
+
config,
|
|
301
|
+
files: allOutputFiles,
|
|
302
|
+
})
|
|
228
303
|
}
|
|
229
304
|
|
|
230
305
|
/**
|
|
231
306
|
* Log a build summary if enabled in the config
|
|
232
|
-
*
|
|
233
|
-
* Need to first clear the spinner
|
|
234
307
|
*/
|
|
235
308
|
|
|
236
309
|
spinner.clear()
|
|
@@ -239,10 +312,10 @@ export default async (config = {}) => {
|
|
|
239
312
|
console.log(table.toString() + '\n')
|
|
240
313
|
}
|
|
241
314
|
|
|
242
|
-
spinner.succeed(`
|
|
315
|
+
spinner.succeed(`Built ${table.length} template${table.length > 1 ? 's' : ''} in ${formatTime(Date.now() - startTime)}`)
|
|
243
316
|
|
|
244
317
|
return {
|
|
245
|
-
files:
|
|
318
|
+
files: allOutputFiles,
|
|
246
319
|
config
|
|
247
320
|
}
|
|
248
321
|
} catch (error) {
|
|
@@ -3,8 +3,8 @@ import posthtml from 'posthtml'
|
|
|
3
3
|
import get from 'lodash-es/get.js'
|
|
4
4
|
import { defu as merge } from 'defu'
|
|
5
5
|
import { stripHtml } from 'string-strip-html'
|
|
6
|
-
import defaultConfig from '../posthtml/defaultConfig.js'
|
|
7
6
|
import { writeFile, lstat, mkdir } from 'node:fs/promises'
|
|
7
|
+
import { getPosthtmlOptions } from '../posthtml/defaultConfig.js'
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Removes HTML tags from a given HTML string based on
|
|
@@ -46,7 +46,7 @@ const removeTags = ({ tag = 'not-plaintext', html = '', config = {} }) => {
|
|
|
46
46
|
return tree.walk(process)
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
const posthtmlOptions = merge(
|
|
49
|
+
const posthtmlOptions = merge(config, getPosthtmlOptions())
|
|
50
50
|
|
|
51
51
|
return posthtml([posthtmlPlugin()]).process(html, { ...posthtmlOptions }).then(res => res.html)
|
|
52
52
|
}
|
|
@@ -95,7 +95,7 @@ export async function handlePlaintextTags(html = '', config = {}) {
|
|
|
95
95
|
return tree.walk(process)
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
-
const posthtmlOptions = merge(
|
|
98
|
+
const posthtmlOptions = merge(config, getPosthtmlOptions())
|
|
99
99
|
|
|
100
100
|
return posthtml([posthtmlPlugin()]).process(html, { ...posthtmlOptions }).then(res => res.html)
|
|
101
101
|
}
|
|
@@ -134,7 +134,7 @@ export async function generatePlaintext(html = '', config = {}) {
|
|
|
134
134
|
).result
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
-
export async function writePlaintextFile(plaintext = '',
|
|
137
|
+
export async function writePlaintextFile(plaintext = '', config = {}) {
|
|
138
138
|
if (!plaintext) {
|
|
139
139
|
throw new Error('Missing plaintext content.')
|
|
140
140
|
}
|
|
@@ -149,25 +149,16 @@ export async function writePlaintextFile(plaintext = '', templateConfig = {}) {
|
|
|
149
149
|
* Fall back to template's build output path and extension, for example:
|
|
150
150
|
* `config.build.output.path`
|
|
151
151
|
*/
|
|
152
|
-
const plaintextConfig = get(
|
|
153
|
-
let plaintextOutputPath = get(plaintextConfig, 'output.path', get(
|
|
152
|
+
const plaintextConfig = get(config, 'plaintext')
|
|
153
|
+
let plaintextOutputPath = get(plaintextConfig, 'output.path', get(config, 'build.output.path'))
|
|
154
154
|
const plaintextExtension = get(plaintextConfig, 'output.extension', 'txt')
|
|
155
155
|
|
|
156
156
|
/**
|
|
157
|
-
* If `plaintext: true` (either from Front Matter or from config)
|
|
157
|
+
* If `plaintext: true` (either from Front Matter or from config),
|
|
158
|
+
* output plaintext file in the same location as the HTML file.
|
|
158
159
|
*/
|
|
159
160
|
if (plaintextConfig === true) {
|
|
160
|
-
|
|
161
|
-
if (typeof templateConfig.permalink === 'string') {
|
|
162
|
-
// Output plaintext at the `permalink` path
|
|
163
|
-
plaintextOutputPath = templateConfig.permalink
|
|
164
|
-
} else {
|
|
165
|
-
// Output plaintext at the same directory as the HTML file
|
|
166
|
-
plaintextOutputPath = path.join(
|
|
167
|
-
get(templateConfig, 'build.output.path'),
|
|
168
|
-
get(templateConfig, 'build.current.relativePath')
|
|
169
|
-
)
|
|
170
|
-
}
|
|
161
|
+
plaintextOutputPath = get(config, 'build.output.path')
|
|
171
162
|
}
|
|
172
163
|
|
|
173
164
|
/**
|
|
@@ -182,11 +173,21 @@ export async function writePlaintextFile(plaintext = '', templateConfig = {}) {
|
|
|
182
173
|
// No need to handle if it's an object, since we already set it to that initially
|
|
183
174
|
|
|
184
175
|
/**
|
|
185
|
-
* If `
|
|
176
|
+
* If the template has a `permalink` key set in the FM, always output plaintext file there
|
|
177
|
+
*/
|
|
178
|
+
if (typeof config.permalink === 'string') {
|
|
179
|
+
plaintextOutputPath = config.permalink
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* If `plaintextOutputPath` is a file path, output file there.
|
|
184
|
+
*
|
|
185
|
+
* The file will be output relative to the project root, and the extension
|
|
186
|
+
* doesn't matter, it will be replaced with `plaintextExtension`.
|
|
186
187
|
*/
|
|
187
188
|
if (path.extname(plaintextOutputPath)) {
|
|
188
189
|
// Ensure the target directory exists
|
|
189
|
-
await lstat(
|
|
190
|
+
await lstat(plaintextOutputPath).catch(async () => {
|
|
190
191
|
await mkdir(path.dirname(plaintextOutputPath), { recursive: true })
|
|
191
192
|
})
|
|
192
193
|
|
|
@@ -200,13 +201,15 @@ export async function writePlaintextFile(plaintext = '', templateConfig = {}) {
|
|
|
200
201
|
}
|
|
201
202
|
|
|
202
203
|
/**
|
|
203
|
-
* If `plaintextOutputPath` is a directory path, output file there
|
|
204
|
+
* If `plaintextOutputPath` is a directory path, output file there using the Template's name.
|
|
205
|
+
*
|
|
206
|
+
* The file will be output relative to the `build.output.path` directory.
|
|
204
207
|
*/
|
|
205
|
-
const templateFileName = get(
|
|
208
|
+
const templateFileName = get(config, 'build.current.path.name')
|
|
206
209
|
|
|
207
210
|
plaintextOutputPath = path.join(
|
|
211
|
+
get(config, 'build.current.path.dir'),
|
|
208
212
|
plaintextOutputPath,
|
|
209
|
-
get(templateConfig, 'build.current.path.dir'),
|
|
210
213
|
templateFileName + '.' + plaintextExtension
|
|
211
214
|
)
|
|
212
215
|
|
package/src/generators/render.js
CHANGED
|
@@ -4,6 +4,7 @@ import { cwd } from 'node:process'
|
|
|
4
4
|
import { defu as merge } from 'defu'
|
|
5
5
|
import expressions from 'posthtml-expressions'
|
|
6
6
|
import { parseFrontMatter } from '../utils/node.js'
|
|
7
|
+
import { getPosthtmlOptions } from '../posthtml/defaultConfig.js'
|
|
7
8
|
import { process as compilePostHTML } from '../posthtml/index.js'
|
|
8
9
|
import { run as useTransformers } from '../transformers/index.js'
|
|
9
10
|
|
|
@@ -40,7 +41,7 @@ export async function render(html = '', config = {}) {
|
|
|
40
41
|
})
|
|
41
42
|
]
|
|
42
43
|
)
|
|
43
|
-
.process(matter)
|
|
44
|
+
.process(matter, getPosthtmlOptions())
|
|
44
45
|
.then(({ html }) => parseFrontMatter(`---${html}\n---`))
|
|
45
46
|
|
|
46
47
|
const templateConfig = merge(matterData, config)
|
|
@@ -57,19 +58,21 @@ export async function render(html = '', config = {}) {
|
|
|
57
58
|
*
|
|
58
59
|
* @param {Object} options
|
|
59
60
|
* @param {string} options.html - The HTML to be transformed
|
|
61
|
+
* @param {Object} options.matter - The front matter data
|
|
60
62
|
* @param {Object} options.config - The current template config
|
|
61
|
-
* @param {function} options.render - The render function
|
|
62
63
|
* @returns {string} - The transformed HTML, or the original one if nothing was returned
|
|
63
64
|
*/
|
|
64
65
|
if (typeof templateConfig.beforeRender === 'function') {
|
|
65
66
|
content = await templateConfig.beforeRender(({
|
|
66
67
|
html: content,
|
|
68
|
+
matter: matterData,
|
|
67
69
|
config: templateConfig,
|
|
68
|
-
render
|
|
69
70
|
})) ?? content
|
|
70
71
|
}
|
|
71
72
|
|
|
72
|
-
|
|
73
|
+
/**
|
|
74
|
+
* Compile PostHTML
|
|
75
|
+
*/
|
|
73
76
|
const compiled = await compilePostHTML(content, templateConfig)
|
|
74
77
|
|
|
75
78
|
/**
|
|
@@ -77,19 +80,18 @@ export async function render(html = '', config = {}) {
|
|
|
77
80
|
*
|
|
78
81
|
* @param {Object} options
|
|
79
82
|
* @param {string} options.html - The HTML to be transformed
|
|
83
|
+
* @param {Object} options.matter - The front matter data
|
|
80
84
|
* @param {Object} options.config - The current template config
|
|
81
|
-
* @param {function} options.render - The render function
|
|
82
85
|
* @returns {string} - The transformed HTML, or the original one if nothing was returned
|
|
83
86
|
*/
|
|
84
87
|
if (typeof templateConfig.afterRender === 'function') {
|
|
85
88
|
compiled.html = await templateConfig.afterRender(({
|
|
86
89
|
html: compiled.html,
|
|
87
|
-
|
|
88
|
-
|
|
90
|
+
matter: matterData,
|
|
91
|
+
config: compiled.config,
|
|
89
92
|
})) ?? compiled.html
|
|
90
93
|
}
|
|
91
94
|
|
|
92
|
-
// Run Transformers
|
|
93
95
|
/**
|
|
94
96
|
* Run Transformers
|
|
95
97
|
*
|
|
@@ -100,29 +102,28 @@ export async function render(html = '', config = {}) {
|
|
|
100
102
|
* @returns {string} - The transformed HTML
|
|
101
103
|
*/
|
|
102
104
|
if (templateConfig.useTransformers !== false) {
|
|
103
|
-
compiled.html = await useTransformers(compiled.html,
|
|
105
|
+
compiled.html = await useTransformers(compiled.html, compiled.config).then(({ html }) => html)
|
|
104
106
|
}
|
|
105
107
|
|
|
106
|
-
// Run `afterTransformers` event
|
|
107
108
|
/**
|
|
108
109
|
* Run `afterTransformers` event
|
|
109
110
|
*
|
|
110
111
|
* @param {Object} options
|
|
111
112
|
* @param {string} options.html - The HTML to be transformed
|
|
113
|
+
* @param {Object} options.matter - The front matter data
|
|
112
114
|
* @param {Object} options.config - The current template config
|
|
113
|
-
* @param {function} options.render - The render function
|
|
114
115
|
* @returns {string} - The transformed HTML, or the original one if nothing was returned
|
|
115
116
|
*/
|
|
116
117
|
if (typeof templateConfig.afterTransformers === 'function') {
|
|
117
118
|
compiled.html = await templateConfig.afterTransformers(({
|
|
118
119
|
html: compiled.html,
|
|
119
|
-
|
|
120
|
-
|
|
120
|
+
matter: matterData,
|
|
121
|
+
config: compiled.config,
|
|
121
122
|
})) ?? compiled.html
|
|
122
123
|
}
|
|
123
124
|
|
|
124
125
|
return {
|
|
125
|
-
config:
|
|
126
|
+
config: compiled.config,
|
|
126
127
|
html: compiled.html,
|
|
127
128
|
}
|
|
128
129
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export default {
|
|
2
2
|
root: './',
|
|
3
|
-
folders: ['src/components', 'src/layouts', 'src/templates'],
|
|
4
|
-
fileExtension: 'html',
|
|
5
3
|
tag: 'component',
|
|
4
|
+
fileExtension: 'html',
|
|
5
|
+
folders: ['src/components', 'src/layouts', 'src/templates'],
|
|
6
6
|
expressions: {
|
|
7
7
|
loopTags: ['each', 'for'],
|
|
8
8
|
missingLocal: '{local}',
|
|
@@ -1,4 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { defu as merge } from 'defu'
|
|
2
|
+
|
|
3
|
+
export function getPosthtmlOptions(userConfigOptions = {}) {
|
|
4
|
+
return merge(
|
|
5
|
+
userConfigOptions,
|
|
6
|
+
{
|
|
7
|
+
recognizeNoValueAttribute: true,
|
|
8
|
+
recognizeSelfClosing: true,
|
|
9
|
+
directives: [
|
|
10
|
+
{ name: '?php', start: '<', end: '>' },
|
|
11
|
+
],
|
|
12
|
+
}
|
|
13
|
+
)
|
|
4
14
|
}
|