@maizzle/framework 4.8.7 → 5.0.0-beta.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.
Files changed (80) hide show
  1. package/bin/maizzle +3 -1
  2. package/package.json +65 -58
  3. package/src/commands/build.js +244 -19
  4. package/src/commands/serve.js +2 -197
  5. package/src/generators/plaintext.js +192 -91
  6. package/src/generators/render.js +128 -0
  7. package/src/index.js +46 -14
  8. package/src/{generators/posthtml → posthtml}/defaultComponentsConfig.js +6 -4
  9. package/src/{generators/posthtml → posthtml}/defaultConfig.js +1 -1
  10. package/src/posthtml/index.js +74 -0
  11. package/src/posthtml/plugins/expandLinkTag.js +36 -0
  12. package/src/server/client.js +181 -0
  13. package/src/server/index.js +383 -0
  14. package/src/server/routes/hmr.js +24 -0
  15. package/src/server/routes/index.js +38 -0
  16. package/src/server/views/error.html +83 -0
  17. package/src/server/views/index.html +24 -0
  18. package/src/server/websockets.js +27 -0
  19. package/src/transformers/addAttributes.js +30 -0
  20. package/src/transformers/attributeToStyle.js +30 -36
  21. package/src/transformers/baseUrl.js +52 -23
  22. package/src/transformers/comb.js +51 -0
  23. package/src/transformers/core.js +20 -0
  24. package/src/transformers/filters/defaultFilters.js +90 -70
  25. package/src/transformers/filters/index.js +14 -78
  26. package/src/transformers/index.js +268 -63
  27. package/src/transformers/inline.js +240 -0
  28. package/src/transformers/markdown.js +13 -14
  29. package/src/transformers/minify.js +21 -16
  30. package/src/transformers/posthtmlMso.js +13 -8
  31. package/src/transformers/prettify.js +16 -15
  32. package/src/transformers/preventWidows.js +32 -26
  33. package/src/transformers/removeAttributes.js +17 -17
  34. package/src/transformers/replaceStrings.js +30 -9
  35. package/src/transformers/safeClassNames.js +24 -24
  36. package/src/transformers/shorthandCss.js +22 -0
  37. package/src/transformers/sixHex.js +15 -15
  38. package/src/transformers/urlParameters.js +18 -16
  39. package/src/transformers/useAttributeSizes.js +65 -0
  40. package/src/utils/getConfigByFilePath.js +124 -0
  41. package/src/utils/node.js +68 -0
  42. package/src/utils/string.js +117 -0
  43. package/types/build.d.ts +117 -57
  44. package/types/components.d.ts +130 -112
  45. package/types/config.d.ts +454 -242
  46. package/types/css/inline.d.ts +234 -0
  47. package/types/css/purge.d.ts +125 -0
  48. package/types/events.d.ts +5 -105
  49. package/types/index.d.ts +148 -116
  50. package/types/markdown.d.ts +20 -18
  51. package/types/minify.d.ts +122 -120
  52. package/types/plaintext.d.ts +46 -52
  53. package/types/posthtml.d.ts +103 -136
  54. package/types/render.d.ts +0 -117
  55. package/types/urlParameters.d.ts +21 -20
  56. package/types/widowWords.d.ts +9 -7
  57. package/src/functions/plaintext.js +0 -5
  58. package/src/functions/render.js +0 -5
  59. package/src/generators/config.js +0 -52
  60. package/src/generators/output/index.js +0 -4
  61. package/src/generators/output/to-disk.js +0 -254
  62. package/src/generators/output/to-string.js +0 -73
  63. package/src/generators/postcss.js +0 -23
  64. package/src/generators/posthtml/index.js +0 -75
  65. package/src/generators/tailwindcss.js +0 -157
  66. package/src/transformers/extraAttributes.js +0 -33
  67. package/src/transformers/inlineCss.js +0 -42
  68. package/src/transformers/removeInlineBackgroundColor.js +0 -56
  69. package/src/transformers/removeInlineSizes.js +0 -43
  70. package/src/transformers/removeInlinedSelectors.js +0 -100
  71. package/src/transformers/removeUnusedCss.js +0 -48
  72. package/src/transformers/shorthandInlineCSS.js +0 -26
  73. package/src/utils/helpers.js +0 -13
  74. package/types/baseUrl.d.ts +0 -79
  75. package/types/fetch.d.ts +0 -143
  76. package/types/inlineCss.d.ts +0 -207
  77. package/types/layouts.d.ts +0 -39
  78. package/types/removeUnusedCss.d.ts +0 -115
  79. package/types/tailwind.d.ts +0 -22
  80. package/types/templates.d.ts +0 -181
@@ -1,118 +1,219 @@
1
- const path = require('path')
2
- const posthtml = require('posthtml')
3
- const {get, merge} = require('lodash')
4
- const {stripHtml} = require('string-strip-html')
5
- const defaultConfig = require('./posthtml/defaultConfig')
6
-
7
- const self = {
8
- removeCustomTags: (tag, html, config = {}) => {
9
- const posthtmlOptions = get(config, 'build.posthtml.options', {})
10
-
11
- const posthtmlPlugin = () => tree => {
12
- const process = node => {
13
- if (!node.tag) {
14
- return node
15
- }
16
-
17
- if (node.tag === tag) {
18
- return {
19
- tag: false,
20
- content: ['']
21
- }
22
- }
23
-
24
- if (Array.isArray(tag) && tag.includes(node.tag)) {
25
- return {
26
- tag: false,
27
- content: ['']
28
- }
29
- }
1
+ import path from 'pathe'
2
+ import posthtml from 'posthtml'
3
+ import get from 'lodash-es/get.js'
4
+ import { defu as merge } from 'defu'
5
+ import { stripHtml } from 'string-strip-html'
6
+ import defaultConfig from '../posthtml/defaultConfig.js'
7
+ import { writeFile, lstat, mkdir } from 'node:fs/promises'
8
+
9
+ /**
10
+ * Removes HTML tags from a given HTML string based on
11
+ * a specified tag name or an array of tag names.
12
+ *
13
+ * @param {Object} options - The options object.
14
+ * @param {string|string[]} [options.tag='not-plaintext'] - The tag name or an array of tag names to remove from the HTML.
15
+ * @param {string} [options.html=''] - The HTML string from which to remove the tags.
16
+ * @param {Object} [options.config={}] - PostHTML options.
17
+ * @returns {string} - The HTML string with the specified tags removed.
18
+ */
19
+ const removeTags = ({ tag = 'not-plaintext', html = '', config = {} }) => {
20
+ /**
21
+ * If the HTML string is empty, return it as is
22
+ */
23
+ if (!html) {
24
+ return html
25
+ }
30
26
 
27
+ const posthtmlPlugin = () => tree => {
28
+ const process = node => {
29
+ if (!node.tag) {
31
30
  return node
32
31
  }
33
32
 
34
- return tree.walk(process)
35
- }
36
-
37
- return posthtml([posthtmlPlugin()]).process(html, {...posthtmlOptions, sync: true}).html
38
- },
39
- handleCustomTags: (html, config = {}) => {
40
- const posthtmlOptions = merge(defaultConfig, get(config, 'build.posthtml.options', {}))
41
-
42
- const posthtmlPlugin = () => tree => {
43
- const process = node => {
44
- if (node.tag === 'plaintext') {
45
- return {
46
- tag: false,
47
- content: ['']
48
- }
49
- }
50
-
51
- if (node.tag === 'not-plaintext') {
52
- return {
53
- tag: false,
54
- content: tree.render(node.content)
55
- }
33
+ /**
34
+ * If the tag is a string and it matches the node tag, remove it
35
+ */
36
+ if (node.tag === tag) {
37
+ return {
38
+ tag: false,
39
+ content: ['']
56
40
  }
57
-
58
- return node
59
41
  }
60
42
 
61
- return tree.walk(process)
43
+ return node
62
44
  }
63
45
 
64
- return posthtml([posthtmlPlugin()]).process(html, {...posthtmlOptions, sync: true}).html
65
- },
66
- generate: async (html, destination, config = {}) => {
67
- const configDestinationPath = get(config, 'destination.path')
68
- const extension = get(config, 'destination.extension', 'txt')
46
+ return tree.walk(process)
47
+ }
69
48
 
70
- const strippedHTML = self.removeCustomTags('not-plaintext', html, config)
49
+ const posthtmlOptions = merge(defaultConfig, config)
71
50
 
72
- const plaintext = stripHtml(strippedHTML, {
73
- dumpLinkHrefsNearby: {
74
- enabled: true
75
- },
76
- stripTogetherWithTheirContents: ['script', 'style', 'xml'],
77
- ...config
78
- }).result
51
+ return posthtml([posthtmlPlugin()]).process(html, { ...posthtmlOptions }).then(res => res.html)
52
+ }
79
53
 
80
- html = self.handleCustomTags(html, config)
54
+ /**
55
+ * Handles custom <plaintext> tags and replaces their content based on the tag name.
56
+ *
57
+ * @param {Object} options - The options object.
58
+ * @param {string} [options.html=''] - The HTML string containing custom tags to be processed.
59
+ * @param {Object} [options.config={}] - PostHTML options.
60
+ * @returns {string} - The modified HTML string after processing custom tags.
61
+ */
62
+ export async function handlePlaintextTags(html = '', config = {}) {
63
+ /**
64
+ * If the HTML string is empty, return early
65
+ */
66
+ if (!html) {
67
+ return html
68
+ }
81
69
 
82
- // If we set plaintext.destination.path in config/fm
83
- if (configDestinationPath) {
70
+ const posthtmlPlugin = () => tree => {
71
+ const process = node => {
84
72
  /**
85
- * Using a file path will generate a single plaintext file,
86
- * no matter how many templates there are.
87
- *
88
- * It will be based on the last-processed template.
73
+ * Remove <plaintext> tags and their content from the HTML
89
74
  */
90
- if (path.extname(configDestinationPath)) {
91
- destination = configDestinationPath
92
-
93
- return {html, plaintext, destination}
75
+ if (node.tag === 'plaintext') {
76
+ return {
77
+ tag: false,
78
+ content: ['']
79
+ }
94
80
  }
95
81
 
96
82
  /**
97
- * Using a directory-like path for plaintext.destination.path
83
+ * Replace <not-plaintext> tags with their content
98
84
  */
99
- destination = path.join(configDestinationPath, path.basename(config.filepath, path.extname(config.filepath)) + '.' + extension)
85
+ if (node.tag === 'not-plaintext') {
86
+ return {
87
+ tag: false,
88
+ content: tree.render(node.content)
89
+ }
90
+ }
100
91
 
101
- return {html, plaintext, destination}
92
+ return node
102
93
  }
103
94
 
104
- /**
105
- * Use template's `permalink` Front Matter key,
106
- * fall back to the original `destination`.
107
- */
108
- destination = get(config, 'permalink', destination)
95
+ return tree.walk(process)
96
+ }
97
+
98
+ const posthtmlOptions = merge(defaultConfig, config)
99
+
100
+ return posthtml([posthtmlPlugin()]).process(html, { ...posthtmlOptions }).then(res => res.html)
101
+ }
102
+
103
+ /**
104
+ * Generate a plaintext representation from the provided HTML.
105
+ *
106
+ * @param {Object} options - The options object.
107
+ * @param {string} [options.html=''] - The HTML string to convert to plaintext.
108
+ * @param {Object} [options.config={}] - Configuration object.
109
+ * @returns {Promise<string>|void} - The generated plaintext as a string.
110
+ */
111
+ export async function generatePlaintext(html = '', config = {}) {
112
+ const { posthtml: posthtmlOptions, ...stripOptions } = config
113
+
114
+ /**
115
+ * Remove <not-plaintext> tags and their content from the HTML.
116
+ * `config` is an object containing PostHTML options.
117
+ */
118
+ html = await removeTags({ tag: 'not-plaintext', html, config: posthtmlOptions })
119
+
120
+ /**
121
+ * Return the plaintext representation from the stripped HTML.
122
+ * The `dumpLinkHrefsNearby` option is enabled by default.
123
+ */
124
+ return stripHtml(
125
+ html,
126
+ merge(
127
+ stripOptions,
128
+ {
129
+ dumpLinkHrefsNearby: {
130
+ enabled: true,
131
+ },
132
+ },
133
+ )
134
+ ).result
135
+ }
136
+
137
+ export async function writePlaintextFile(plaintext = '', templateConfig = {}) {
138
+ if (!plaintext) {
139
+ throw new Error('Missing plaintext content.')
140
+ }
141
+
142
+ if (typeof plaintext !== 'string') {
143
+ throw new Error('Plaintext content must be a string.')
144
+ }
109
145
 
110
- if (typeof destination === 'string') {
111
- destination = path.join(path.dirname(destination), path.basename(destination, path.extname(destination)) + '.' + extension)
146
+ /**
147
+ * Get plaintext output path config, i.e `config.plaintext.destination.path`
148
+ *
149
+ * Fall back to template's build output path and extension, for example:
150
+ * `config.build.output.path`
151
+ */
152
+ const plaintextConfig = get(templateConfig, 'plaintext')
153
+ let plaintextOutputPath = get(plaintextConfig, 'output.path', get(templateConfig, 'build.output.path'))
154
+ const plaintextExtension = get(plaintextConfig, 'output.extension', 'txt')
155
+
156
+ /**
157
+ * If `plaintext: true` (either from Front Matter or from config)
158
+ */
159
+ if (plaintextConfig === true) {
160
+ // If the template has a `permalink` key set in the FM
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
+ )
112
170
  }
171
+ }
113
172
 
114
- return {html, plaintext, destination}
173
+ /**
174
+ * If `plaintext: path/to/file.ext` in the FM
175
+ * Can't work if set in config.js as file path, because it would be the same for all templates
176
+ * We check later if it's a dir path, won't work if it's a file path
177
+ */
178
+ if (typeof plaintextConfig === 'string') {
179
+ plaintextOutputPath = plaintextConfig
115
180
  }
116
- }
117
181
 
118
- module.exports = self
182
+ // No need to handle if it's an object, since we already set it to that initially
183
+
184
+ /**
185
+ * If `plaintextOutputPath` is a file path, output file there
186
+ */
187
+ if (path.extname(plaintextOutputPath)) {
188
+ // Ensure the target directory exists
189
+ await lstat(path.dirname(plaintextOutputPath)).catch(async () => {
190
+ await mkdir(path.dirname(plaintextOutputPath), { recursive: true })
191
+ })
192
+
193
+ // Ensure correct extension is used
194
+ plaintextOutputPath = path.join(
195
+ path.dirname(plaintextOutputPath),
196
+ path.basename(plaintextOutputPath, path.extname(plaintextOutputPath)) + '.' + plaintextExtension
197
+ )
198
+
199
+ return writeFile(plaintextOutputPath, plaintext)
200
+ }
201
+
202
+ /**
203
+ * If `plaintextOutputPath` is a directory path, output file there, using the template's name
204
+ */
205
+ const templateFileName = get(templateConfig, 'build.current.path.name')
206
+
207
+ plaintextOutputPath = path.join(
208
+ plaintextOutputPath,
209
+ get(templateConfig, 'build.current.path.dir'),
210
+ templateFileName + '.' + plaintextExtension
211
+ )
212
+
213
+ // Ensure the target directory exists
214
+ await lstat(path.dirname(plaintextOutputPath)).catch(async () => {
215
+ await mkdir(path.dirname(plaintextOutputPath), { recursive: true })
216
+ })
217
+
218
+ return writeFile(plaintextOutputPath, plaintext)
219
+ }
@@ -0,0 +1,128 @@
1
+ import { parse } from 'pathe'
2
+ import posthtml from 'posthtml'
3
+ import { cwd } from 'node:process'
4
+ import { defu as merge } from 'defu'
5
+ import expressions from 'posthtml-expressions'
6
+ import { parseFrontMatter } from '../utils/node.js'
7
+ import { process as compilePostHTML } from '../posthtml/index.js'
8
+ import { run as useTransformers } from '../transformers/index.js'
9
+
10
+ export async function render(html = '', config = {}) {
11
+ if (typeof html !== 'string') {
12
+ throw new TypeError(`first argument must be a string, received ${html}`)
13
+ }
14
+
15
+ if (html.length === 0) {
16
+ throw new RangeError('received empty string')
17
+ }
18
+
19
+ /**
20
+ * Parse front matter
21
+ *
22
+ * Parse expressions in front matter and add to config
23
+ * This could be handled by components() but plugins aren't working with it currently
24
+ */
25
+ let { content, matter } = parseFrontMatter(html)
26
+
27
+ /**
28
+ * Compute template config
29
+ *
30
+ * Merge it with front matter data and set the `cwd` for Tailwind.
31
+ */
32
+ const { data: matterData } = await posthtml(
33
+ [
34
+ expressions({
35
+ strictMode: false,
36
+ missingLocal: '{local}',
37
+ locals: {
38
+ page: config
39
+ }
40
+ })
41
+ ]
42
+ )
43
+ .process(matter)
44
+ .then(({ html }) => parseFrontMatter(`---${html}\n---`))
45
+
46
+ const templateConfig = merge(matterData, config)
47
+
48
+ /**
49
+ * Used for PostCSS `from` to make `@config` work in Tailwind
50
+ *
51
+ * @todo use only when in Node environment
52
+ */
53
+ templateConfig.cwd = parse(cwd()).base
54
+
55
+ /**
56
+ * Run `beforeRender` event
57
+ *
58
+ * @param {Object} options
59
+ * @param {string} options.html - The HTML to be transformed
60
+ * @param {Object} options.config - The current template config
61
+ * @param {function} options.render - The render function
62
+ * @returns {string} - The transformed HTML, or the original one if nothing was returned
63
+ */
64
+ if (typeof templateConfig.beforeRender === 'function') {
65
+ content = await templateConfig.beforeRender(({
66
+ html: content,
67
+ config: templateConfig,
68
+ render
69
+ })) ?? content
70
+ }
71
+
72
+ // Compile PostHTML
73
+ const compiled = await compilePostHTML(content, templateConfig)
74
+
75
+ /**
76
+ * Run `afterRender` event
77
+ *
78
+ * @param {Object} options
79
+ * @param {string} options.html - The HTML to be transformed
80
+ * @param {Object} options.config - The current template config
81
+ * @param {function} options.render - The render function
82
+ * @returns {string} - The transformed HTML, or the original one if nothing was returned
83
+ */
84
+ if (typeof templateConfig.afterRender === 'function') {
85
+ compiled.html = await templateConfig.afterRender(({
86
+ html: compiled.html,
87
+ config: templateConfig,
88
+ render
89
+ })) ?? compiled.html
90
+ }
91
+
92
+ // Run Transformers
93
+ /**
94
+ * Run Transformers
95
+ *
96
+ * Runs only if `useTransformers` is not explicitly disabled in the config.
97
+ *
98
+ * @param {string} html - The HTML to be transformed
99
+ * @param {Object} config - The current template config
100
+ * @returns {string} - The transformed HTML
101
+ */
102
+ if (templateConfig.useTransformers !== false) {
103
+ compiled.html = await useTransformers(compiled.html, templateConfig).then(({ html }) => html)
104
+ }
105
+
106
+ // Run `afterTransformers` event
107
+ /**
108
+ * Run `afterTransformers` event
109
+ *
110
+ * @param {Object} options
111
+ * @param {string} options.html - The HTML to be transformed
112
+ * @param {Object} options.config - The current template config
113
+ * @param {function} options.render - The render function
114
+ * @returns {string} - The transformed HTML, or the original one if nothing was returned
115
+ */
116
+ if (typeof templateConfig.afterTransformers === 'function') {
117
+ compiled.html = await templateConfig.afterTransformers(({
118
+ html: compiled.html,
119
+ config: templateConfig,
120
+ render
121
+ })) ?? compiled.html
122
+ }
123
+
124
+ return {
125
+ config: templateConfig,
126
+ html: compiled.html,
127
+ }
128
+ }
package/src/index.js CHANGED
@@ -1,17 +1,49 @@
1
- const serve = require('./commands/serve')
2
- const toFile = require('./commands/build')
3
- const transformers = require('./transformers')
4
- const toString = require('./functions/render')
5
- const PostCSS = require('./generators/postcss')
6
- const toPlaintext = require('./functions/plaintext')
7
- const TailwindCSS = require('./generators/tailwindcss')
1
+ import serve from './server/index.js'
2
+ import build from './commands/build.js'
3
+ import { render } from './generators/render.js'
8
4
 
9
- module.exports = {
5
+ import { addAttributes } from './transformers/addAttributes.js'
6
+ import { attributeToStyle } from './transformers/attributeToStyle.js'
7
+ import { addBaseUrl } from './transformers/baseUrl.js'
8
+ import { comb } from './transformers/comb.js'
9
+ import { filters } from './transformers/filters/index.js'
10
+ import { inline } from './transformers/inline.js'
11
+ import { markdown } from './transformers/markdown.js'
12
+ import { minify } from './transformers/minify.js'
13
+ import { useMso } from './transformers/posthtmlMso.js'
14
+ import { prettify } from './transformers/prettify.js'
15
+ import { removeAttributes } from './transformers/removeAttributes.js'
16
+ import { replaceStrings } from './transformers/replaceStrings.js'
17
+ import { safeClassNames } from './transformers/safeClassNames.js'
18
+ import { shorthandCSS } from './transformers/shorthandCss.js'
19
+ import { sixHEX } from './transformers/sixHex.js'
20
+ import { addURLParams } from './transformers/urlParameters.js'
21
+ import { useAttributeSizes } from './transformers/useAttributeSizes.js'
22
+ import { preventWidows } from './transformers/preventWidows.js'
23
+ import { generatePlaintext } from './generators/plaintext.js'
24
+
25
+ export {
26
+ build,
10
27
  serve,
11
- build: toFile,
12
- ...transformers,
13
- render: toString,
14
- postcss: PostCSS,
15
- plaintext: toPlaintext,
16
- tailwindcss: TailwindCSS
28
+ render,
29
+ addAttributes,
30
+ attributeToStyle,
31
+ addBaseUrl,
32
+ comb as removeUnusedCSS,
33
+ comb as purgeCSS,
34
+ filters,
35
+ inline as inlineCSS,
36
+ markdown,
37
+ minify,
38
+ useMso,
39
+ prettify,
40
+ removeAttributes,
41
+ replaceStrings,
42
+ safeClassNames,
43
+ shorthandCSS,
44
+ sixHEX,
45
+ addURLParams,
46
+ useAttributeSizes,
47
+ preventWidows,
48
+ generatePlaintext,
17
49
  }
@@ -1,9 +1,11 @@
1
- module.exports = {
1
+ export default {
2
2
  root: './',
3
3
  folders: ['src/components', 'src/layouts', 'src/templates'],
4
4
  fileExtension: 'html',
5
5
  tag: 'component',
6
- attribute: 'src',
7
- yield: 'content',
8
- propsAttribute: 'locals'
6
+ expressions: {
7
+ loopTags: ['each', 'for'],
8
+ missingLocal: '{local}',
9
+ strictMode: false,
10
+ },
9
11
  }
@@ -1,4 +1,4 @@
1
- module.exports = {
1
+ export default {
2
2
  recognizeNoValueAttribute: true,
3
3
  recognizeSelfClosing: true
4
4
  }
@@ -0,0 +1,74 @@
1
+ import get from 'lodash-es/get.js'
2
+ import { defu as merge } from 'defu'
3
+
4
+ // PostHTML
5
+ import posthtml from 'posthtml'
6
+ import components from 'posthtml-component'
7
+ import posthtmlPostcss from 'posthtml-postcss'
8
+ import defaultPosthtmlConfig from './defaultConfig.js'
9
+ import expandLinkTag from './plugins/expandLinkTag.js'
10
+
11
+ // PostCSS
12
+ import tailwindcss from 'tailwindcss'
13
+ import postcssImport from 'postcss-import'
14
+ import postcssSafeParser from 'postcss-safe-parser'
15
+ import customProperties from 'postcss-custom-properties'
16
+
17
+ import defaultComponentsConfig from './defaultComponentsConfig.js'
18
+
19
+ export async function process(html = '', config = {}) {
20
+ const postcssPlugin = posthtmlPostcss(
21
+ [
22
+ postcssImport(),
23
+ tailwindcss(get(config, 'css.tailwind', {})),
24
+ get(config, 'css.inline.resolveCSSVariables', true) && customProperties(),
25
+ ...get(config, 'postcss.plugins', []),
26
+ ],
27
+ merge(
28
+ get(config, 'postcss.options', {}),
29
+ {
30
+ from: config.cwd || './',
31
+ parser: postcssSafeParser
32
+ }
33
+ )
34
+ )
35
+
36
+ const posthtmlOptions = merge(get(config, 'posthtml.options', {}), defaultPosthtmlConfig)
37
+
38
+ const componentsUserOptions = get(config, 'components', {})
39
+
40
+ const expressionsOptions = merge(
41
+ get(config, 'build.expressions', get(config, 'posthtml.expressions', {})),
42
+ get(componentsUserOptions, 'expressions', {}),
43
+ )
44
+
45
+ const locals = merge(
46
+ get(config, 'locals', {}),
47
+ get(expressionsOptions, 'locals', {}),
48
+ { page: config },
49
+ )
50
+
51
+ return posthtml([
52
+ ...get(config, 'posthtml.plugins.before', []),
53
+ expandLinkTag,
54
+ postcssPlugin,
55
+ components(
56
+ merge({
57
+ expressions: {
58
+ locals,
59
+ }
60
+ }, defaultComponentsConfig)
61
+ ),
62
+ expandLinkTag,
63
+ postcssPlugin,
64
+ ...get(config, 'posthtml.plugins.after', get(config, 'posthtml.plugins', []))
65
+ ])
66
+ .process(html, posthtmlOptions)
67
+ .then(result => ({
68
+ config,
69
+ html: result.html,
70
+ }))
71
+ .catch(error => {
72
+ throw error
73
+ })
74
+ }
@@ -0,0 +1,36 @@
1
+ import fs from 'node:fs'
2
+
3
+ const targets = new Set(['expand', 'inline'])
4
+
5
+ // TODO: refactor to a Promise so we can use async readFile
6
+ const plugin = (() => tree => {
7
+ const process = node => {
8
+ /**
9
+ * Don't expand link tags that are not explicitly marked as such
10
+ */
11
+ if (node.attrs && ![...targets].some(attr => attr in node.attrs)) {
12
+ for (const attr of targets) {
13
+ node.attrs[attr] = false
14
+ }
15
+
16
+ return node
17
+ }
18
+
19
+ if (
20
+ node.tag === 'link'
21
+ && node.attrs
22
+ && node.attrs.href
23
+ && node.attrs.rel === 'stylesheet'
24
+ ) {
25
+ node.content = [fs.readFileSync(node.attrs.href, 'utf8')]
26
+ node.tag = 'style'
27
+ node.attrs = {}
28
+ }
29
+
30
+ return node
31
+ }
32
+
33
+ return tree.walk(process)
34
+ })()
35
+
36
+ export default plugin